İnternet. Bilgisayar. Yardım. Tavsiye. Tamirat

I2C veriyolu ve STM32 MCU'larda kullanımı. STM32, seri arayüz I2C Stm32 i2c sinyalleri ack ve nack

kselltrum 20 Şubat 2017, 01:17

ARM mimarisi için STM32 ve mikroC derleyicisi ile ilk adımlar - Bölüm 4 - I2C, pcf8574 ve HD4478 tabanlı LCD bağlantısı

  • Mikrodenetleyici programlama

Bir sonraki makaleyi, genellikle bir mikro denetleyiciye bağlı çeşitli mikro devrelerde kullanılan ortak i2c arayüzüyle çalışmaya adamak istiyorum.

I2C, iki fiziksel bağlantı (ortak kabloya ek olarak) üzerinden çalışan bir veri yoludur. İnternette bu konuda pek çok şey yazılıyor; Wikipedia'da güzel makaleler var. Ayrıca veri yolu çalışma algoritması çok açık bir şekilde açıklanmıştır. Kısaca veri yolu iki telli senkron bir veri yoludur. Aynı anda veri yolunda 127'ye kadar cihaz bulunabilir (cihaz adresi 7 bittir, buna daha sonra tekrar döneceğiz). Aşağıda, ana cihaz MK olacak şekilde cihazları i2c veriyoluna bağlamak için tipik bir diyagram bulunmaktadır.


i2c için tüm cihazlar (hem ana hem de yardımcı cihazlar) açık drenaj çıkışlarını kullanır. Basitçe söylemek gerekirse, lastiği YALNIZCA ZEMİNE çekebilirler. Yüksek veri yolu seviyesi, çekme dirençleri ile sağlanır. Bu dirençlerin değeri genellikle 4,7 ila 10 kOhm aralığında seçilir. i2c, cihazları bağlayan fiziksel hatlara oldukça duyarlıdır; bu nedenle, büyük kapasitanslı bir bağlantı kullanılırsa (örneğin, uzun ince veya korumalı bir kablo), bu kapasitansın etkisi, sinyallerin kenarlarını "bulanıklaştırabilir" ve sinyallere müdahale edebilir. otobüsün normal çalışması. Çekme direnci ne kadar küçük olursa, bu kapasitansın sinyal kenarlarının özellikleri üzerindeki etkisi o kadar az olur, ancak i2c arayüzlerindeki çıkış transistörlerindeki YÜK DAHA BÜYÜK olur. Bu dirençlerin değeri her özel uygulama için seçilir, ancak bunlar 2,2 kOhm'dan az olmamalıdır, aksi takdirde veri yolu ile çalışan cihazlardaki çıkış transistörlerini kolayca yakabilirsiniz.

Veri yolu iki hattan oluşur: SDA (veri hattı) ve SCL (saat sinyali). Bus Master cihazını saatler, genellikle bizim MK'miz. SCL yüksek olduğunda bilgi veri yolundan okunur. SDA durumu yalnızca saat sinyali düşük olduğunda değiştirilebilir.. SCL yüksek olduğunda, sinyal üretirken SDA'daki sinyal değişir BAŞLANGIÇ (SCL yüksek olduğunda SDA'daki sinyal yüksekten düşüğe değişir) ve DURMAK - SCL seviyesi yüksek olduğunda SDA'daki sinyal düşükten yükseğe değişir).

Ayrı olarak, i2c'de adresin 7 bitlik bir sayı olarak belirtildiği söylenmelidir. 8 - en az anlamlı bit, veri aktarımının yönünü gösterir 0 - kölenin veri ileteceği, 1 - alacağı anlamına gelir.. Kısaca i2c ile çalışma algoritması şu şekildedir:

  • SDA ve SCL'de yüksek seviye- otobüs ücretsizdir, çalışmaya başlayabilirsiniz
  • Ana asansörler SCL 1'e ve durumu değiştirir S.D.A. 1'den 0'a - onu yere çeker - bir sinyal oluşur BAŞLANGIÇ
  • Master, bir yön biti ile 7 bitlik bir köle adresi iletir (veri S.D.A. ne zaman sergilenir SCL yere çekilir ve serbest bırakıldığında köle tarafından okunur). Kölenin önceki biti "yakalayacak" zamanı yoksa çeker SCL yere indirerek ustaya veri yolunun durumunun değiştirilmesine gerek olmadığını açıkça belirtin: "Hala öncekini okuyorum." Usta lastiği bıraktıktan sonra kontrol eder köle onun gitmesine izin mi verdi?.
  • Master, adresin 8 bitini ilettikten sonra 9. saat döngüsünü üretir ve veri yolunu serbest bırakır. Eğer köle onun adresini duyup kabul ederse, o zaman basacak S.D.A. yere. Sinyal bu şekilde oluşuyor SORMAK- kabul edildi, her şey yolunda. Eğer köle hiçbir şey anlamıyorsa ya da orada değilse, o zaman lastiğe basacak kimse olmayacaktır. usta mola bekleyecek ve anlaşılmadığını anlayacaktır.
  • Adresi ilettikten sonra yönü belirlediysek efendiden köleye(Adresin 8 biti 1'e eşittir), daha sonra master, varlığını kontrol etmeyi unutmadan verileri köleye iletir. SORMAK köleden, köle aygıtının gelen bilgiyi işlemesini bekliyor.
  • Master, Slave'den veri aldığında, Master'ın kendisi bir sinyal üretir. SORMAK her baytı aldıktan sonra köle onun varlığını kontrol eder. Master özellikle gönderemeyebilir SORMAK komutu göndermeden önce DURMAK, genellikle köleye daha fazla veri sağlamaya gerek olmadığını açıkça belirtir.
  • Master tarafından veri gönderildikten sonra (yazma modu), köleden veri okunması gerekiyorsa, daha sonra master sinyali tekrar üretir BAŞLANGIÇ , köle adresini bir okuma bayrağıyla göndererek. (komuttan önce ise BAŞLANGIÇ transfer edilmedi DURMAK sonra bir ekip kurulur TEKRAR BAŞLAT). Bu, master-slave iletişiminin yönünü değiştirmek için kullanılır. Örneğin, kayıt adresini köleye iletiriz ve ondan veri okuruz.)
  • Slave ile çalışmanın tamamlanmasının ardından master bir sinyal üretir DURMAK- saat sinyalinin yüksek seviyesinde, 0'dan 1'e bir veri yolu geçişi oluşturur.
STM 32, donanımla uygulanan i2c veri yolu alıcı-vericilerine sahiptir. Bir MK'de bu tür 2 veya 3 modül bulunabilir. Bunları yapılandırmak için, kullanılan MK referansında açıklanan özel kayıtlar kullanılır.

MicroC'de, i2c'yi (aynı zamanda herhangi bir çevre birimini) kullanmadan önce, uygun şekilde başlatılması gerekir. Bunu yapmak için aşağıdaki işlevi kullanıyoruz (Ana olarak başlatma):

I2Cn_Init_Advanced(unsigned long: I2C_ClockSpeed, const Module_Struct *module);

  • N- örneğin kullanılan modülün numarası I2C1 veya I2C2.
  • I2C_ClockSpeed- veri yolu hızı, 100000 (100 kbs, standart mod) veya 400000 (400 kbs, hızlı mod). İkincisi 4 kat daha hızlıdır ancak tüm cihazlar bunu desteklemez
  • *modül- örneğin bir çevresel modülün işaretçisi &_GPIO_MODULE_I2C1_PB67, şunu unutmayalım Kod Asistanı (ctrl-boşluk ) çok yardımcı olur.
Öncelikle otobüsün boş olup olmadığını kontrol edelim, bunun için bir fonksiyon var I2Cn_Is_Idle(); Otobüs boşsa 1, üzerinde bir değişim varsa 0 döndürülür.

I2Cn_Start();
Nerede N- mikrodenetleyicimizin kullanılan i2c modülünün numarası. Bus'ta bir hata varsa işlev 0'ı, her şey yolundaysa 1'i döndürür.

Verileri köleye aktarmak için şu işlevi kullanırız:

I2Cn_Write(imzasız karakter köle_adresi, imzasız karakter *buf, imzasız uzun sayım, imzasız uzun END_mode);

  • N- kullanılan modülün numarası
  • köle_adresi- 7 bitlik köle adresi.
  • *buf- verilerimize bir işaretçi - bir bayt veya bayt dizisi.
  • saymak- aktarılan veri baytlarının sayısı.
  • END_mode- verileri köleye aktardıktan sonra ne yapılması gerektiği, END_MODE_STOP - bir sinyal iletmek DURMAK, veya END_MODE_RESTART Tekrar gönderin BAŞLANGIÇ, bir sinyal üretiyor TEKRAR BAŞLAT ve kendisiyle yapılan oturumun bitmediğini ve verilerin artık ondan okunacağını departmana açıkça bildirmek.
Slave'den veri okumak için şu işlevi kullanın:

I2Cn_Read(char köle_adresi, char *ptrdata, imzasız uzun sayım, imzasız uzun END_mode);

  • N- kullanılan modülün numarası
  • köle_adresi- 7 bitlik köle adresi.
  • *buf- veri aldığımız bir değişkene veya diziye işaretçi, char veya short int yazın
  • saymak- alınan veri baytlarının sayısı.
  • END_mode- köleden veri aldıktan sonra ne yapılmalı - END_MODE_STOP - bir sinyal iletmek DURMAK, veya END_MODE_RESTART bir sinyal gönder TEKRAR BAŞLAT.
MK'mize bir şey bağlamayı deneyelim. Başlangıç ​​olarak: i2c veri yolu aracılığıyla kontrol edilen giriş/çıkış bağlantı noktalarının genişleticisi olan yaygın PCF8574(A) mikro devresi. Bu çip, fiziksel G/Ç bağlantı noktası olan yalnızca bir dahili kayıt içerir. Yani, ona bir bayt iletirseniz, bu hemen onun sonuçlarına maruz kalacaktır. Eğer ondan bir bayt sayarsanız (İletim BAŞLANGIÇ okuma bayrağı, sinyal ile adres RESTERT, verileri okuyun ve sonunda bir sinyal oluşturun DURMAK) daha sonra mantıksal durumları çıktılarına yansıtacaktır. Mikro devremizi veri sayfasına uygun olarak bağlayalım:


Mikro devre adresi pinlerin durumundan oluşur A0, A1, A2. Mikro devre için PCF8574 adres şu şekilde olacaktır: 0100A0A1A2. (Örneğin yüksek seviyede sırasıyla A0, A1, A2 var, mikro devremizin adresi 0b0100 olacaktır. 111 = 0x27). İçin PCF8574A - 0111A0A1A2 bağlantı şemamızla 0b0111 adresini verecektir. 111 = 0x3F. Diyelim ki A2 toprağa bağlıysa, o zaman adres PCF8574A irade 0x3B. Toplamda, her biri 8 PCF8574A ve PCF8574 olmak üzere bir i2c veriyoluna aynı anda 16 mikro devre asabilirsiniz.

Bir şey aktarmayı deneyelim, i2c veri yolunu başlatalım ve PCF8574'ümüze bir şeyler aktaralım.

#define PCF8574A_ADDR 0x3F //PCF8574'ümüzün adresi void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // START sinyalini oluştur I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); // 1 byte veriyi aktar ve STOP'u oluştur sinyal) char PCF8574A_reg; // PCF8574'e yazdığımız değişken void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // I2C'yi başlat gecikme_ms(25); // Biraz bekle PCF8574A_reg.b0 = 0; // ilk LED'i yak PCF8574A_reg.b1 = 1; // ikinci LED'i kapat (1) ( gecikme_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // LED'lerin durumunu tersine çevir I2C_PCF8574_WriteReg (PCF8574A_reg) ; // verileri PCF8574'ümüze aktarın ))
Programımızı derleyip çalıştırıyoruz ve ledlerimizin dönüşümlü olarak yanıp söndüğünü görüyoruz.
LED'lerin katotunu PCF8574'e bağlamamın bir nedeni var. Mesele şu ki, çıkışa mantıksal 0 verildiğinde, mikro devre çıkışını dürüstçe toprağa çekiyor, ancak mantıksal 1 uygulandığında, onu 100 μA'lık bir akım kaynağı aracılığıyla + güce bağlıyor. Yani çıktıda “dürüst” bir mantıksal 1 elde edemezsiniz. Ve bir LED'i 100 µA ile yakamazsınız. Bu, PCF8574 çıkışını ek kayıtlar olmadan girişe yapılandırmak için yapıldı. Basitçe çıkış yazmacı 1'e yazıyoruz (temel olarak pin durumlarını Vdd'ye ayarlıyoruz) ve onu toprağa kısa devre yapabiliriz. Mevcut kaynak, G/Ç genişleticimizin çıkış aşamasının "yanmasına" izin vermeyecektir. Bacak yere çekilirse toprak potansiyeli üzerindedir ve mantıksal 0 okunur. Bacak +'ya çekilirse mantıksal 1 okunur. bu mikro devrelerle çalışırken bunu daima hatırlamalısınız.


Genişletici çipimizin pinlerinin durumunu okumaya çalışalım.

#define PCF8574A_ADDR 0x3F //PCF8574'ümüzün adresi void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // START sinyalini üret I2C1_Write(PCF8574A_ADDR, &wData, 1, END_MODE_STOP); // 1 byte veriyi aktar ve STOP'u oluştur signal ) void I2C_PCF8574_ReadReg (unsigned char rData) ( I2C1_Start(); // START sinyalini oluşturun I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // 1 byte veriyi okuyun ve STOP sinyalini oluşturun) char PCF8574A_reg; //PCF8574'e yazdığımız değişken char PCF8574A_out; // okuduğumuz değişken ve PCF8574 char lad_state; //LED'imiz açık veya kapalı void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // I2C delay_ms(25)'i başlatıyoruz; // Biraz bekle PCF8574A_reg.b0 = 0; // ilk LED'i yak PCF8574A_reg.b1 = 1; / / ikinci LED'i kapatın PCF8574A_reg.b6 = 1; // 6 ve 7 numaralı pinleri PCF8574A_reg.b7'ye bağlayın = 1; (1) ( gecikme_ms(100); I2C_PCF8574_WriteReg (PCF8574A_reg); // veriyi yazın PCF8574 I2C_PCF8574_ReadReg (PCF8) 574A_out ); // PCF8574'ten oku eğer (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0; // 1 düğmeye basılırsa (PCF8574'ten okunan baytın 6. biti 0 ise) LED'imizi açın/kapatın) if (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // 2 düğme ve 2 LED için benzer))
Şimdi butonlara basarak LED’imizi açıp kapatıyoruz. Mikro devrenin başka bir çıkışı var Dahili. I/O genişleticimizin pinlerinin durumu her değiştiğinde, üzerinde bir darbe üretilir. MK'mizin harici kesme girişine bağlayarak (Harici kesintileri nasıl yapılandıracağınızı ve bunlarla nasıl çalışacağınızı aşağıdaki makalelerden birinde anlatacağım).

Bir karakter ekranını ona bağlamak için port genişleticimizi kullanalım. Bunlardan çok sayıda var, ancak neredeyse hepsi bir denetleyici çipi temelinde inşa edildi HD44780 ve onun klonları. Mesela ben LCD2004 ekran kullandım.


Bunun ve HD44780 denetleyicinin veri sayfası internette kolayca bulunabilir. Ekranımızı sırasıyla PCF8574'e ve onun ekranını da STM32'mize bağlayalım.

HD44780 paralel geçitli bir arayüz kullanır. Veriler, pindeki 8 (saat döngüsü başına) veya 4 (2 saat döngüsü başına) kapı darbesi ile iletilir. e. (ekran denetleyicisi tarafından azalan kenarda okunur, 1'den 0'a geçiş). Çözüm R.S. ekranımıza veri gönderip göndermediğimizi gösterir ( RS = 1) (görüntülemesi gereken karakterler aslında ASCII kodlarıdır) veya komut ( RS = 0). RW veri aktarımının, yazmanın veya okumanın yönünü gösterir. Genellikle ekrana veri yazarız, bu nedenle ( RW=0). Direnç R6 ekran kontrastını kontrol eder. Kontrast ayarı girişini basitçe toprağa veya güce bağlayamazsınız, aksi takdirde hiçbir şey göremezsiniz.. VT1, ekranın arka ışığını MK komutlarına göre açıp kapatmak için kullanılır. MicroC'nin paralel bir arayüz aracılığıyla bu tür ekranlarla çalışmak için bir kitaplığı var, ancak genellikle bir ekrana 8 ayak harcamak pahalıdır, bu nedenle bu tür ekranlarla çalışmak için neredeyse her zaman PCF8574'ü kullanırım. (İlgilenen olursa, paralel bir arayüz aracılığıyla MicroC'de yerleşik HD44780 tabanlı ekranlarla çalışma hakkında bir makale yazacağım.) Değişim protokolü özellikle karmaşık değil (4 veri hattı kullanacağız ve bilgileri 2 saat döngüsünde aktaracağız), aşağıdaki zamanlama diyagramında açıkça gösterilmiştir:


Verileri ekranımıza aktarmadan önce servis komutları iletilerek başlatılmalıdır. (veri sayfasında açıklanmıştır, burada yalnızca en çok kullanılanları sunuyoruz)

  • 0x28- 4 hat üzerinden göstergeyle iletişim
  • 0x0C- görüntü çıkışını etkinleştirin, imleç gösterimini devre dışı bırakın
  • 0x0E- görüntü çıkışını etkinleştirin, imleç gösterimini etkinleştirin
  • 0x01- göstergeyi temizle
  • 0x08- görüntü çıkışını devre dışı bırak
  • 0x06- sembol görüntülendikten sonra imleç tanıdık 1 yer hareket eder
Bu göstergeyle çok sık çalışmamız gerekeceğinden bir eklenti kütüphanesi oluşturacağız. "i2c_lcd.h" . Bunu yapmak için Proje Müdürü Başlık Dosyaları ve Seç Yeni Dosya Ekle . Başlık dosyamızı oluşturalım.

#define PCF8574A_ADDR 0x3F //PCF8574'ümüzün adresi #define DB4 b4 // PCF8574 pinleri ile gösterge arasındaki yazışma #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 #define RW b2 #define RS b1 #define BL b0 // arka ışık kontrolü #define displenth 20 // görüntü satırımızdaki karakter sayısı static unsigned char BL_status; // arka ışığın durumunu saklayan değişken (açık/kapalı) void lcd_I2C_Init(void); // Ekran ve PCF8574 başlatma işlevi void lcd_I2C_txt(char *pnt); // Bir metin satırını görüntüler, parametre bu satırı gösteren bir işaretçidir void lcd_I2C_int(int pnt); // Bir tamsayı değişkeninin değerini görüntüler, parametre çıkış değeridir void lcd_I2C_Goto(unsigned short row, unsigned short col); // imleci belirtilen konuma hareket ettirir, parametreler satır - satır (ekrana bağlı olarak 1'den 2'ye veya 4'e kadar) ve sütun - (1'den eksilme'ye kadar)) void lcd_I2C_cls(); // Ekranı temizler void lcd_I2C_backlight (imzasız kısa int durumu); // Etkinleştirir (1 iletirken ve devre dışı bırakır - 0 iletirken ekran arka ışığı)
Şimdi fonksiyonlarımızı anlatalım, tekrar şuraya geçiyoruz: Proje Müdürü klasöre sağ tıklayın Kaynaklar ve Seç Yeni Dosya Ekle . Dosya oluştur "i2c_lcd.c" .

#include "i2c_lcd.h" //başlık dosyamızı ekleyin char lcd_reg; // PCF8574 VOID'e gönderilen verilerin geçici depolama kaydı I2C_PCF8574_WRITEREG (UNSIGNED ChaR WDATA) // PCF8574 yongasındaki I2C verilerinin işlevi (I2C1_START (); i2C1_WRITE (PCF8574A_ADDR, & WDATA, 1 , End_mode_stop)); /gönderme işlevi ekranımıza bir komut ( lcd_reg = 0; //geçici kayıt defterine 0 yazın lcd_reg.BL = BL_status.b0; //arka ışık pinini, arka ışık durumunu saklayan değişkenin değerine göre ayarlayın lcd_reg.DB4 = com. b4; // komutumuzun en önemli 4 bitini gösterge veri yoluna ayarlayın lcd_reg.DB5 = com.b6; lcd_reg.DB7 = com.b7; //strobe çıkışını 1'e ayarlayın lcd_reg); PCF8574 kaydı, aslında gecikme_us göstergesine veri gönderiyor (300); //zaman aşımı için bekleyin lcd_reg.EN = 0; //flaş darbesini 0'a sıfırlayın, gösterge verileri okur I2C_PCF8574_WriteReg (lcd_reg = 0); .BL = BL_status.b0; lcd_reg.DB4 = com.b0; //4 düşük bit için aynı lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; lcd_reg.EN = 1; I2C_PCF8574_WriteReg(lcd_reg); gecikme_us(300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg(lcd_reg); gecikme_us(300); ) void LCD_CHAR (unsigned char com) //veriyi (ASCII karakter kodu) göstergeye gönderiyoruz ( lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; //bir karakter gönderiyoruz RS bitini 1'e ayarlayarak komut göndermekten farklıdır lcd_reg.DB4 = com.b4; //girişlerdeki en önemli 4 biti ayarlayın lcd_reg.DB5 = com.b5; lcd_reg.DB7 = com.b7; 300); lcd_reg.EN = 0; // flaşı 0'a sıfırlayın, gösterge verileri okur I2C_PCF8574_Reg (lcd_reg); lcd_reg.DB4 = com.b0; .DB5 = com.b1; lcd_reg.DB7 = com.b3); gecikme_us (300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg (lcd_reg); //MK gecikme_ms için I 2c modülümüzü başlat 200) ;lcd_Command(0x28); // Saat modu başına 4 bitlik gösterimde gecikme_ms (5); lcd_Command(0x08); //Ekran gecikme_ms'sine (5) veri çıkışını devre dışı bırakın; lcd_Command(0x01); //Ekran gecikmesini temizle_ms (5); lcd_Command(0x06); //delay_ms (5) sembolünü görüntüledikten sonra otomatik imleç kaydırmayı etkinleştirin; lcd_Command(0x0C); //İmleci göstermeden bilgi görüntülemeyi aç gecikme_ms (25); ) void lcd_I2C_txt(char *pnt) //Ekrana bir karakter dizisi çıktısı ( unsigned short int i; //geçici karakter dizisi indeks değişkeni char tmp_str; //karakterlerin geçici dizisi, uzunluk ekranın uzunluğundan 1 daha büyük satır, çünkü satırın bir NULL ASCII karakteriyle sonlandırılması gerekiyor сiv 0x00 strncpy(tmp_str, pnt, displenth); //orijinal dizgenin en fazla displenth karakterini geçici dizgemize kopyalayın (i=0; i) Şimdi yeni oluşturduğumuz kütüphaneyi ana fonksiyonumuzla dosyaya bağlayalım:

#include "i2c_lcd.h" //başlık dosyamızı unsigned int i'ye dahil ediyoruz; //geçici değişken sayacı void main() ( lcd_I2C_Init(); //ekranı başlat lcd_I2C_backlight (1); //arka ışığı aç lcd_I2C_txt ("Merhaba habrahabr"); //satırı göster while (1) ( gecikme_ms( 1000) ; lcd_I2C_Goto (2,1); // 2. satırın 1. karakterine git lcd_i2c_int (i); // i++ değerini göster ) )

Her şey doğru bir şekilde monte edilmişse gösterge üzerinde yazı ve her saniye artan bir sayaç görmeliyiz. Genel olarak karmaşık bir şey yok :)

Bir sonraki yazımızda i2c protokolünü ve onunla çalışan cihazları anlamaya devam edeceğiz. EEPROM 24XX bellek ve MPU6050 ivmeölçer/jiroskop ile çalışmayı düşünelim.

Son zamanlarda lastikle ilgili olumsuz eleştirilerle giderek daha fazla karşılaşıyorum. I2C en STM32, onunla çalışmanın tefle dans etmek vb. anlamına geldiğini söylüyorlar.
Geçen ay, üzerinde çalışan iki mikro devreyi başlatmayı başardım I2C ve dans etmek yok, sadece veri sayfasını dikkatlice okumak.

Modül I2C en STM32 aşağıdaki özelliklere sahiptir:

  • iki modda çalışabilir FM(hızlı mod) ve Sm(standart mod), birincisi 400KHz'e kadar, ikincisi 100KHz'e kadar frekanslarda çalışır
  • Destekli 1 bayt tampon DMA
  • donanım sağlama toplamı hesaplamasını destekler
  • temelinde uygulanması mümkündür SMBus(Sistem Yönetim Veriyolu) ve PMBus(Güç Yönetimi Veriyolu)
  • İletim başarılı olduğunda ve bir hata oluştuğunda oluşturulan iki kesme vektörü
  • gürültü filtresi
  • Master veya Slave modunda çalışabilir
Ana modda:
  • bir zamanlama sinyali üretir
  • BAŞLAT ve DURDUR'u üretir
Slave modunda:
  • yanıt vereceği ana ve alternatif adresleri programlayabilirsiniz
  • STOP'u tanımlar

Varsayılan olarak modül modundadır Köle ancak otomatik olarak moda geçer Usta devlet oluşumundan sonra BAŞLANGIÇ.
Master ve Slave arasındaki temel fark şudur: Usta bir saat sinyali üretir ve her zaman bir veri aktarımını başlatır ve bitirir. Köle Aynı, adresine yanıt verir ve yayın yapar ve yayın adresine verilen yanıt devre dışı bırakılabilir. Ayrıca Köle durum üretir ACK, ancak aynı zamanda devre dışı bırakılabilir.

Bu kadar ayrıntılı bir açıklama gereklidir çünkü her iki modda da cihaz hem verici hem de alıcı olarak hareket edebilir.

  • Köle verici
  • Köle alıcısı
  • Ana verici
  • Ana alıcı

I2C modülünün yapısı aşağıda gösterilmiştir.

Kontrol kaydı I2C_CR1:

SWRST(Yazılım sıfırlama) - bu bitteki bir birim, modülün tüm kayıtlarının değerini varsayılan duruma sıfırlar; bir hata oluştuğunda sıfırlamak için kullanılabilir.

UYARI(SMBus uyarısı) - bu bitin bire ayarlanması sinyalin oluşturulmasına izin verir uyarı modunda SMBus.

PEC(Paket hatası kontrolü) - bu bit yazılım tarafından kontrol edilir, ancak PEC, START, STOP veya PE=0 iletildiğinde donanım tarafından sıfırlanabilir. Bu bitteki bir, iletimi mümkün kılar ÇHS.

POS(Onay/PEC Konumu (veri alımı için)) - bu bitin durumu konumu belirler ACK/PEC Ana modda iki baytlık bir yapılandırmada.

ACK(Onay etkinleştirme) - bu bitteki bir birim göndermeye izin verir ACK/NACK bir adres veya veri baytı aldıktan sonra.

DURMAK(Oluşturmayı durdur) - bu bitin bire ayarlanması bir sinyal üretir DURMAK Ana modda.

BAŞLANGIÇ(Oluşturmayı başlat) - bu bitin bire ayarlanması bir durum oluşturur BAŞLANGIÇ Ana modda,

BURUN AÇILMASI(Saat uzatmayı devre dışı bırakın (Bağımlı mod)) - veri işleme zaman alırsa Köle hatta basarak master'ın iletimini durdurabilirsiniz SCL Yere indiğinde Master bekleyecek ve hat serbest bırakılana kadar hiçbir şey göndermeyecektir. Bu bitteki sıfır basar SCL yere.

ENGC(Genel çağrı etkinleştirme) - bu bit bire ayarlanırsa modül yanıt verir ACK om 0x00 yayın adresine.

ENPEC(PEC etkin) - bu bitin bire ayarlanması donanım sayımına olanak sağlar ÇHS.

ENARP(ARP etkinleştirme) - bu bitin bire ayarlanması etkinleştirilir ARP.

SMB TİPİ(SMBus tipi) - eğer bu bit sıfıra ayarlanırsa modül Slave modunda, eğer biri Master modundaysa çalışır.

SMBUS(SMBus modu) - eğer bu bit sıfıra ayarlanırsa modül çalışır. I2C, Eğer biri SMBus.

P.E.(Çevresel etkinleştirme) - bu bitteki bir birim modülü etkinleştirir.

Kontrol kaydı I2C_CR2:

SON(DMA son aktarımı) - bu bitteki bir tane izin verir DMA bir iletim sonu sinyali oluşturmak EOT(Transferin Sonu).

DMAEN(DMA istekleri etkin) - bu bitteki bir birim, bir isteğin yapılmasına izin verir. DMA bayrakları ayarlarken TxE veya RxNE.

İTBUFEN(Arabellek kesme etkinleştirme) - eğer bu bit temizse, alma ve gönderme kesmeleri dışında tüm kesmeler etkinleştirilir.

İTEVTEN(Olay kesmeyi etkinleştirme) - bu bitteki bir, olay kesmeyi etkinleştirir.

ITERREN(Hata kesmeyi etkinleştirme) - bu bitteki bir, hatalar meydana geldiğinde kesintileri etkinleştirir.

frekans(Çevresel saat frekansı) - modül saat frekansı bu bit alanına yazılmalıdır; 2'den 50'ye kadar bir değer alabilir.

I2C_OAR1'i kaydedin:

EKLEME MODU(Adresleme modu) - bu bit, Slave adresinin boyutunu belirler, sıfır, 7 bitlik adres boyutuna, bir - 10 bitlik adres boyutuna karşılık gelir.

EKLEMEK(Arayüz adresi) - adres 10 bit ise adresin en önemli bitleri.

EKLEMEK(Arayüz adresi) - cihaz adresi.

EK0(Arayüz adresi) - adres 10 bit ise adresin en az anlamlı biti.

I2C_OAR2 kaydı:

EKLE2- Slave'in yanıt vereceği alternatif bir adres.

SONUÇ(Çift adresleme modunu etkinleştir) - bu bitteki bir, Slave'in 7 bit modunda alternatif bir adrese yanıt vermesini sağlar.

I2C_DR- veri kaydı, yazdığımız verileri kayıt defterine göndermek için Dr., resepsiyon için okuyoruz.

I2C_SR1 durum kaydı:

SMBALERT(SMBus uyarısı) - otobüste bir uyarı olması durumunda oluşur SMBus.

ZAMAN AŞIMI(Zaman Aşımı veya Tlow hatası) - satırda hata oluşursa SCL yere bastırıldı. İçin usta 10ms, için köle 25mS.

PEÇERR(Alımda PEC Hatası) - bir hata olduğunda ortaya çıkar PEC kabul üzerine.

OVR(Aşırı/Underrun) - veri taşması durumunda oluşur.

A.F.(Arızayı onaylama) - bir sinyal alındığında ayarlanır NACK. Sıfırlamak için 0 yazmanız gerekmektedir.

ARLO(Tahkim kaybı (ana mod)) - tahkim kaybolduğunda ayarlanır. Sıfırlamak için 0 yazmanız gerekmektedir.

BERR(Veri yolu hatası) - veri yolu hatası. Bir sinyalin ne zaman oluşacağını ayarlayın BAŞLANGIÇ veya DURMAK yanlış anda.

TxE(Veri kaydı boş (vericiler)) - DR kaydı boş olduğunda veya ondan gelen veriler kaydırma kaydına taşındığında ayarlanır.

RxNE(Veri kaydı boş değil (alıcılar)) - adres dışında bir bayt veri alınırken ayarlanır.

DUR(Algılamayı durdur (bağımlı mod)) - çalışırken köle bir sinyal algılandığında ayarlanır DURMAK, daha önce bir ACK sinyali varsa. Sıfırlamak için okumalısınız SR1 ve kaydedin CR1.

EK10(10 bitlik başlık gönderildi (Ana mod)) - 10 bitlik bir adresin ilk baytını gönderirken ayarlayın.

BTF(Bayt aktarımı tamamlandı) - bayrak, bayt alındığında/iletildiğinde ayarlanır; BURUN AÇILMASI sıfıra eşittir.

ADDR(Adres gönderildi (ana mod)/eşleşti (bağımlı mod)) - modunda usta modunda adres aktarımından sonra ayarlanır köle adres eşleştiğinde ayarlanır. Sıfırlamak için SR1 ve ardından SR2 kaydını okumanız gerekir.

S.B.(Başlat biti (Ana mod)) - BAŞLAT sinyali oluştuğunda ayarlayın. Bayrağı sıfırlamak için şunu okumalısınız: SR1 ve verileri kayıt defterine yazın Dr. .

I2C_SR2 durum kaydı:

PEC(Paket hatası kontrol kaydı) - çerçeve sağlama toplamı bu bit alanına yazılır.

ÇİFT(Çift bayrak (Slave modu)) - bu bitteki sıfır, Slave'in aldığı adresin karşılık geldiğini gösterir. OAR1, aksi takdirde OAR2.

SMBHOST(SMBus ana bilgisayar başlığı (Slave modu)) - başlığın ne zaman alınacağını ayarlayın SMBus Ana Bilgisayarı.

SMBDEFAULT(SMBus cihazının varsayılan adresi (Slave modu)) - varsayılan adresin kabul edilip edilmediğini ayarlayın
İçin SMBus-cihazlar.

GENÇALL(Genel çağrı adresi (Bağımlı mod)) - bağımlı modda bir yayın adresinin alınıp alınmayacağını ayarlayın.

TRA(Verici/alıcı) - bu bitteki bir, modülün bir verici, aksi takdirde bir alıcı olarak çalıştığını gösterir.

MEŞGUL(Otobüs meşgul) - meşgul bayrağı.

MSL(Ana/bağımlı) - bu bitteki bir, modülün Ana modda çalıştığını, aksi takdirde Bağımlı olduğunu gösterir.

Frekans kontrol kaydı I2C_CCR:

F/K(I2C ana mod seçimi) - bu bit bire ayarlandığında modül HIZLI, aksi takdirde STANDART.

GÖREV(Fm modu görev döngüsü) - bu bit sinyal görev döngüsünü ayarlar SCL modunda HIZLI. Sıfır ayarlanırsa alt/uyluk = 2, aksi halde alt/uyluk = 16/9.

CCR(Fm/Sm modunda saat kontrol kaydı (Ana mod)) - Ana modda çalışırken, SCL hattının saat frekansını ayarlar.

Sm modu veya SMBus:
Uyluk = CCR * TPCLK1
Düşük = CCR * TPCLK1

FM modu:
GÖREV = 0 ise:
Uyluk = CCR * TPCLK1
Düşük = 2 * CCR * TPCLK1

DUTY = 1 ise: (400 kHz'e ulaşmak için)
Uyluk = 9 * CCR * TPCLK1
Düşük = 16 * CCR * TPCLK1

Mod için alıyoruz S.M. takip etme:
CCR * TPCLK1 + CCR * TPCLK1 = 10.000ns
CCR = 10.000/(2* TPCLK1)

I2C_TRISE kaydı:

ÜÇLÜ- Cephenin yükselme zamanını belirler. Formül kullanılarak hesaplanır (Tr maks/TPCLK1)+1,
Nerede TR maksimumİçin S.M.şuna eşittir: 1000nS, ve için FM 300nS,
A TPCLK1- 1/ olarak hesaplanan süre F(APB1).

Filtre kontrol kaydı I2C_FLTR:

ANOFF(Analog gürültü filtresi KAPALI) - bu bitteki sıfır, analog filtreyi açar.

DNF(Dijital gürültü filtresi) - dijital filtre ayarlamak için bit alanı. Ayrıntılar için lütfen belgelere bakın.

Çalışan bir projeden bir modülün başlatılması.
void I2C2_Init(void) ( /* SDL -> PB10 SDA -> PB11 RST -> PE15 */ // portların ve I2C modülünün saat hızını etkinleştirin RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEN | RCC_AHB1ENR_GPIOEEN; RCC->APB1ENR |= RCC_APB1ENR_I2C2EN; / / alternatif fonksiyon, açık drenaj çıkışı, 2 MHz GPIOB->AFR |= (0x04<<2*4); GPIOB->AFR |= (0x04<<3*4); GPIOB->MODER |= GPIO_MODER_MODER10_1; GPIOB->OTYPER |= GPIO_OTYPER_OT_10; GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR10; GPIOB->MODER |= GPIO_MODER_MODER11_1; GPIOB->OTYPER |= GPIO_OTYPER_OT_11; GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR11; //PE15 itme-çekme çıkışı 50MHz GPIOE->MODER |= GPIO_MODER_MODER15_0; GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15; AU_RST_HIGH //modülü I2C moduna ayarlayın I2C2->CR1 &= ~2C_CR1_SMBUS; //I2C2->CR2 &= ~I2C_CR2_FREQ; modülünün saat frekansını belirtin I2C2->CR2 |= 42; // Fclk1=168/4=42MHz //I2C'yi yapılandırma, standart mod, 100 KHz görev döngüsü 1/2 I2C2->CCR &= ~(I2C_CCR_FS | I2C_CCR_DUTY); //10,000nS/(2* TPCLK1) I2C2->CCR |= 208 formülünü kullanarak SCL modülünün çalışma frekansını ayarlayın; //10 000ns/48ns = 208 //Standart_Mode = 1000nS, Fast_Mode = 300nS, 1/42MHz = 24nS I2C2->TRISE = 42; //(1000nS/24nS)+1 //modülü açın I2C2->CR1 |= I2C_CR1_PE; ) void I2C_Write(uint8_t reg_addr, uint8_t data) ( //I2C2->CR1'i başlat |= I2C_CR1_START; while(!(I2C2->SR1 & I2C_SR1_SB))(); (void) I2C2->SR1; //cihazı ilet adres I2C2->DR = I2C_ADDRESS(ADDR,I2C_MODE_WRITE); while(!(I2C2->SR1 & I2C_SR1_ADDR))(); (void) I2C2->SR2; >DR = reg_addr; (!(I2C2->SR1 & I2C_SR1_TXE))(); //veriyi yaz I2C2->DR = veri; while(!(I2C2->SR1 & I2C_SR1_BTF))() |= I2C_CR1_STOP; ) uint8_t I2C_Read(uint8_t reg_addr) ) ( uint8_t data; //başlat I2C2->CR1 |= I2C_CR1_START; while(!(I2C2->SR1 & I2C_SR1_SB))(); (void) I2C2->SR1; / /cihaz adresini ilet I2C2->DR = I2C_ADDRESS (ADR,I2C_MODE_WRITE); while(!(I2C2->SR1 & I2C_SR1_ADDR))(); (void) I2C2->SR2; kayıt adresi I2C2->DR = reg_addr; I2C2->SR1 & I2C_SR1_TXE))(); I2C2->CR1 |= I2C_CR1_STOP; //yeniden başlat!!! //cihaz adresini aktarıyoruz, ancak şimdi okumak için I2C2->DR = I2C_ADDRESS(ADR,I2C_MODE_READ); while(!(I2C2->SR1 & I2C_SR1_ADDR))()); (geçersiz) I2C2->SR1; (geçersiz) I2C2->SR2; //I2C2->CR1'i oku &= ~I2C_CR1_ACK; while(!(I2C2->SR1 & I2C_SR1_RXNE))(); veri = I2C2->DR; I2C2->CR1 |= I2C_CR1_STOP; verileri döndür; )

Makale, STMicroelectronics'in STM32 serisinin 32 bit ARM mikro denetleyicilerinin I2C seri arayüzünü açıklamaktadır. Arayüz konfigürasyon kayıtlarının mimarisi, bileşimi ve amacı dikkate alınmış ve kullanımına yönelik program örnekleri verilmiştir.

giriiş

I2C arayüzü veya IIC, kısaltmasını İngilizce Inter-Integrated Circuit kelimesinden alır ve Seri Veri Adresi ve Seri Saat kelimelerinin kısaltması olan SDA ve SCL adı verilen iki çift yönlü iletişim hattından oluşan bir seri veri yoludur. Mikrodenetleyici ile ADC, DAC, bellek yongaları, diğer mikrodenetleyiciler ve yongalar gibi çeşitli çevre birimleri arasında veri alışverişini sağlar. Cihazları I2C arayüzü üzerinden bağlama şeması Şekil 1'de gösterilmektedir.

Pirinç. 1. I 2 C arayüzü üzerinden cihazların bağlantı şeması

I2C arayüz standardı 1980'lerin başında Philips tarafından geliştirildi. Bu standarda göre arayüz 7 bitlik bir adrese sahipti. 100 kbps'ye kadar hızlarda 127 cihaza erişime izin verdi. Daha sonra arayüz geliştirilerek 10 bit haline getirildi ve 1023 cihaza 400 kbit/s'ye varan hızlarda erişim sağlandı. Maksimum
bir veri yoluna bağlanan izin verilen çip sayısı, 400 pF'lik maksimum veri yolu kapasitansı ile sınırlıdır. Standardın 1998 yılında yayımlanan 2.0 versiyonu tanıtıldı
Azaltılmış güç tüketimi ile 3,4 Mbit/s'ye kadar hızlara sahip yüksek hızlı çalışma modu. 2001'deki Sürüm 2.1 yalnızca küçük iyileştirmeler içermektedir.

I 2 C arayüzünün açıklaması

STM32 mikrodenetleyici, gelişimiyle öne çıkan bir I2C arayüzü içerir. Birden fazla veri yolu yöneticisine izin verir ve yüksek hızlı modu destekler. Ayrıca STM32 mikrodenetleyici, sağlama toplamı oluşturma ve doğrulama da dahil olmak üzere çok çeşitli uygulamalar için I2C arayüzünü kullanabilir. Ayrıca SMBus (Sistem Yönetim Veriyolu) ve PMBus (Güç Yönetim Veriyolu) protokolleri aracılığıyla da çalıştırılabilir. Çoğu STM32 modeli, I2C1 ve I2C2 adlı iki I2C arayüzü içerir. Arayüz aşağıdaki dört moddan birinde çalışabilir:

  • Köle verici;
  • Köle alıcısı;
  • Ana verici (öncü verici);
  • Ana alıcı.

Varsayılan olarak arayüz "Slave" modunda çalışır ve bir başlatma koşulu oluşturduktan sonra otomatik olarak "Master" moduna geçer. "Ana"dan "Bağımlı"ya geçiş, tahkim kaybolduğunda veya birkaç "Ana" mikro denetleyicinin bir sistemde dönüşümlü olarak çalışmasına izin veren bir durdurma koşulu oluşturulduktan sonra gerçekleşir. Master modunda I2C veri alışverişini başlatır ve bir saat sinyali üretir. Seri veri aktarımından önce her zaman bir başlangıç ​​koşulu gelir ve değişim her zaman bir durma koşuluyla sona erer. Bu koşulların her ikisi de Master modunda programlı olarak oluşturulur. Slave modunda I2C kendi adresini (7 veya 10 bit) ve genel çağrı adresini tanıyabilir. Tüm çağrıların adresi tespiti programlı olarak etkinleştirilebilir veya devre dışı bırakılabilir. Adres ve veriler, en anlamlı bit başta olmak üzere 8 bitlik paketler halinde iletilir. Başlangıç ​​koşulundan sonraki ilk bayt adresi içerir (7 bit modunda bir bayt ve 10 bit modunda iki bayt). Adres her zaman Ana modda iletilir.

Bir veri baytının iletildiği 8 saat döngüsünü, alıcının adını ACKnowledge kelimesinden alan ACK bildirim bitini göndermesi gereken 9. saat döngüsü takip eder. Şekil 2, I2C arayüzünün bir mesajının zamanlama diyagramını göstermektedir. Yanıtta bir bildirimin bulunması programlı olarak etkinleştirilebilir veya devre dışı bırakılabilir. I2C arayüzü adres boyutu (7 bit veya 10 bit ve adres
Tüm Çağrılar) programlı olarak seçilebilir.


Pirinç. 2. Bir arayüz I paketinin zamanlama diyagramı

I 2 C arayüz bloğunun mimarisi

STM32 mikrodenetleyicisine ait I2C arayüz bloğunun fonksiyonel diyagramı Şekil 3’te gösterilmektedir.


Pirinç. 3. I 2 C arayüz bloğunun fonksiyonel şeması

Bu diyagramdaki kaydırma kaydı, verilerin gönderildiği ve alındığı ana kayıttır. İletilen veriler veri kaydına önceden kaydedilir ve ardından kaydırma kaydı aracılığıyla sırayla SDA iletişim hattına iletilir. Aynı iletişim hattı üzerinden alınan veriler bir kaydırma yazmacında toplanır ve daha sonra veri yazmacına taşınır. Böylece arayüz bir seferde yalnızca bir veri gönderip alabilir. Ek olarak, kaydırma yazmacı donanım olarak bir karşılaştırıcıya bağlanır, bu da alınan adresi karşılaştırmanıza olanak tanır
adres kayıtları ile birleştirir ve böylece bir sonraki veri bloğunun kime yönelik olduğunu belirler. Frekans kontrol düğümü, SCL senkronizasyon sinyalini ana cihaz olarak oluşturmanıza ve bu sinyalden yardımcı cihaz olarak senkronizasyon yapmanıza olanak tanır. CCR kaydı bu düğümün yazılım konfigürasyonunu sağlar. Arayüz bloğu APB1 veriyolunun PCLK1 çıkışına şu şekilde bağlanır:
iki ön ölçekleyici. Mikrodenetleyici iki iletişim modunu destekler: Standart Hız - 100 kHz'e kadar ve Hızlı Hız - 400 kHz'e kadar. Değişim moduna bağlı olarak modül saat frekansı standart modda en az 2 MHz, hızlı modda en az 4 MHz olmalıdır. Hesaplama bloğu, donanımın bir veri bloğunun sağlama toplamını hesaplamasına ve bunu PEC kaydında saklamasına olanak tanır. I2C arayüz bloğunun kontrolünün yanı sıra olay ve kesme bayraklarının oluşturulması da kontrol mantık ünitesi tarafından gerçekleştirilir. Ayrıca DMA isteklerine hizmet vermenize ve bir ACK sinyali oluşturmanıza da olanak tanır. Bu bloğun mikro denetleyici ile iletişimi, CR1, CR2 kontrol kayıtları ve SR1, SR2 durum kayıtları kullanılarak programlı olarak gerçekleştirilir.

I 2 C'den gelen kesintiler

I2C arayüzü, çalışma moduna ve güncel olaylara bağlı olarak kesme istekleri oluşturabilen bir donanım organizasyonuna sahiptir. Tablo 1, I2C arayüzünden gelen kesme isteklerini göstermektedir.

Tablo 1. I 2 C arayüzünden gelen kesme istekleri

Kayıtların açıklaması

I2C arayüzüyle çalışmak için STM32 mikrodenetleyicinin özel kayıtları vardır. Bu kayıtların, içerdikleri bitlerin adlarıyla birlikte bir haritası Tablo 2'de sunulmaktadır. I2C arayüzünün çalışması için gerekli kayıtları ele alalım. Bunlar şunları içerir:

  • I 2 C_CR1 – kontrol kaydı 1;
  • I 2 C_CR2 – kontrol kaydı 2;
  • I 2 C_OAR1 – kendi adres kaydı 1;
  • I 2 C_OAR2 – kendi adres kaydı 2;
  • I 2 C_DR – veri kaydı;
  • I 2 C_SR1 – durum kaydı 1;
  • I 2 C_SR2 – durum kaydı 2;
  • I 2 C_CCR – saat sinyali kontrol kaydı;
  • I 2 C_TRISE – TRISE parametre kaydı.

Bu kayıtların bazı bitleri SMBus modunda çalışmak için kullanılır. I2C_CR1 kaydı, I2C arayüzünün ilk kontrol kaydıdır. Aşağıdaki kontrol bitlerine sahiptir:

  • bit 15 SWRST – I 2 C veriyolunun yazılım sıfırlamasını sağlar;
  • rakam 14 – ayrılmıştır;
  • bit 13 SMBus – SMBus modunda bir alarm sinyali üretir;
  • bit 12 PEC – Paket Hata Kontrolü işlevine hizmet eder;
  • bit 11 POS – alım üzerine ACK veya PEC sinyallerini analiz etmeye yarar;
  • bit 10 ACK – geçerli bir adres veya veri baytı aldıktan sonra ACK bildirim bitini döndürür;
  • bit 9 STOP – bir durma koşulunun oluşturulmasına ve analiz edilmesine hizmet eder;
  • bit 8 START – başlatma koşulunu oluşturmaya ve analiz etmeye yarar;
  • bit 7 NOSCTETCH – bağımlı modda saat genişletmeyi devre dışı bırakır;
  • bit 6 ENGC – genel aramaya izin verir;
  • bit 5 ENPEC – PEC sinyalini etkinleştirir;
  • bit 4 ENARP – ARP sinyalini etkinleştirir;
  • bit 3 SMBTYPE – arayüz tipini SMBus modu için ana veya bağımlı olarak atar;
  • rakam 2 – ayrılmış;
  • bit 1 SMBUS – I 2 C ve SMBus modlarını değiştirir;
  • bit 0 PE – arayüzün çalışmasına izin verir.

I2C_CR2 kaydı, I2C arayüzünün ikinci kontrol kaydıdır ve aşağıdaki kontrol bitlerine sahiptir:

  • bitler 15…13 – ayrılmış;
  • bit 12 LAST – alınan son bayta dayalı olarak bir NACK sinyalinin oluşturulmasına izin vermek için ana alıcı modunda kullanılır;
  • bit 11 DMAEN – DMA isteğine izin verir;
  • bit 10 ITBUFEN – ara bellekteki kesintilere izin verir;
  • bit 9 ITEVTEN – olay kesintilerini etkinleştirir;
  • bit 8 ITERREN – hata kesintilerini etkinleştirir;
  • 7 ve 6 rakamları – ayrılmıştır;
  • bitler 5…0 FREQ – veriyolu çalışma frekansını ayarlayın.

I2C_OAR1 kaydı, kendi adresinin ilk kaydıdır ve aşağıdaki bitleri içerir:

  • bit 15 ADDMODE – 7 veya 10 bit adresleme modunu bağımlı olarak ayarlar;
  • bitler 14…10 – ayrılmış;
  • bit 9 ve 8 ADD – 10 bitlik arayüz adreslemesi için 9 ve 8 adres bitlerini atayın;
  • bitler 7…1 ADD – 7…1 adres bitlerini atayın;
  • bit 0 ADD0 – 10 bitlik arayüz adreslemesi için adres biti 0'ı atar.

I2C_OAR2 kaydı, kendi adresinin ikinci kaydıdır ve aşağıdaki bitleri içerir:

  • bitler 15...8 – ayrılmış;
  • bitler 7…1 ADD – çift adresleme modunda 7…1 adres bitlerini atayın;
  • bit 0 ENDUAL – çift adresleme moduna izin verir.

I2C_DR veri kaydı, I2C veriyolu üzerinden veri almak ve iletmek için 8 DR bitine sahiptir. Veriler iletim için bu kayda yazılır ve alındıktan sonra buradan okunur. 15...9 bitleri ayrılmıştır. I2C_SR1 kaydı ilk durum kaydıdır ve aşağıdaki bitleri içerir:

  • bit 15 SMBALERT – bir SMBus veriyolu alarmını bildirir;
  • rakam 13 – ayrılmıştır;
  • bit 14 TIMEOUT – SCL sinyali için zaman aşımı hatasını bildirir;
  • bit 12 PECERR – alım sırasında bir PEC hatasını gösterir;
  • bit 11 OVR – veri taşması hatası olduğunda oluşturulur;
  • bit 10 AF – bildirim hatası durumunda oluşur;
  • bit 9 ARLO – veri yolu hakları hatasını gösterir;
  • bit 8 BERR – bir veri yolu hatası olduğunda ayarlanır;
  • bit 7 TxE – veri kaydının boş olduğunu bildirir;
  • rakam 5 – ayrılmıştır;
  • bit 6 RxNE – veri kaydının boş olmadığını bildirir;
  • bit 4 STOPF – bağımlı modda bir durma koşulunu algılar;
  • bit 3 ADD10 – master adresin ilk baytını 10 bit adreslemeyle gönderdiğinde ayarlanır;
  • bit 2 BTF – bayt aktarımının tamamlandığını bildirir;
  • bit 1 ADDR – ana modda bir adresin gönderilip gönderilmediğini veya ikincil modda bir adresin alınıp alınmadığını ayarlayın;
  • bit 0 SB – ana modda bir başlatma koşulu oluşturulurken ayarlanır.

I2C_SR2 kaydı ikinci durum kaydıdır ve aşağıdaki bitleri içerir:

  • bitler 15…8 PEC – çerçeve sağlama toplamını içerir;
  • bit 7 DUALF – bağımlı modda çift adresleme bayrağıdır;
  • bit 6 SMBHOST – SMBus Ana Bilgisayar başlığı bağımlı modda alındığında ayarlanır;
  • bit 5 SMBDEFAULT – bağımlı moddaki SMBus cihazının varsayılan adresi kabul edilirse oluşur;
  • bit 4 GENCALL – bağımlı moddaki genel çağrı adresinin alındığını gösterir;
  • rakam 3 – ayrılmış;
  • bit 2 TRA – iletim/alma modu hakkında bilgi verir;
  • bit 1 BUSY – veriyolunun meşgul olduğunu bildirir;
  • bit 0 MSL – “Master”/“Slave” modunu algılar.

I2C_CCR kaydı, aşağıdaki bitleri içeren bir saat sinyali kontrol kaydıdır:

  • bit 15 F/S – ana mod için standart veya yüksek hızı ayarlar;
  • bit 14 DUTY – hızlı modda 2 veya 16/9'luk bir görev döngüsü atar;
  • 13 ve 12. rakamlar ayrılmıştır;
  • bit 11…0 CCR – ana modda hızlı ve standart hız için saat sinyalini kontrol eder.

I2C_TRISE kaydı, aşağıdakileri içeren bir TRISE parametre kaydıdır:

  • bitler 15...6 – ayrılmış;
  • bitler 5…0 TRISE – ana modda hızlı ve standart hız için maksimum yükselme süresini tanımlar. Bu parametre, satır durumlarının örneklendiği zaman noktasını belirtir.

Tüm I2C kayıtlarının ve bitlerinin amacına ilişkin daha ayrıntılı bir açıklama www.st.com web sitesinde bulunabilir.

Pprogramlama arayüzBEN 2 İLE

I2C arayüzünü kullanmanın pratik bir uygulamasını düşünelim. Bunu yapmak için STM32 mikrodenetleyicinin standart çevre birimi kütüphanesini kullanabilirsiniz. I2C arayüzü için mod, hız ve diğer her şeye ilişkin ayarlar başlık dosyasında bulunur ve bir yapı olarak bildirilir:

I2C_InitTypeDef:typedef struct( uint32_t I2C_ClockSpeed; uint16_t I2C_Mode; uint16_t I2C_DutyCycle; uint16_t I2C_OwnAddress1; uint16_t I2C_Ack; uint16_t I2C_ AcknowledgedAddress; )I2C_InitType Def;

Bu yapıdaki elemanların amaçları şu şekildedir:

  • uint32_t I 2 C_ClockSpeed ​​– saat sinyali frekansı, maksimum – 400 KHz;
  • uint16_t I 2 C_Mode – çalışma modu;
  • uint16_t I 2 C_DutyCycle – hızlı modda çalışma ayarları;
  • uint16_t I 2 C_OwnAddress – cihazın kendi adresi;
  • uint16_t I 2 C_Ack – ACK onay bitinin kullanımının etkin mi yoksa devre dışı mı olduğu;
  • uint16_t I 2 C_AcknowledgedAddress – adres formatını seçin: 7 bit veya 10 bit.

I2C arayüzünü başlatma ve onunla çalışma prosedürlerine bakalım. I2C arayüzünü ana cihaz olarak yapılandırmak ve onun üzerinden veri aktarmak için aşağıdaki adımları uygulamanız gerekir:

  1. bağlantı noktası saatlamasına izin ver;
  2. hızını, adresini ve adres formatını belirterek I 2 C'yi başlatın;
  3. mikrodenetleyici pinlerini atayın;
  4. arayüzü etkinleştirin;
  5. bir başlangıç ​​koşulu oluşturun;
  6. adreslenen cihazın adresini ve verilerini gönderin;
  7. bir durma koşulu oluşturun.

Programlama sürecini kolaylaştırmak için I2C ile çalışmaya yönelik bir dizi temel işlev oluşturmanız önerilir. Liste 1, yukarıda açıklanan algoritmaya uygun olarak I2C arayüzünü başlatma fonksiyonunu göstermektedir.

Listeleniyor 1 GPIO_InitTypeDef gpio; // G/Ç bağlantı noktaları için bir yapı oluşturun I2C_InitTypeDef i2c; // I2C arayüzü için bir yapı oluşturun void init_I2C1(void) ( // Saatlemeyi etkinleştirin RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // I2C'yi başlat i2c.I2C_ClockSpeed ​​= 10000; i2c.I2C_Mode = I2C_Mode_I2C; = I2C_DutyCycle_2; // Adresi ayarla=0x12 i2c.I2C_OwnAddress1 = 0x12; i2c.I2C_Ack = I2C_Ack_Disable; gpio.GPIO_Pin = GPIO_Pin_6 |

Şimdi I2C aracılığıyla iletişim kurma fonksiyonuna bakalım. Yeteneklerini genişletmek için bu işlevin üç parametresi vardır: kullanılan I2C bloğunun sayısı, veri aktarımının yönü ve yardımcı cihazın adresi. Bu fonksiyonun kodu Liste 2'de gösterilmektedir.

Listeleme 2 void I2C_StartTransmission(I2C_TypeDef* I2Cx, uint8_t iletimDirection, uint8_tslavAddress) ( // (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)); // Başlangıç ​​koşulunu oluşturun I2C_GenerateSTART(I2Cx, ENABLE); // Bitin ayarlanmasını bekleyin while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)); // Adresi köleye gönder I2C_Send7bitAddress(I2Cx,slavAddress,transmitterDirection); // Veri aktarılıyorsa if(transmissionDirection== I2C_Direction_Transmitter) (while(! I2C_CheckEvent(I2Cx, I2C_EVENT_MA) STER_TRANSMITTER_MODE_SELECTED));) // Veri alınırsa if(transmissionDirection== I2C_Direction_Receiver) (while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));)) )

Yukarıdaki işlev, Liste 3'te gösterilen verileri göndermek ve almak için basit işlevleri kullanır.

Liste 3 // Veri aktarım fonksiyonu void I2C_WriteData(I2C_TypeDef* I2Cx, uint8_t data) ( // Kütüphane veri aktarım fonksiyonunu çağırın I2C_SendData(I2Cx, data); // Veri aktarımının bitmesini bekleyin while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) )); ) // Veri alma fonksiyonu uint8_t I2C_ReadData(I2C_TypeDef* I2Cx) ( // Verinin gelmesini bekleyin while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)); data = I2C_ReceiveData(I2Cx); // Kayıt dönüşünden verileri okuyun data; //Veriyi çağıran fonksiyona döndür

I2C aracılığıyla veri alışverişini bitirdikten sonra durma koşulu oluşturma işlevini çağırmanız gerekir.
I2C_Generate STOP(I2Cx, ETKİNLEŞTİR).
Yukarıdaki işlevlere dayanarak, çok çeşitli çevresel aygıtlarla çalışacak programlar oluşturabilirsiniz.

Çözüm

I2C arayüzünün yadsınamaz avantajı, cihazları yalnızca iki iletişim hattı ve ortak bir kablo kullanarak bağlama kolaylığıdır, bu sayede bu arayüz teknolojide sağlam bir şekilde yerleşmiştir ve hala modern ekipmanlarda yaygın olarak kullanılmaktadır.

I2C veriyolu oldukça uzun bir süredir ortalıkta dolaşıyor: 1980'lerde Philips tarafından düşük hızlı cihazlar için yaratıldı. Şu anda yaygın olarak kullanılıyor ve büyük olasılıkla evinizde bu otobüsle en az bir cihazınız var. Veri yolu adı Inter-Entegre Devre anlamına gelir. Çoğu mikrodenetleyicide şu anda bir donanım I2C modülü bulunur; bazılarında STM32 gibi birkaç tane bulunur (F4 serisinde üç I2C modülü vardır).

Veri yolu, biri veri (SDA), diğeri saat sinyali (SCL) olmak üzere 2 hattan oluşur, her iki hat da başlangıçta güce bağlıdır. Gerilimin tam olarak ne olması gerektiğine dair net bir gösterge bulunmadığına dikkat edilmelidir, ancak en sık +5V ve +3,3V kullanılır. Hattaki cihazlar CAN'daki gibi eşler arası değildir, dolayısıyla her zaman bir Ana cihazın bulunması gerekir. Birden fazla Ana cihaza sahip olmak mümkündür, ancak bu yine de bir Ana Cihaz ve bir grup Bağımlı cihazdan çok daha az yaygındır.

Veri aktarımı, gerekli cihazın adresini veri yoluna gönderen yönetici tarafından başlatılır; saat ölçümü de yönetici tarafından gerçekleştirilir. Ancak aynı zamanda Slave cihazı, sanki Master cihaza veri almak veya göndermek için zamanı olmadığını bildiriyormuşçasına saat hattını "tutma" yeteneğine sahiptir ki bu bazen çok faydalıdır. Mevcut sürümde en yaygın olanı, 100 kHz (Standart mod) ve 400 kHz (Hızlı mod) veri yolu frekanslarına sahip I2C uygulamalarıdır.

2-3 Mbit/s gibi çok daha yüksek hızlara ulaşmanızı sağlayan I2C sürüm 2.0'ın bir uygulaması vardır, ancak bunlar yine de çok nadirdir. Hattın ayrıca 400 pF'lik bir kapasitans sınırı vardır. Genellikle sensörler ve diğer I2C cihazlarının veri sayfaları kapasitelerini gösterir, böylece başka bir sensörün sığıp sığmayacağını kabaca hesaplayabilirsiniz.

Mikrodenetleyicilerde çoğu zaman pinlerde dahili bir çekme bulunur ve bu, serbest durumda hat üzerinde gerekli +3,3V (veya +5V) değerini verir, ancak bu çekme normal bir hat için kesinlikle yeterli değildir. Bu nedenle, hem SCL hem de SDA'nın 4,7 kOhm..2 kOhm dirençli güç kaynağına harici olarak çekilmesi her zaman faydalı olacaktır.

Ayrıca, I2C hattını uzun tutmanın genellikle tavsiye edilmediğini ve çoğu zaman belirli dijital cihazlar arasında değişim için baskılı devre kartlarında bulunduğunu da belirtmek gerekir; I2C, teller üzerinden çok daha az kullanılır (ancak bunun olduğunu düşünmeyin); nadirdir, her ikisi de oldukça normaldir). Uzun bir I2C hattı yapmanız gerekiyorsa ve hatta 400 kHz'de bile, çekme dirençlerinin direncini azaltmalısınız. 1 kOhm, bir metrenin biraz üzerinde uzunlukta ve üzerinde birden fazla cihaz bulunan bir hat için son derece kabul edilebilir bir değerdir. Dirençlerin direncini azaltarak hattaki akımı artıracağınızı, bunun fazla olması durumunda cihazlara zarar verebileceğini unutmayın.

Yazılım açısından bakıldığında, I2C veri yolu üzerindeki değişim şuna benzer: Master, START başlatma sırasını gönderir (yüksek bir SCL seviyesinde, SDA sıfıra çekilir), ardından okuma veya yazma modunu belirten bit bayrağını içeren bir adres gönderir. , aşağıdaki formatta:

Mod bitinin sıfır olması, Master'ın Slave cihazına bilgi yazacağı anlamına gelir, bu da Slave'den bilgi okuyacağı anlamına gelir. Başka bir açıdan bakarsanız, her I2C cihazı iki "sanal" cihaz sağlar; bu, adres baytının tamamı (yani orijinal 7 bit + mod biti) çift ise, o zaman bu bir yazma adresidir, tek ise okumadır. adres. Buna göre veriyolundaki cihaz sayısında bir sınır vardır: 127.

Slave adresini aldıktan sonra, cihazın, hatta böyle bir adrese sahip bir Slave cihazının varlığını doğrulayacak olan adresi kabul etmesi konusunda master'ı bilgilendirmesi gerekir. Onay, adres eşleşiyorsa ve çalışmaya hazırsa sıfıra, eşleşmiyorsa bire eşit olan özel bir 9. bittir. Bunlar sırasıyla ACK ve NACK sinyalleridir. Ayrıca ACK, verilerin daha sonra alınması ve iletilmesi için kullanılır. Master, köleye yazıyorsa, kölenin her baytı bir ACK sinyali ile onaylaması gerekir. Eğer köle yöneticiye veri gönderirse, yöneticinin son bayt dışındaki tüm baytlara ACK ile yanıt vermesi gerekir; bu, daha fazla verinin gönderilmesine gerek olmadığının bir sinyali olacaktır.

Tüm iletimin sonunda Master, SCL hattını yükseltirken SDA hattını yüksek bir seviyeye yükseltmekten oluşan son bir STOP dizisi göndermelidir.

Yani standart "paket" şuna benzer:

Artık bu bus ile STM32 mikrodenetleyici üzerinde çalışmayı düşünmeye geçebiliriz. Eski serilerdeki (örneğin, STM32F407) filtre kaydı haricinde, bu modülün tüm serilerde yaklaşık olarak aynı olduğunu hemen belirtmekte fayda var, bu nedenle bir kez yazılan kod çalışmaya devam edebilir.

İlk olarak, tüm çevre birimleri için gerekli olan I2C modülünün saat ölçümünü etkinleştirmelisiniz. Alternatif fonksiyon modunda pinlerin etkinleştirilmesi ve yapılandırılması da gereklidir. Hangi veriyolunda neyin bulunduğunu görmek için Cihaza Genel Bakış bölümündeki veri sayfasına gitmeniz gerekir (F4 için bu, örneğin sayfa 18'dir) (Şekil 1). Resimden I2C'nin APB1 veri yolu üzerinde olduğunu görebilirsiniz. Bir sonraki adım GPIO'yu etkinleştirmek ve yapılandırmaktır; gereken tek şey: alternatif işlev modu (I2C veri sayfasına göre AF4'e atıfta bulunur), OpenDrain tipi ve çekmenin harici olması gerekir. 100 kHz için pinlerin "hızı" Düşük (2 MHz) seçilebilir ve 400 kHz ST için Orta veya Hızlı (10 MHz'den) seçilmesi önerilir. Son olarak I2C'yi yapılandırabilirsiniz. Kayıtları göstermenin bir anlamı yok, hepsi referans kılavuzunda, standart durum için gereken her şey aşağıda olacak. I2C modülünün kendisini açmadan önce, I2C modülünün bulunduğu veri yolu frekansının mevcut değerini CR2 kaydına yazmalısınız, bu durumda bu APB1 veri yolunun frekansıdır. Veri sayfasında bu değere PCLK adı verilir.

Farklı kontrolörlerin farklı otobüs numaraları ve adları vardır. Yani F4xx serisinde hem APB1 hem de APB2 vardır ve PCLK değişkeni buna göre numaralandırılacaktır - PCLK1 ve PCLK2. Belirli bir veri yolu saat frekansını görüntülemek veya hesaplamak için resmi ST Microelectronics web sitesinden indirilen CubeMX uygulamasını kullanabilirsiniz.

Pirinç. 1 - STM32F407 kontrol cihazının çevre şeması.

Register CR2 ayrıca bu modülden gelen kesintileri de etkinleştirir. Bunun anlamı, modülün NVIC'ye bir şey olduğunu rapor edip etmeyeceği, yoksa sadece durum kaydında gerekli işaretleri mi ayarlayacağıdır. Olay bayraklarının her zaman durum kaydına yerleştirileceğini belirtmekte fayda var ki bu da mantıklıdır. Öncelikle İTEVTEN ve ITERREN kesmeleri sırasıyla ilgi çekici, olay ve hata kesmeleridir. En geneli olarak tamamen ve yalnızca olaylarla başa çıkabilirsiniz.

I2C1->CR2 |= 48; // Çevresel frekans 24MHz I2C1->CR2 |= I2C_CR2_ITEVTEN; // Olayları etkinleştir

CCR kaydı, veri yolunun dışarı doğru saatlenmesinden sorumludur, dolayısıyla PCLK/I2C_SPEED formülü kullanılarak hesaplanan bir değeri buraya girmeniz gerekir. Örneğin, bir veri yolunu 400 kHz'de başlatmak istiyoruz, dahili veri yolu APB1 sırasıyla 48 MHz'de saatlidir, CCR'de 48 * 106 / 4 * 105 = 120'ye eşit bir değer yazacağız. Ayrıca bu kayıtta Yavaş/Hızlı çalışma modunu belirtmek gerekir, bu sonuncusu, 16. bittir.

I2C1->CCR &= ~I2C_CCR_CCR; I2C1->CCR |= 120; I2C1->CCR |= I2C_CCR_FS; // HızlıMod, 400 kHz

TRISE kaydı, SDA ve SCL'deki sinyallerin kenarlarından sorumludur; buraya küçük bir marjla bir değer girilmelidir. Rezerv olmadan mümkündür, asıl mesele daha az değildir - hiçbir şey kazanılmayacaktır. Girilen değer şu şekilde hesaplanır: TRISE = RISE/tPCLK. tPCLK = 1/PCLK. RISE sabiti sinyalin maksimum yükselme süresidir; spesifikasyona göre Yavaş Mod için 1000 ns, Hızlı mod için 300 ns'dir. tPCLK, standart olarak 1/F formülü kullanılarak elde edilen bir periyottur. Hızlı Modumuz olduğundan TRISE'deki değer şu şekilde gereklidir: 3.000*10-7/2.083*10-8 = 14.4, vb. Bir marja ihtiyaç duyulursa, yuvarlarız, yani. 15.

Bu gösterge önemlidir ancak hatalı saat frekansı kadar önemli değildir. Yavaş Mod formülünü kullanarak Hızlı Modda TRISE sabitini yanlışlıkla hesapladım ve her şey çalışıyor. Ancak lastik özelliklerine göre bunu doğru şekilde yapmak yine de daha iyidir. “i2c spesifikasyonu” arama ifadesini kullanarak bulabilirsiniz. Evet evet İngilizce.

I2C1->TRİSE = 24;

Bu adımlar tamamlandıktan sonra NVIC modülündeki modülü ve kesintileri (gerekirse) etkinleştirebilirsiniz.

I2C1->CR1 |= I2C_CR1_PE; // I2C bloğunu etkinleştir NVIC_EnableIRQ(I2C1_EV_IRQn); NVIC_SetPriority(I2C1_EV_IRQn, 1);

Modülü kurduktan sonra onunla nasıl çalışacağınıza dair iki seçenek vardır. Birinci seçenek - yoklama, yani. while döngüsünde bayrağın görünmesini bekleyeceksiniz (ve denetleyici yalnızca bununla meşgul olacak, ki bu çoğu durumda kötüdür) veya ikinci seçenek kesintiler üzerinedir. Tercih edilir olduğu için ikinci seçeneği değerlendireceğim ve kesme durumlarının mantığını anlarsanız, yoklamaya geçmek sorun olmaz.

Yani yapmanız gereken ilk şey, I2C modülünden koda bir olay kesme işleyicisi eklemek. İşlev belirli bir şekilde adlandırılmalıdır ve adı başlangıç ​​dosyasından alınabilir. I2C1 modülü için fonksiyona I2C1_EV_IRQHandler adı verilir. Bu nedenle gerekli .c dosyasına aşağıdaki işlevi ekliyoruz:

Geçersiz I2C1_EV_IRQHandler(geçersiz) ( )

Bu yöntemin ismi elbette değiştirilebilir. Başlangıç ​​dosyasında, bu işlevin varlığını gerektirmeyen etiketler montaj kodunda kolayca oluşturulur. Kaynak dosyada bir işlev bulan C dili derleyicisi, örneğin aynı I2C1_EV_IRQHandler, ona tam olarak aynı adı tahsis eder. Son montaj gerçekleştiğinde tüm bunlar bir araya getirilecek ve isim yerine kodda istenilen konuma geçiş yapılacaktır. Bu nedenle, etiketin adını istediğiniz gibi değiştirebilirsiniz (tabii ki, işlevleri adlandırma kurallarına göre), tavsiye edilmese de - diğer geliştiriciler bunun bir kesme işleyicisi olup olmadığını anlamayabilir.

C++ ile yazıyorsanız, kesme işleyicisini harici bir “C” ( ... ) bloğuna “sarmayı” unutmayın, çünkü C++ derleyicisi, derleme zamanında fonksiyon adını kendi kurallarına göre değiştirir (bununla ilgili bilgiler). Örneğin, parametreler ve dönüş değeri buraya girilir), böylece toplayıcı daha sonra yazılı işleyiciyi ve başlangıç ​​dosyasındaki etiketi ilişkilendirmeyecektir.

Bir kesme işleyicisi yazmak için doğrudan belgelere, referans kılavuzuna gidebilirsiniz, farklı çalışma modları için oldukça ayrıntılı diyagramlar vardır. İlk olarak, verileri bir bağımlı cihaza göndermeyi ele alalım:

Örneğin cihaza sadece 1 byte veri gönderip aktarımı durdurmak istiyoruz. Bunu yapmak için yalnızca EV5, EV6 ve EV8 durumlarına ihtiyacımız var. Program kodunun bir yerinde uint8_t türünde global bir veri değişkenimiz vardı; bunu bir değerle başlattık ve yardımcı cihaza aktarmak istiyoruz. İletim, zaten bildiğimiz gibi, BAŞLAT dizisiyle başlatılır:

I2C1->CR1 |= I2C_CR1_START;

Bu satır program sırasında aktarımın başlaması gereken yere yerleştirilebilir. Örneğin, veri değişkeni başlatıldıktan sonra. Daha sonra kesme işleyici fonksiyonunun içinde kod olacak. Öncelikle durum değerlerini ayrı değişkenlere kaydetmeniz gerekir:

Uçucu uint32_t sr1 = I2C1->SR1, sr2 = I2C1->SR2;

Başlatma sırasını gönderdikten sonra EV5 olayıyla bir kesinti meydana gelecektir. Bu durumda SB bitinin durum kaydında ayarlanması gerekir. Bu bit set edilirse okuma veya yazma modu bitiyle adresi göndermemiz gerekir. Basitleştirmek için şunu yapabilirsiniz:

<<1) | mode)

Artık bir EV5 durum işleyicisi yazabilirsiniz:

If(sr1 & I2C_SR1_SB) ( I2C1->DR = I2C_ADDRESS(0x14,I2C_MODE_READ); )

Adres gönderildiğinde ve yardımcı cihaz bir ACK dizisiyle yanıt verdiğinde, EV6 olayı ve aynı anda EV8 olayı meydana gelecektir: ADDR ve TXE bayrakları ayarlanacaktır. Ana modda ADDR, adresin ikincil cihaz tarafından gönderildiği ve kabul edildiği anlamına gelir ve TXE, arabelleğin sonraki iletim için veri girmekte serbest olduğu anlamına gelir. SR1 ve SR2'yi okuduğumuzda (her ikisinin de okunması gerekir) ADDR bayrağı kendini sıfırlayacak ve TXE bayrağı ayrı bir kod bloğunda işlenecektir. Yani aslında yalnızca EV5 ve EV8'in işlenmesi gerekir; EV6 yalnızca gerekli ikincil öğenin hattaki varlığı hakkında bilgi verir. Bir TXE işleyicisinde gereken tek şey veri aktarımıdır. Sadece 1 byte iletmek istediğimiz için hemen STOP dizisini göndereceğiz:

If(sr1 & I2C_SR1_TXE) ( I2C1->DR = veri; I2C1->CR1 |= I2C_CR1_STOP; )

Böylece, veri değişkenini doldurarak ve BAŞLAT sırasını oluşturma komutunu vererek, tüm işler kesintiler halinde yapılacak ve bu arada kontrolör çoğu zaman diğer yararlı işlerle meşgul olacaktır (yani, kesme işleyicisinin kendisi).

1 bayttan fazla verinin gönderilmesi gerekiyorsa koddaki değişiklikler minimum düzeydedir. Şimdi uint8_t verisi yerine aşağıdaki global değişkenleri oluşturalım:

Uint8_t yineleme; uint8_t veri = ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );

Tamsayı tipinin global değişkenleri varsayılan olarak sıfır olduğundan, iter değişkenini açıkça başlatmaya gerek yoktur. İşleyicideki değişiklikler genellikle minimum düzeydedir; EV8 olay işleme bloğunu yeniden yazmanız gerekir:

If(sr1 & I2C_SR1_TXE) ( if(iter< 10) { I2C1->DR = veri; ) else ( I2C1->CR1 |= I2C_CR1_STOP; ))

Böylece aşağıdaki işleyici fonksiyonunu elde ettik:

#define I2C_MODE_READ 1 #define I2C_MODE_WRITE 0 #define I2C_ADDRESS(addr, mod) ((addr<<1) | mode) uint8_t iter; uint8_t data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; void I2C1_EV_IRQHandler(void) { volatile uint32_t sr1 = module ->SR1, sr2 = modül ->SR2; if(sr1 & I2C_SR1_SB) ( modül ->DR = I2C_ADDRESS(0x14,I2C_MODE_READ); ) if(sr1 & I2C_SR1_TXE) ( if(iter< 10) { I2C1->DR = veri; ) else ( I2C1->CR1 |= I2C_CR1_STOP; )) ))

Kod, örneğin bir modül bağlamı oluşturularak, girdi olarak yalnızca bağlamı kabul edecek ve gerekli modülle gerekli eylemleri gerçekleştirecek bir işleyici işlevi oluşturularak vb. daha fazla değiştirilebilir. Örneğin aşağıdaki fonksiyonu yapabilirsiniz:

Void I2C_handler(I2C_TypeDef* module, uint8_t addr, uint8_t data) ( volatile uint32_t sr1 = module ->SR1, sr2 = module ->SR2; if(sr1 & I2C_SR1_SB) ( module ->DR = I2C_ADDRESS(addr,I2C_MODE_READ); ) if(sr1 & I2C_SR1_TXE) ( modül ->DR = veri; modül ->CR1 |= I2C_CR1_STOP; ) )

Bu, aynı işlevin aynı anda iki modülde kullanılmasına olanak sağlayacaktır. Örneğin şu şekilde ekleyebiliriz:

Geçersiz I2C1_EV_IRQHandler(void) ( I2C_handler(I2C1, 0x14, 0x10); ) void I2C1_EV_IRQHandler(void) ( I2C_handler(I2C1, 0x27, 0xFF); )

Dokümantasyonda gördüğümüz cihaz adresinin modülün ilettiği bayttan gelen bitler olduğunu, bit 0'ın ise mod olduğunu bir kez daha hatırlatayım. Dolayısıyla, yukarıdaki argümanlarda 0x14 adresini ve veri aktarım modunu belirttikten sonra iletim için 0x29 baytını alacağım. Makroda herhangi bir kontrol olmadığından, içine aktarabileceğiniz maksimum adresin 0x7F olduğunu unutmamalısınız, aksi takdirde bir sıçrama yaparsınız.

Diyagramdan da görülebileceği gibi, okuma modu için her şey benzerdir:

İşleme için EV5, EV6, EV7, EV7_1 durumlarına ihtiyacımız var. EV6 durumu, SR1 ve SR2 kayıtları okunduktan sonra yine de kendini sıfırlayacaktır ve EV7_1 durumu gereken son bayta karşılık gelir. Onlar. Sondan bir önceki baytı aldığımızda, bir sonraki baytın zaten son bayt olması için ACK mesajının köleye gönderilmesini devre dışı bırakmalıyız. Öyleyse önceki kodumuzu alalım ve 10 baytlık veriyi kabul etmek için buna benzer ek bir işleyici ekleyelim:

If(sr1 & I2C_SR1_RXNE) ( if(rx_iter == 8) ( I2C1->CR1 &= ~I2C_CR1_ACK; ) else if (rx_iter == 9) ( I2C1->< 10) { rx_data = I2C1->DR; ))

Bu durumda global değişkenlerin olması gerekir:

Uint8_t rx_iter; uint8_t rx_data;

Böylece I2C1 modülü için aşağıdaki kesme işleyicisini elde ettik:

#define I2C_MODE_READ 1 #define I2C_MODE_WRITE 0 #define I2C_ADDRESS(addr, mod) ((addr<<1) | mode) uint8_t iter; uint8_t data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; uint8_t rx_iter; uint8_t rx_data; uint8_t i2c_mode; void I2C1_EV_IRQHandler(void) { volatile uint32_t sr1 = I2C1->SR1, sr2 = I2C1->SR2; if(sr1 & I2C_SR1_SB) ( I2C1->DR = I2C_ADDRESS(0x14,i2c_mode); ) if(sr1 & I2C_SR1_TXE) ( if(iter< 10) { I2C1->DR = veri; ) else ( I2C1->CR1 |= I2C_CR1_STOP; ) ) if(sr1 & I2C_SR1_RXNE) ( if(rx_iter == 8) ( I2C1->CR1 &= ~I2C_CR1_ACK; ) else if (rx_iter == 9) ( I2C1-> CR1 |= I2C_CR1_ACK ) if(rx_iter< 10) { rx_data = I2C1->DR; ))

Yukarıdaki veriler genel olarak birçok durum için yeterlidir ve yukarıdaki kodu yazmanın mantığını anladıktan sonra, kodu gerektiği gibi genişletmek için belgeleri kullanabilirsiniz. Ve bir şey daha - genellikle ikincil cihazdan bir şey okumak için ona bir şeyler yazmanız gerekir.

Gönderi Görüntülemeleri: 318

Yayınlanma tarihi: 26.10.2016

Bir önceki yazımızda STM32'nin Master olarak I 2 C bus ile çalışmasına bakmıştık. Yani liderdi ve sensörü sorguladı. Şimdi STM32’yi Slave yapıp isteklere cevap verelim yani kendisi sensör olarak çalışıyor. 0'dan 0xFF'ye kadar adreslere sahip kayıtlar için 255 bayt bellek ayıracağız ve Master'ın bunları yazmasına/okumasına izin vereceğiz. Örneği bu kadar basit hale getirmek için STM32'mizi I 2 C arayüzüne sahip bir analog-dijital dönüştürücü yapalım. ADC 8 kanalı işleyecektir. Kontrolör, kayıtlardan okurken dönüşümlerin sonuçlarını Master'a verecektir. ADC dönüşümünün sonucu 12 bit olduğundan her ADC kanalı için 2 kayda (2 bayt) ihtiyacımız var.

i2c_slave.h ayarları içerir:

I2CSLAVE_ADDR– cihazımızın adresi;

ADC_ADDR_START– ADC dönüşümlerinin sonuçlarından sorumlu olan kayıtların başlangıç ​​adresi.

Dosyada i2c_slave.c biz en çok işlevlerle ilgileniyoruz get_i2c1_ram Ve set_i2c1_ram. İşlev get_i2c1_ram Kayıtlardan veri okumaktan sorumludur. Master'a verilen belirtilen adresten veri döndürür. Bizim durumumuzda veriler diziden okunur i2c1_ram ancak Master, ADC sonuçları için ayrılan aralıktaki kayıt adreslerini isterse ADC dönüşüm verileri gönderilir.

get_i2c1_ram:

Uint8_t get_i2c1_ram(uint8_t adr) ( //ADC verileri if ((ADC_ADDR_START<= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) { return ADCBuffer; } else { // Other addresses return i2c1_ram; } }

İşlev set_i2c1_ram– Master'dan alınan verileri belirtilen adresteki kayıtlara yazar. Bizim durumumuzda veriler basitçe bir diziye yazılır i2c1_ram. Ancak bu isteğe bağlıdır. Örneğin bir çek ekleyebilir ve belirli bir numara belirli bir adrese ulaştığında bazı işlemler gerçekleştirebilirsiniz. Bu sayede mikrodenetleyiciye farklı komutlar gönderebilirsiniz.

set_i2c1_ram:

Void set_i2c1_ram(uint8_t adr, uint8_t val) ( i2c1_ram = val; return; )

Başlatma oldukça basittir:

Int main(void) ( SetSysClockTo72(); ADC_DMA_init(); I2C1_Slave_init(); while(1) ( ) )

Öncelikle kontrolörün maksimum çalışma frekansını ayarlıyoruz. I 2 C veriyolunda herhangi bir gecikmeden kaçınmamız gerektiğinde maksimum hıza ihtiyaç duyulur. Daha sonra DMA kullanarak ADC işlemini başlatırız. HAKKINDA . HAKKINDA . Ve son olarak I 2 C veriyolunu şu şekilde başlatıyoruz: Köle. Gördüğünüz gibi karmaşık bir şey yok.

Şimdi STM32 modülümüzü Raspberry Pi’ye bağlayalım. Potansiyometreleri ADC kanallarına bağlayalım. Ve ADC göstergelerini kontrol cihazımızdan okuyacağız. I 2 C veri yolunun çalışması için veri yolunun her hattına çekme dirençleri takmanız gerektiğini unutmayın.

Raspberry konsolunda cihazımızın I 2 C veriyolunda görünür olup olmadığını kontrol edelim (bununla ilgili):

I2cdetect -y 1

Gördüğünüz gibi cihaz adresi 0x27 0x4E belirtmemize rağmen. Zamanınız olduğunda bunun neden olduğunu düşünün.

I 2 C-Slave cihazının kayıtlarından okumak için şu komutu yürütün:

I2cget -y 1 0x27 0x00

Nerede:
0x27– cihaz adresi,
0x00– kayıt adresi (0x00…0xFF).

I 2 C-Slave cihazının kayıtlarına yazmak için şu komutu yürütün:

I2cset -y 1 0x27 0xA0 0xDD

De:
0x27– cihaz adresi,
0xA0– adresi kaydet
0xGG-8 bit veri (0x00…0xFF)

Önceki komut kayda 0xDD sayısını yazdı 0xA0(ilk 16 kayda yazabilirsiniz ama bir anlamı yok ama onlar ADC'ye ayrılmış). Şimdi okuyalım:

I2cget -y 1 0x27 0xA0

ADC kanal verilerini okuma sürecini basitleştirmek için bir komut dosyası yazdım:

#!/usr/bin/env python içe aktarma smbus içe aktarma süresi veriyolu = smbus.SMBus(1) adres = 0x27 while (1): ADC = (); i için aralık(0, 8): LBS = bus.read_byte_data(adres, 0x00+i*2) MBS = bus.read_byte_data(adres, 0x00+i*2+1) ADC[i] = MBS*256 + LBS ADC time.sleep(0.2) yazdır

8 ADC kanalının tümünün sonuçlarını yoklar ve konsolda görüntüler.

Benzer şekilde birkaç mikrodenetleyiciyi birleştirebilirsiniz. Bunlardan biri Master(), diğeri Slave olmalıdır.

Sana başarılar diliyorum!

Makaleyi beğendin mi? Arkadaşlarınla ​​paylaş!
Bu makale yardımcı oldu mu?
Evet
HAYIR
Geri bildiriminiz için teşekkürler!
Bir şeyler ters gitti ve oyunuz sayılmadı.
Teşekkür ederim. Mesajınız gönderildi
Metinde bir hata mı buldunuz?
Seçin, tıklayın Ctrl + Enter ve her şeyi düzelteceğiz!