STM32, seriyalı I2C interfeysi. STM32, serial interfeysi I2C Stm32 interfeysi i2c təsviri davam etdi

Bəzi insanlar piroqu sevir, bəziləri isə yox.

i2c interfeysi geniş yayılmışdır və istifadə olunur. Stm32f4-də bu protokolu həyata keçirən üç modul var.
Təbii ki, ilə tam dəstək bütün bu şey.

Modulla işləmək, ümumiyyətlə, digər kontrollerlərdə olduğu kimidir: siz ona əmrlər verirsiniz, o, onları yerinə yetirir və nəticəni bildirir:
I> Biz START getdik.
S> Ok, göndərdim.
Mən> Əla, ünvanı indi göndər. Bu kimi: 0xXX.
S> Yaxşı, göndərdim. Mənə ACK dedilər. Gəlin davam edək.
Mən> Hələ sağam, yaxşıdır. Budur registr nömrəsi: 0xYY, - gedək.
S> Göndərildi, ACK aldı.
Mən> İndi ona məlumatları göndərin, bayt budur: 0xZZ.
S> Göndərildi, o, daha çox razılaşır: ACK.
Mən> Onu sikdirin, hələ yox. STOP getdilər.
S> Tamam.

Və hər şey təxminən bu ruhdadır.

IN bu nəzarətçi i2c pinləri portlar arasında belə səpələnmişdir:
PB6: I2C1_SCL
PB7: I2C1_SDA

PB8: I2C1_SCL
PB9: I2C1_SDA

PB10: I2C2_SCL
PB11: I2C2_SDA

PA8: I2C3_SCL
PC9: I2C3_SDA
Ümumiyyətlə, 59-cu səhifədəki periferik qurğuların pinoutuna baxmaq rahatdır.

Təəccüblüdür ki, i2c ilə işləmək üçün onun bütün registrlərinə ehtiyacınız var, xoşbəxtlikdən onlardan bir neçəsi var:
I2C_CR1- əmrlərin/vəziyyətlərin göndərilməsi və iş rejimlərinin seçilməsi üçün modula əmrlər;
I2C_CR2- DMA-nın qurulması və modulun işləmə tezliyinin göstərilməsi (2-42 MHz);
I2C_OAR1- cihazın ünvanının (qul üçün), ünvan ölçüsünün (7 və ya 10 bit) təyin edilməsi;
I2C_OAR2- cihazın ünvanını təyin etmək (iki ünvan varsa);
I2C_DR- məlumat reyestri;
I2C_SR1- modulun status reyestri;
I2C_SR2- status reyestri (əgər ADDR və ya STOPF bayraqları SR1-də qoyulubsa, kölə, oxunmalıdır);
I2C_CCR- interfeys sürətinin təyin edilməsi;
I2C_TRISE- kənarların vaxtlarının qurulması.

Halbuki onların yarısı “yazıb unudaraq” tiplidir.

STM32F4-Discovery lövhəsində artıq təcrübə edə biləcəyiniz I2C cihazı var: CS43L22, audio DAC. PB6/PB9 sancaqlarına qoşulur. Əsas odur ki, PD4 pininə yüksək səviyyədə tətbiq etməyi unutma (~ RESET orada oturur), əks halda DAC cavab verməyəcək.

Quraşdırma proseduru təxminən aşağıdakı kimidir:
1 . Portların və modulun özünün saatlamasına icazə verin.
PB6/PB9 sancaqlarına ehtiyacımız var, ona görə də portu aktivləşdirmək üçün RCC_AHB1ENR registrində bit 1 (GPIOBEN) təyin etməliyik.
Və I2C modulunu aktivləşdirmək üçün RCC_APB1ENR registrində bit 21 (I2C1EN) təyin edin. İkinci və üçüncü modul üçün bit nömrələri müvafiq olaraq 22 və 23-dür.
2 . Sonra sancaqlar konfiqurasiya edilir: Oped Drain çıxışı (GPIO->OTYPER), alternativ funksiya rejimi (GPIO->MODER) və alternativ funksiya nömrəsi (GPIO->AFR).
İstəyirsinizsə, lövhədə deyilsə, pull-up (GPIO->PUPDR) konfiqurasiya edə bilərsiniz (və hər iki xəttin enerji təchizatı üçün çəkiliş istənilən formada tələb olunur). I2C üçün nömrə həmişə eynidir: 4. Hər bir periferiya növü üçün ayrıca nömrənin olması xoşdur.
3 . Fpclk1 periferiyasının cari takt tezliyi (MHz ilə ifadə olunur) CR2 registrində göstərilir. Anladığım kimi, bu, müxtəlif protokol vaxtlarını hesablamaq üçün lazımdır.
Yeri gəlmişkən, normal rejim üçün ən azı iki, sürətli rejim üçün ən azı dörd olmalıdır. Və 400 kHz tam sürətə ehtiyacınız varsa, o da 10-a (10, 20, 30, 40 MHz) bölünməlidir.
Maksimum icazə verilən saat tezliyi: 42 MHz.
4 . İnterfeys sürəti CCR registrində konfiqurasiya edilir və rejim seçilir (normal/sürətli).
Mənası belədir: Tsck = CCR * 2 * Tpckl1, yəni. SCK müddəti CCR ilə mütənasibdir (sürətli rejim üçün hər şey bir az daha mürəkkəbdir, lakin RM-də təsvir edilmişdir).
5 . TRISE registrində maksimum yüksələn kənar vaxtı tənzimlənir. Standart rejim üçün bu vaxt 1 µs-dir. Reyestrdə bu vaxta uyğun gələn avtobus dövrələrinin sayını, əlavə olaraq birini yazmalısınız:
Tpclk1 dövrü 125 ns davam edərsə, onda (1000 ns / 125 ns) + 1 = 8 + 1 = 9 yazın.
6 . Kəsmə siqnallarının yaradılması (səhv, status və məlumat) isteğe bağlı olaraq aktivləşdirilir;
7 . Modul işə salınır: CR1 registrindəki PE bayrağı 1-ə təyin edilib.

Sonra modul lazım olduğu kimi işləyir. Sadəcə əmrlərin düzgün sırasını yerinə yetirmək və nəticələri yoxlamaq lazımdır. Məsələn, reyestr qeydi:
1 . Əvvəlcə CR1 registrində eyni adlı bayraq qoyaraq START göndərməlisiniz. Hər şey qaydasındadırsa, bir müddət sonra SR1 reyestrində SB bayrağı qurulacaq.
Bir məqamı qeyd etmək istərdim - əgər xəttdə çəkilmə yoxdursa (və onlar 0-dadır), onda bu bayraq ümumiyyətlə gözləməyə bilər.
2 . Əgər bayraq alınsa, o zaman ünvanı göndəririk. Yeddi bitlik ünvan üçün biz sadəcə onu DR-də xəttdə olduğu kimi yazırıq (7 ünvan biti + istiqamət biti). On bit üçün daha mürəkkəb bir alqoritm.
Əgər cihaz ünvana ACK ilə cavab verirsə, o zaman SR1 registrində ADDR bayrağı, əks halda isə AF (Uğursuzluğu təsdiqlə) bayrağı görünəcək.
ADDR görünsə, SR2 registrini oxumalısınız. Orada heç nəyə baxmaq lazım deyil, sadəcə SR1 və SR2-nin ardıcıl oxunması bu bayrağı sıfırlayır. Və bayraq təyin edilərkən, SCL master tərəfindən aşağı səviyyədə saxlanılır, bu, uzaq cihazdan məlumat göndərməzdən əvvəl gözləməsini istəməyiniz lazım olduqda faydalıdır.
Hər şey qaydasındadırsa, modul göndərilən ünvanın ən az əhəmiyyətli bitindən asılı olaraq məlumatların qəbulu və ya ötürülməsi rejiminə keçəcək. Yazmaq üçün sıfır, oxumaq üçün bir olmalıdır.
amma biz rekorda baxırıq, ona görə də orada bir sıfır olduğunu güman edəcəyik.
3 . Sonra bizi maraqlandıran reyestrin ünvanını göndəririk. Eyni şəkildə DR-də yazmaq. Ötürüldükdən sonra TXE (ötürmə buferi boşdur) və BTF (ötürmə tamamlandı) bayraqları qoyulur.
4 . Sonra cihaz ACK ilə cavab verərkən göndərilə bilən məlumatlar gəlir. Cavab NACK olarsa, bu bayraqlar qoyulmayacaq.
5 . Köçürmə başa çatdıqdan sonra (və ya gözlənilməz vəziyyət yarandıqda) STOP göndəririk: CR1 reyestrində eyni adlı bayraq qoyulur.

Oxuyanda hər şey eynidir. Dəyişikliklər yalnız qeydiyyat ünvanı yazıldıqdan sonra baş verir.
Məlumatların yazılması əvəzinə START yenidən göndərilir (yenidən başladın) və ünvan ən az əhəmiyyətli bit dəsti ilə göndərilir (oxu işarəsi).
Modul cihazdan məlumat gözləyəcək. Onu növbəti baytları göndərməyə təşviq etmək üçün qəbul etməzdən əvvəl CR1-də ACK bayrağını təyin etməlisiniz (modul qəbul etdikdən sonra eyni ACK-ı göndərəcək).
Bundan bezdiyiniz zaman bayrağı çıxarın, cihaz NACK-ı görəcək və susacaq. Bundan sonra adi qaydada STOP göndəririk və alınan məlumatlara sevinirik.

Budur kod şəklində eyni şey:
// Modulun işə salınması void i2c_Init(void) ( uint32_t Clock = 16000000UL; // Modulun takt tezliyi (system_stm32f4xx.c istifadə edilmir) uint32_t Sürət = 100000UL; // 100 kHz RCC- saatı // AH1 portu aktivləşdirilir. |= RCC_AHB1ENR_GPIOBEN; // PB6, PB9 sancaqlarını quraşdırın // Drenajı açın! GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_9; // Açılış xaricidir, ona görə də burada konfiqurasiya edilə bilməz! // lazım gələrsə, GPIOB->PUPDR reyestrinə baxın // Alternativ GPIOB funksiyasının sayı ->AFR &= ~(0x0FUL)<< (6 * 4)); // 6 очистим GPIOB->AFR |= (0x04UL<< (6 * 4)); // В 6 запишем 4 GPIOB->AFR &= ~(0x0FUL<< ((9 - 8) * 4)); // 9 очистим GPIOB->AFR |= (0x04UL<< ((9 - 8) * 4)); // В 9 запишем 4 // Режим: альтернативная функция GPIOB->MODER &= ~((0x03UL<< (6 * 2)) | (0x03UL << (9 * 2))); // 6, 9 очистим GPIOB->MODER |= ((0x02UL<< (6 * 2)) | (0x02UL << (9 * 2))); // В 6, 9 запишем 2 // Включить тактирование модуля I2C1 RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // Bu nöqtədə I2C söndürülməlidir // Hər şeyi sıfırla (SWRST == 1, sıfırla) I2C1->CR1 = I2C_CR1_SWRST; // PE == 0, bu əsas şeydir I2C1->CR1 = 0; // Güman edirik ki, biz RC-dən (16 MHz) işləyirik // Saat sistemində heç bir preskaler yoxdur (hamısı 1) // Bütün bunları dostcasına, // modulun faktiki saat tezliyindən hesablamalıyıq. I2C1->CR2 = Saat / 1000000UL; // 16 MHz // Tezliyi tənzimləyin ( // Tclk = (1 / Fperiph); // Bud = Tclk * CCR; // Tlow = Bud; // Fi2c = 1 / CCR * 2; // CCR = Fperiph / ( Fi2c * 2); uint16_t Dəyər = (uint16_t)(Saat / (Sürət * 2)); // Minimum dəyər: 4 if(Dəyər)< 4) Value = 4; I2C1->CCR = Dəyər; ) // Limit artım vaxtını təyin edin // Standart rejimdə bu vaxt 1000 ns-dir // Biz sadəcə olaraq MHz-də ifadə olunan tezlikə bir əlavə edirik (bax RM səh. 604). I2C1->TRISE = (Saat / 1000000UL) + 1; // I2C1->CR1 modulunu aktivləşdir |= (I2C_CR1_PE); // İndi bir şey edə bilərsiniz ) // Bir bayt bool göndər i2c_SendByte(uint8_t Address, uint8_t Register, uint8_t Data) ( if(!i2c_SendStart()) false qaytarın; // Chip ünvanı if(!i2c_SendAddress(Address)_) return iS2c (); // Qeydiyyat ünvanı if(!i2c_SendData(Register)) return i2c_SendStop(); // Data if(!i2c_SendData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) // Qəbul bayt bool i2c_ReceiveByte(uint8_t Address, uint8_t Register, uint8_t * Data) ( if(!i2c_SendStart()) false qaytarın; // Çip ünvanı if(!i2c_SendAddress(Address)) qaytarın i2c_SendAddress(Address)) əgər i2c_SendSendster ünvanını qaytarın(); //! i2c_SendData(Register)) qaytar i2c_SendStop(); // Yenidən başladın if(!i2c_SendStart()) false qaytarır; // Çip ünvanı (oxu) if(!i2c_SendAddress(Ünvan | 1)) qaytar i2c_SendStop(); // Qəbul et. if(!i2c_ReceiveData(Data)) return i2c_SendStop(); // Durdur! DAC işləyir (bunu bir şəkildə etmək lazımdır) // 0x00 dəyərini 0x00 ilə qeyd etmək üçün 0x94 ünvanlı cihaza bir bayt göndərin. i2c_SendByte(0x94, 0x00, 0x00); // 0x01 (ID) registrindən 0x94 ünvanlı cihazdan i2c_ReceiveByte(0x94, 0x01, &ID) dəyişən buferinə baytı qəbul edin; )
Əlbəttə ki, təlim nümunəsi istisna olmaqla, bunu edə bilməzsiniz. Fəaliyyətin tamamlanmasını gözləmək belə sürətli nəzarətçi üçün çox uzundur.

(HCS08 Ailə Mikronəzarətçiləri üçün Tərtibatçı Bələdçisi)

I2C modulunu idarə etmək üçün 6 xüsusi funksiya registrindən istifadə olunur:

  • IICC - I2C modulunun ilk nəzarət reyestri;
  • IICC2 - I2C modulunun ikinci nəzarət reyestri;
  • IICS - I2C modulunun status reyestri;
  • IICF - I2C modulunun ötürmə sürəti reyestri;
  • IICA - I2C modulunun ünvan reyestri;
  • IICD I2C modulu məlumat registridir.

QE seriyalı MCU-da 2 I2C modulu və müvafiq olaraq hər bir tipin iki nəzarət registrindən ibarətdir. Məsələn, birinci status reyestri IIC1S, ikinci status reyestri isə IIC2S-dir.

11.2.8.1. IICC nəzarət reyestri

MK seriyası AC üçün. AW, Dx, EL, GB, GT, JM, LC, QE. Q.G. SG, SH, SL
Qeydiyyatdan keçin Rejim D7 D6 D5 D4 D3 D2 D1 D0
IICC Oxumaq IICEN IICIE MST TX TXAK 0 0 0
Qeyd RSTA
Sıfırlayın 0 0 0 0 0 0 0 0
Bitlərin təsviri:
Bit adı Təsvir C dilində simvol
IICEN I2C modulunu aktivləşdirən bit:
0 — I2C nəzarətçisi deaktiv edilib;
1 - I2C nəzarətçi aktivləşdirilib.
BİICEN
IICIE I2C-dən modulun kəsilməsini aktivləşdirən bit:
0 - I2C sorğu kəsmələri deaktiv edilib;
1 - I2C sorğu kəsmələri aktivləşdirilib.
bHCIE
MST I2C nəzarətçisinin iş rejimi seçimi biti:
0 - I2C nəzarətçisi Slave rejimində işləyir;
1 - I2C nəzarətçi Master rejimində işləyir.
Bu bit 0-dan 1-ə dəyişdikdə, Başlanğıc vəziyyəti yaradılır. Əksinə, bit 1-dən 0-a dəyişdikdə Stop şərti yaranır.
bMST
TX SDA məlumat xəttində ötürmə istiqaməti seçimi biti:
0 — xətt giriş üçün işləyir;
1 - xətt çıxış üçün işləyir.
bTX
TXAK Qəbul rejimində təsdiq biti:
0 - bayt aldıqdan sonra təsdiq biti yaradılır;
1 - bayt qəbul edilməmiş bit yaradılır.
Bu bit məlumat baytını aldıqdan sonra onun qul və ya master olmasından asılı olmayaraq təsdiq bitinin yaradılmasına nəzarət edir.
bTXAK
RSTA I2C modulu master rejimində işləyirsə, bu bitə 1 yazmaq Start - “Yenidən başladın” vəziyyətinin yenidən yaranmasına səbəb olur. bRSTA

11.2.8.2. IICS Status Qeydiyyatı

MK seriyalı AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL üçün
Qeydiyyatdan keçin Rejim D7 D6 D5 D4 D3 D2 D1 D0
IICS Oxumaq TCF IAAS MƏŞĞUL ARBL 0 SRW IICIF RXAK
Qeyd
Sıfırlayın 0 0 0 0 0 0 0 0
Bitlərin təsviri:
Bit adı Təsvir C dilində simvol
TCF Mübadilə tamamlama biti. Bir bayt mübadiləsi tamamlandıqdan sonra təyin edin:
0 — mübadilə tamamlanmayıb;
1 - mübadilə tamamlandı.
IICD məlumat registrini oxuduqda (qəbul rejimində) və ya IICD məlumat registrini yazdıqda (ötürmə rejimində) TCF bayrağı 0-a qədər silinir.
bTCF
IAAS Qul ünvanı bayrağı. Cihazın qul rejimində işlədiyini və əsas mesajda ötürülən ünvanın IICA ünvan reyestrində saxlanılan qul ünvanına bərabər olduğunu təyin edin.
IICC reyestrinə yazarkən bayraq təmizlənir.
biIAAS
MƏŞĞUL Məşğul xətt bayrağı. Bu bayraq, əgər I2C modulu xəttdəki başlanğıc bitini tanıyıbsa təyin edilir. Modul xəttdə dayanma bitini aşkar etdikdə bayraq silinir.
0 — I2C avtobusu pulsuzdur;
1 - I2C avtobusu məşğuldur.
bBUSY
ARBL Arbitraj itkisi bayrağı:
0 - I2C avtobusunun işində pozuntuların olmaması;
1 – arbitrajın itirilməsi var. I2C modulu bir müddət gözləməli və sonra yenidən köçürmə əməliyyatına başlamalıdır.
BARBL
SRW Slave ötürücü istiqamət biti. Bu bit ünvan sahəsində R/W bitinin vəziyyətini göstərir:
0 - qul qəbul edir. Rəhbər quluna ötürür;
1 - qul ötürür. Rəhbər quldan alır.
bSRW
IICIF I2C modulu üçün xidmət göstərilməyən kəsmə sorğularının bayrağı. Bayraqlardan biri təyin olunarsa, 1-ə təyin edin: TCF, IAAS və ya ARBL.
0 - xidmət göstərilməyən fasilələrin olmaması;
1 - xidmət edilməmiş fasilələr var.
Bayrağa 1 yazmaqla sıfırlanır.
biIICIF
RXAK Usta etiraf biti:
0—məlumatların qəbulu təsdiqlənmişdir;
1 — qul məlumat qəbulunu qəbul etmədi.
Bu bit birjanın vaxt diaqramında ASK sahəsinin vəziyyətini əks etdirir.
bRXAK

11.2.8.3. IICA Ünvan Qeydiyyatı

Qeydiyyatdan keçin Rejim D7 D6 D5 D4 D3 D2 D1 D0
IICA Oxumaq ADDR
Qeyd
Sıfırlayın 0 0 0 0 0 0 0 0

Bu registr tərtibatçının təyin etdiyi 7 bitlik kölə ünvanını saxlayır bu cihaz sistemi inkişaf etdirərkən. Bu ünvan avtomatik olaraq I2C avtobusunda qulun ünvan sahəsində aldığı ünvan kodu ilə müqayisə edilir. Ünvanlar uyğun gələrsə, IICS status reyestrində IAAS biti təyin edilir.

11.2.8.4. IICF Baud Rate Qeydiyyatı

MK seriyalı AC, AW, Dx, EL,GB, GT, JM, LC, QE, QG, SG, SH, SL üçün
Qeydiyyatdan keçin Rejim D7 D6 D5 D4 D3 D2 D1 D0
IICF Oxumaq MULT ICR
Qeyd
Sıfırlayın 0 0 0 0 0 0 0 0
Bitlərin təsviri:

SCL_DIV və SDA_HV əmsallarının dəyərləri

ISR SCL_DIV SDA_HV ISR SCL_DIV SDA_HV
0x00 20 7 0x20 160 17
0x01 22 7 0x21 192 17
0x02 24 8 0x22 224 33
0x03 26 8 0x23 256 33
0x04 28 9 0x24 288 49
0x05 30 9 0x25 320 49
0x06 34 10 0x26 384 65
0x07 40 10 0x27 480 65
0x08 28 7 0x28 320 33
0x09 32 7 0x29 384 33
0x0A 36 9 0x2A 448 65
0x0B 40 9 0x2B 512 65
0x0C 44 11 0x2C 576 97
0x0D 48 11 0x2D 640 97
0x0E 56 13 0x2E 768 129
0x0F 68 13 0x2F 960 129
0x10 48 9 0x30 640 65
0x11 56 9 0x31 768 65
0x12 64 13 0x32 896 129
0x13 72 13 0x33 1024 129
0x14 80 17 0x34 1152 193
0x15 88 17 0x35 1280 193
0x16 104 21 0x36 1536 257
0x17 128 21 0x37 1920 257
0x18 80 9 0x38 1280 129
0x19 96 9 0x39 1536 129
0x1A 112 17 0x3A 1792 257
0x1B 128 17 0x3B 2048 257
0x1C 144 25 0x3C 2304 385
0x1D 160 25 0x3D 2560 385
0x1E 192 33 0x3E 3072 513
0x1F 240 33 0x3F 3840 513

Bu registr I2C mübadiləsinin sürətini və vaxt parametrlərini təyin edən iki bit sahəsini saxlayır. Sinxronizasiya siqnallarının tezliyi düsturla müəyyən edilir:

I2C avtobusunda SDA_hold_time məlumatların yerləşdirilməsi vaxtı SCL siqnalının 0-a təyin edildiyi andan SDA xəttindəki məlumatların dəyişməsi arasındakı vaxt intervalıdır. Veri sürəti registrinin ICR faktoru üçün cədvəldən SDA_HV (SDA_Hold_Value) parametri ilə təyin edilmişdir:

.

11.2.8.5. IICD məlumat reyestri

MK seriyalı AC, AW, Dx, EL,GB, GT, JM, LC, QE, QG, SG, SH, SL üçün
Qeydiyyatdan keçin Rejim D7 D6 D5 D4 D3 D2 D1 D0
IICD Oxumaq I2C DATA
Qeyd
Sıfırlayın 0 0 0 0 0 0 0 0

I2C modulu master rejimində işləyirsə, bu registrə yazma əməliyyatı I2C rabitəsini işə salır (lakin IICC idarəetmə registrində rabitə istiqaməti biti düzgün qurulduqda, yəni TX = 1). Proqramın verilənlər registrinə yazdığı Start vəziyyətindən sonrakı ilk bayt qullar tərəfindən cihazın ünvanı kimi şərh olunur. Buna görə də proqram birinci baytın məzmununu düzgün formalaşdırmalıdır. Qeydiyyatın oxunması əməliyyatı I2C vasitəsilə alınan son baytı qaytarır. Registr oxunması əməliyyatı həm də növbəti baytı qəbul etməyə başlayır, lakin yalnız IICC nəzarət registrində rabitə istiqaməti biti düzgün qurulduqda, yəni. TX-də = 0! TX = 1 ilə registr oxunması əməliyyatı quldan I2C vasitəsilə yeni baytın alınmasına səbəb olmayacaq.

Əgər I2C modulu qul rejimində işləyirsə, master cihaz bu quldan qəbul dövrünü yerinə yetirdikdə, bu registrdə yazılmış məlumatlar I2C avtobusunun SDA xəttinə ötürüləcək. Qeydiyyatın oxunması əməliyyatı masterdən alınan son baytı qaytarır.

11.2.8.6. Nəzarət reyestri IICC2

MK seriyası üçün AC, Dx, EL, JM, QE, SG, SH, SL
Qeydiyyatdan keçin Rejim D7 D6 D5 D4 D3 D2 D1 D0
IICC Oxumaq GCAEN ADEXT 0 0 0 AD10 AD9 AD8
Qeyd
Sıfırlayın 0 0 0 0 0 0 0 0

ARM arxitekturası üçün STM32 və mikroC kompilyatoru ilə ilk addımlar - Part 4 - I2C, pcf8574 və HD4478 əsaslı LCD bağlantısı

Növbəti məqaləni mikrokontrollerə qoşulmuş müxtəlif mikrosxemlərdə tez-tez istifadə olunan ümumi i2c interfeysi ilə işləməyə həsr etmək istərdim.

I2C iki fiziki əlaqə üzərində işləyən bir avtobusdur (ümumi naqildən əlavə). İnternetdə bu haqda çox yazılıb, Vikipediyada yaxşı məqalələr var. Bundan əlavə, avtobusun işləmə alqoritmi çox aydın şəkildə təsvir edilmişdir. Bir sözlə, avtobus iki telli sinxron avtobusdur. Avtobusda eyni vaxtda 127-yə qədər cihaz ola bilər (cihaz ünvanı 7 bitdir, biz buna sonra qayıdacayıq). Aşağıda cihazları i2c avtobusuna qoşmaq üçün tipik diaqram var, əsas cihaz kimi MK ilə.


i2c üçün bütün cihazlar (həm master, həm də qullar) açıq drenaj çıxışlarından istifadə edir. Sadəcə olaraq, onlar təkəri YALNIZ YERƏ cəlb edə bilərlər. Yüksək avtobus səviyyəsi çəkilmə rezistorları ilə təmin edilir. Bu rezistorların dəyəri adətən 4,7 ilə 10 kOhm aralığında seçilir. i2c cihazları birləşdirən fiziki xətlərə olduqca həssasdır, buna görə də böyük tutumlu bir əlaqə istifadə edilərsə (məsələn, uzun nazik və ya ekranlanmış kabel), bu tutumun təsiri siqnal kənarlarını "qarışdıra" və müdaxilə edə bilər. normal əməliyyat təkərlər. Çəkmə rezistoru nə qədər kiçik olsa, bu tutum siqnal kənarlarının xüsusiyyətlərinə bir o qədər az təsir edər, lakin i2c interfeyslərindəki çıxış tranzistorlarındakı YÜK DAHA ÇOX olar. Bu rezistorların dəyəri hər bir xüsusi tətbiq üçün seçilir, lakin onlar 2,2 kOhm-dan az olmamalıdır, əks halda avtobusla işləyən cihazlarda çıxış tranzistorlarını sadəcə yandıra bilərsiniz.

Avtobus iki xəttdən ibarətdir: SDA (məlumat xətti) və SCL (saat siqnalı). Avtobus Master cihazını saatlar, adətən bizim MK. SCL yüksək olduqda, məlumat məlumat avtobusundan oxunur. SDA vəziyyəti yalnız saat siqnalı aşağı olduqda dəyişdirilə bilər.. SCL yüksək olduqda, siqnallar yaradan zaman SDA-da siqnal dəyişir BAŞLAMAQ (SCL yüksək olduqda SDA-da siqnal yüksəkdən aşağıya dəyişir) və STOP - SCL səviyyəsi yüksək olduqda, SDA-da siqnal aşağıdan yuxarıya dəyişir).

Ayrı-ayrılıqda, i2c-də ünvanın 7 bitlik bir nömrə kimi göstərildiyini söyləmək lazımdır. 8 - ən az əhəmiyyətli bit məlumatların ötürülməsi istiqamətini göstərir 0 - qulun məlumatları ötürəcəyini, 1 - qəbul edəcəyini bildirir.. Qısaca desək, i2c ilə işləmək üçün alqoritm aşağıdakı kimidir:

  • SDA və SCL-də yüksək səviyyə- avtobus pulsuzdur, işə başlaya bilərsiniz
  • Usta qaldırır SCL 1-ə keçir və vəziyyəti dəyişir S.D.A. 1-dən 0-a qədər - onu yerə çəkir - bir siqnal yaranır BAŞLAMAQ
  • Master 7 bitlik kölə ünvanını istiqamət biti ilə ötürür (məlumatlar S.D.A. zaman sərgilənirlər SCL yerə çəkilir və buraxıldıqda qul tərəfindən oxunur). Qulun əvvəlki biti "tutmağa" vaxtı yoxdursa, o, cəlb edir SCL yerə endirərək, məlumat avtobusunun vəziyyətinin dəyişdirilməsinə ehtiyac olmadığını ustaya aydınlaşdırır: "Mən hələ də əvvəlkini oxuyuram." Usta təkəri buraxdıqdan sonra yoxlayır qul onu buraxdı?.
  • Ünvanın 8 bitini ötürdükdən sonra master 9-cu saat dövrəsini yaradır və məlumat şinini buraxır. Əgər qul onun ünvanını eşidib qəbul etsə, o zaman basacaq S.D.A. yerə. Siqnal belə formalaşır SORUŞUN- qəbul olundu, hər şey qaydasındadır. Qul heç nə başa düşmürsə və ya sadəcə orada deyilsə, təkəri basacaq heç kim olmayacaq. usta bir fasilə gözləyəcək və başa düşülmədiyini anlayacaq.
  • Ünvanı ötürdükdən sonra istiqaməti təyin etmişiksə ağadan köləyə(ünvanın 8 biti 1-ə bərabərdir), sonra master məlumatların mövcudluğunu yoxlamağı unutmadan qulluğa ötürür. SORUŞUN quldan, kölə cihazın daxil olan məlumatı emal etməsini gözləyir.
  • Usta quldan məlumat aldıqda, master özü bir siqnal yaradır SORUŞUN hər baytı aldıqdan sonra və qul onun varlığına nəzarət edir. Usta xüsusi olaraq göndərə bilməz SORUŞUNəmri göndərməzdən əvvəl STOP, adətən qul üçün daha çox məlumat təqdim etməyə ehtiyac olmadığını başa salır.
  • Əgər master tərəfindən məlumat göndərildikdən sonra (yazma rejimi), quldan məlumatları oxumaq lazımdırsa, sonra master yenidən siqnal yaradır BAŞLAMAQ , qul ünvanının oxunmuş bayrağı ilə göndərilməsi. (əgər əmrdən əvvəl BAŞLAMAQ köçürülməmişdir STOP sonra komanda qurulur YENİDƏN BAŞLAMAQ). Bu, master-slave ünsiyyətinin istiqamətini dəyişdirmək üçün istifadə olunur. Məsələn, registr ünvanını qula ötürürük və sonra ondan məlumatları oxuyuruq.)
  • Qul ilə iş başa çatdıqdan sonra usta bir siqnal yaradır STOP- saat siqnalının yüksək səviyyəsində 0-dan 1-ə məlumat avtobusunun keçidini təşkil edir.
STM 32 aparatla təchiz edilmiş i2c avtobus ötürücülərinə malikdir. MK-da 2 və ya 3 belə modul ola bilər.Onları konfiqurasiya etmək üçün istifadə olunan MK üçün istinadda təsvir olunan xüsusi registrlərdən istifadə olunur.

MicroC-də i2c-dən (həmçinin hər hansı bir periferiyadan) istifadə etməzdən əvvəl o, düzgün işə salınmalıdır. Bunu etmək üçün aşağıdakı funksiyadan istifadə edirik (Master kimi işə salma):

I2Cn_Init_Advanced(uzun imzasız: I2C_ClockSpeed, const Module_Struct *modul);

  • n- istifadə olunan modulun sayı, məsələn I2C1 və ya I2C2.
  • I2C_ClockSpeed- avtobus sürəti, 100000 (100 kbs, standart rejim) və ya 400000 (400 kbs, sürətli rejim). İkincisi 4 dəfə sürətlidir, lakin bütün cihazlar onu dəstəkləmir
  • *modul- məsələn, periferik modula göstərici &_GPIO_MODULE_I2C1_PB67, bunu burada unutmayaq Kod köməkçisi (ctrl-boşluq ) çox kömək edir.
Əvvəlcə avtobusun pulsuz olub olmadığını yoxlayaq, bunun üçün bir funksiya var I2Cn_Is_Idle(); avtobus pulsuzdursa 1, dəyişdirmə olarsa 0 qaytarır.

I2Cn_Start();
Harada n- mikrokontrollerimizin istifadə olunan i2c modulunun sayı. Avtobusda xəta olarsa funksiya 0, hər şey qaydasındadırsa 1 qaytaracaq.

Məlumatları qulluğa ötürmək üçün funksiyadan istifadə edirik:

I2Cn_Write(imzasız char qul_ünvanı, imzasız char *buf, imzasız uzun sayı, imzasız uzun END_rejimi);

  • n- istifadə olunan modulun nömrəsi
  • qul_ünvanı- 7 bitlik kölə ünvanı.
  • *buf- məlumatlarımızın göstəricisi - bayt və ya bayt massivi.
  • saymaq- ötürülən məlumat baytlarının sayı.
  • END_rejimi- məlumatları kölə ötürdükdən sonra nə etməli, END_MODE_STOP - siqnal ötürmək STOP, və ya END_MODE_RESTART yenidən göndər BAŞLAMAQ, siqnal yaratmaq YENİDƏN BAŞLAMAQ və şöbəyə onunla sessiyanın bitmədiyini və məlumatların indi ondan oxunacağını aydınlaşdırmaq.
Quldan məlumatları oxumaq üçün funksiyadan istifadə edin:

I2Cn_Read(char slave_address, char *ptrdata, unsigned long count, unsigned long END_rejim);

  • n- istifadə olunan modulun nömrəsi
  • qul_ünvanı- 7 bitlik kölə ünvanı.
  • *buf- verilənləri qəbul etdiyimiz dəyişənə və ya massiv üçün göstərici, char və ya qısa int yazın
  • saymaq- alınan məlumat baytlarının sayı.
  • END_rejimi- quldan məlumat aldıqdan sonra nə etməli - END_MODE_STOP - siqnal ötürmək STOP, və ya END_MODE_RESTART siqnal göndərin YENİDƏN BAŞLAMAQ.
Gəlin MK-ya nəyisə bağlamağa çalışaq. Başlamaq üçün: i2c avtobusu ilə idarə olunan giriş/çıxış portlarının genişləndiricisi olan geniş yayılmış PCF8574(A) mikrosxemi. Bu çip yalnız bir daxili registrdən ibarətdir ki, bu da onun fiziki I/O portudur. Yəni ona bir bayt ötürsəniz, dərhal onun nəticələrinə məruz qalacaq. Ondan bir baytı sayarsanız (Transmit BAŞLAMAQ oxu bayraqlı ünvan, siqnal YENİDƏN BAŞLAYIN, məlumatları oxuyun və nəhayət bir siqnal yaradın STOP) onda məntiqi halları öz çıxışlarında əks etdirəcək. Mikrosxemimizi məlumat cədvəlinə uyğun olaraq birləşdirək:


Mikrosxem ünvanı sancaqların vəziyyətindən formalaşır A0, A1, A2. Mikrosxem üçün PCF8574ünvan olacaq: 0100A0A1A2. (Məsələn, bizdə yüksək səviyyədə A0, A1, A2 var, ona görə də mikrosxemimizin ünvanı 0b0100 olacaq. 111 = 0x27). üçün PCF8574A - 0111A0A1A2, əlaqə diaqramımızla 0b0111 ünvanını verəcəkdir 111 = 0x3F. Əgər, deyək ki, A2 yerə bağlıdırsa, o zaman üçün ünvan PCF8574A olacaq 0x3B. Ümumilikdə 16 mikrosxem eyni vaxtda bir i2c avtobusuna, hər biri 8 PCF8574A və PCF8574-ə quraşdırıla bilər.

Gəlin nəyisə köçürməyə, i2c avtobusunu işə salmağa və PCF8574-ə nəyisə köçürməyə çalışaq.

#define PCF8574A_ADDR 0x3F //Bizim PCF8574-ün ünvanı etibarsızdır I2C_PCF8574_WriteReg(insigned char wData) ( I2C1_Start(); // START siqnalını yaradın I2C1_Write(PCF8574A_ADDRDE,&w) tərəfindən START siqnalı I2C1_Write(PCF8574A_ADDRDE END,&w). STOP yaradın siqnal ) char PCF8574A_reg ; // PCF8574-də yazdığımız dəyişən void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Start I2C delay_ms(25); // Bir az gözləyin PCF8574A_reg.b0 = 0; //8 ilk PCF8574A_reg.b0 LED-i yandırın. = 1; // (1) (delay_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // I2C_PCF857 (I2C_PCF857) LED-lərin vəziyyətini dəyişdirərkən (500); // ikinci LED-i söndürün. ; // məlumatları PCF8574-ə köçürün))
Proqramımızı tərtib edib icra edirik və görürük ki, LEDlərimiz növbə ilə yanıb-sönür.
Mən bir səbəbə görə LED-lərin katodunu PCF8574-ə bağladım. İş ondadır ki, çıxışa məntiqi 0 verildikdə, mikrosxem öz çıxışını vicdanla yerə çəkir, lakin məntiqi 1 tətbiq edildikdə, onu 100 μA cərəyan mənbəyi vasitəsilə + gücə bağlayır. Yəni, çıxışda "dürüst" məntiqi 1 ala bilməzsiniz. Və 100 µA olan bir LED yandıra bilməzsiniz. Bu, PCF8574 çıxışını əlavə registrlər olmadan girişə konfiqurasiya etmək üçün edildi. Biz sadəcə çıxış registrinə 1 yazırıq (əsasən pin vəziyyətlərini Vdd olaraq təyin edirik) və sadəcə onu yerə qısaltmaq olar. Cari mənbə I/O genişləndiricimizin çıxış mərhələsinin “yanmasına” imkan verməyəcək. Ayaq yerə dartılırsa, yer potensialı onun üzərindədir və məntiqi 0 oxunur.Ayaq +-a çəkilirsə, məntiqi 1 oxunur.Bir tərəfdən sadədir, amma digər tərəfdən bu mikrosxemlərlə işləyərkən bunu həmişə yadda saxlamalısınız.


Genişləndirici çipimizin sancaqlarının vəziyyətini oxumağa çalışaq.

#define PCF8574A_ADDR 0x3F //Bizim PCF8574-ün ünvanı etibarsızdır I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // START siqnalını yaradın I2C1_Write(PCF8574A_ADDRDE_ və data END, & tərəfindən ötürülməsi); STOP yaradın siqnal ) void I2C_PCF8574_ReadReg (imzasız char rData) ( I2C1_Start(); // START siqnalını yaradın I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // 1 bayt məlumat oxuyun və PCA_4 siqnalını yaradın. //PCF8574 char PCF8574A_out-a yazdığımız dəyişən; // oxuduğumuz dəyişən və PCF8574 char lad_state; //bizim LED yanır və ya sönür əsas etibarsız () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // I2C gecikmə_ms(25); // Bir az gözləyin PCF8574A_reg.b0 = 0; // ilk LED-i yandırın PCF857_reg.b14 1; // ikinci LED-i söndürün PCF8574A_reg.b6 = 1; // 6 və 7-ci pinləri işə salın. PCF8574-ə I2C_PCF8574_ReadReg (PCF85 74A_out ); // PCF8574-dən oxu, əgər (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0; // PCF8574A_reg.b0 olduqda; // Əgər PCF 8-dən 1 düyməsi oxunursa (F 8-dən 8-ə qədər) LED-imizi yandırın/söndürün) əgər (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // 2 düymə və 2 LED üçün oxşardır ) )
İndi düymələri basaraq LED-imizi yandırırıq və ya söndürürük. Mikrosxemin başqa çıxışı var INT. I/O genişləndiricimizin sancaqlarının vəziyyəti hər dəfə dəyişdikdə onun üzərində impuls yaranır. Onu MK-mizin xarici kəsmə girişinə qoşmaqla (Xarici fasilələri necə konfiqurasiya edəcəyinizi və onlarla necə işləməyinizi aşağıdakı məqalələrdən birində izah edəcəyəm).

Onun vasitəsilə simvol ekranını birləşdirmək üçün port genişləndiricimizdən istifadə edək. Bunların çoxu var, lakin demək olar ki, hamısı nəzarətçi çipi əsasında qurulub HD44780 və onun klonları. Məsələn, mən LCD2004 ekranından istifadə etdim.


Bunun və HD44780 nəzarətçisinin məlumat cədvəlini İnternetdə asanlıqla tapmaq olar. Displeyimizi PCF8574-ə, onunkini isə STM32-yə bağlayaq.

HD44780 paralel qapılı interfeysdən istifadə edir. Məlumat çıxışda 8 (bir saat dövründə) və ya 4 (2 saat dövründə) qapı impulsları ilə ötürülür. E. (azalan kənarda displey nəzarətçisi tərəfindən oxunur, 1-dən 0-a keçid). Nəticə R.S. displeyimizə məlumat göndərdiyimizi göstərir ( RS = 1) (göstərməli olduğu simvollar əslində ASCII kodlarıdır) və ya əmri ( RS = 0). RW məlumatların ötürülməsi, yazılması və ya oxunması istiqamətini göstərir. Adətən biz məlumatları displeyə yazırıq, buna görə də ( RW=0). Rezistor R6 ekran kontrastını idarə edir. Siz sadəcə olaraq kontrast tənzimləmə girişini yerə və ya gücə qoşa bilməzsiniz, əks halda heç nə görməyəcəksiniz.. VT1 MK əmrlərinə uyğun olaraq ekranın arxa işığını yandırmaq və söndürmək üçün istifadə olunur. MicroC-nin paralel interfeys vasitəsilə bu cür displeylərlə işləmək üçün kitabxanası var, lakin adətən ekranda 8 ayaq sərf etmək baha başa gəlir, ona görə də mən demək olar ki, həmişə belə ekranlarla işləmək üçün PCF8574-dən istifadə edirəm. (Kimsə maraqlıdırsa, mən paralel interfeys vasitəsilə MicroC-də quraşdırılmış HD44780 əsaslı displeylərlə işləmək haqqında məqalə yazacam.) Mübadilə protokolu xüsusilə mürəkkəb deyil (biz 4 məlumat xəttindən istifadə edəcəyik və məlumatı 2 saatda ötürəcəyik), aşağıdakı vaxt diaqramı ilə aydın şəkildə göstərilir:


Məlumatları displeyimizə ötürməzdən əvvəl, xidmət əmrlərini ötürməklə işə salınmalıdır. (məlumat cədvəlində təsvir edilmişdir, burada yalnız ən çox istifadə olunanları təqdim edirik)

  • 0x28- 4 xətt vasitəsilə göstərici ilə əlaqə
  • 0x0C- şəkil çıxışını aktivləşdirin, kursor ekranını söndürün
  • 0x0E- şəkil çıxışını aktivləşdirin, kursor ekranını aktivləşdirin
  • 0x01- göstəricini silin
  • 0x08- şəkil çıxışını söndürün
  • 0x06- simvol göründükdən sonra kursor 1 tanış yerlə hərəkət edir
Bu göstərici ilə kifayət qədər tez-tez işləməyimiz lazım olacağından, bir plug-in kitabxanası yaradacağıq "i2c_lcd.h" . Bu məqsədlə ildə Layihə meneceri Başlıq Faylları və seçin Yeni Fayl əlavə edin . Başlıq faylımızı yaradaq.

#define PCF8574A_ADDR 0x3F //PCF8574-ün ünvanı #define DB4 b4 // PCF8574 pinləri ilə indikator arasında uyğunluq #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN RWRS #b3defdef BL b0 // arxa işığın idarə edilməsi # displenth müəyyən et 20 // displey xəttimizdəki simvolların sayı statik işarəsiz simvol BL_status; // arxa işığın vəziyyətini saxlayan dəyişən (on/off) void lcd_I2C_Init(void); // Displey və PCF8574 inisializasiya funksiyası void lcd_I2C_txt(char *pnt); // Mətn sətrini göstərir, parametr bu sətirin göstəricisidir void lcd_I2C_int(int pnt); // Tam dəyişənin qiymətini göstərir, parametr çıxış dəyəridir void lcd_I2C_Goto(imzasız qısa sıra, işarəsiz qısa col); // kursoru göstərilən mövqeyə köçürür, parametrlər sətir - sətir (ekrandan asılı olaraq 1-dən 2 və ya 4-ə qədər) və col - (1-dən displenth)) void lcd_I2C_cls(); // Ekran boşluğunu təmizləyir lcd_I2C_backlight (imzasız qısa int vəziyyəti); // Aktivləşdirir (1 ötürüldükdə və söndürüldükdə - ekranın arxa işığı 0 ötürüldükdə)
İndi funksiyalarımızı təsvir edək, yenə gedirik Layihə meneceri qovluğun üzərinə sağ vurun Mənbələr və seçin Yeni Fayl əlavə edin . Fayl yaradın "i2c_lcd.с" .

#include "i2c_lcd.h" //başlıq faylımızı daxil edin char lcd_reg; //PCF8574-ə göndərilən məlumatların müvəqqəti saxlanması üçün qeydiyyatdan keçin void I2C_PCF8574_WriteReg(unsigned char wData) //verilənlərin i2c vasitəsilə PCF8574 çipinə göndərilməsi funksiyası ( I2C1_Start(); I2C1_Write(PCF8574A_AD)(PCF8574A_AD1, v LCD, END_TA) MAN ( char com) // displeyimizə əmr göndərmək funksiyası ( lcd_reg = 0; //müvəqqəti reyestrə 0 yazın lcd_reg.BL = BL_status.b0; // arxa işıq pinini dəyişənin dəyərinə uyğun olaraq təyin edin. arxa işıq vəziyyəti lcd_reg.DB4 = com.b4; // komandamızın 4 ən əhəmiyyətli bitini göstərici məlumat avtobusuna təyin edin lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; lcd_reg.EN = 1; //strobe çıxışını 1-ə təyin edin I2C_PCF8574_WriteReg ( lcd_reg); //məlumatları faktiki olaraq delay_us (300) indikatoruna göndərərək PCF8574 registrinə yazın; //vaxtın bitməsini gözləyin lcd_reg.EN = 0; / /strobe impulsunu 0-a sıfırlayın, göstərici məlumatı oxuyur I2C_PCF8574_WriteReg (lcd_reg); delay_us (300) ; lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.DB4 = minimum com.b04; //same. əhəmiyyətli bitlər lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; lcd_reg.EN = 1; I2C_PCF8574_WriteReg(lcd_reg); delay_us(300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg(lcd_reg); delay_us(300); ) void LCD_CHAR (imzasız char com) //indikatora verilənlərin (ASCII simvol kodu) göndərilməsi ( lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; // simvol göndərilməsi RS bitini 1 lcd_reg.DB4 = com.b4 olaraq təyin etməklə əmrlərin göndərilməsindən fərqlidir; //girişlərdə 4 ən əhəmiyyətli biti təyin edin lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; I2C_PCF8574_WriteReg (lcd_reg) ; delay_us (300); lcd_reg.EN = 0; // strobe impulsunu 0-a sıfırlayın, göstərici I2C_PCF8574_WriteReg (lcdus_reg3;_0lc; gecikmə) məlumatlarını oxuyur; .BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; lcd_reg.DB4 = com.b0; //girişlərdə ən az əhəmiyyətli olan 4 bit təyin edin lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; I2C_PCF8574_WriteReg ( lcd_reg); gecikmə_us (300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg (lcd_reg); gecikmə_us (30) (ICVCl); (302) 1_İnit_Advanced(400000, &_GPIO_MODULE_I2C1_PB67 ); //MK delay_ms(200) üçün I2c modulumuzu işə salın; lcd_Command(0x28); // Hər saat rejimində 4 bitdə göstərin gecikmə_ms (5); lcd_Command(0x08); //Delay_ms displeyinə verilənlərin çıxışını deaktiv edin (5); lcd_Command(0x01); //Delay_ms displeyini silin (5); lcd_Command(0x06); //Delay_ms (5) simvolunu göstərdikdən sonra kursorun avtomatik sürüşməsini aktivləşdirin; lcd_Command(0x0C); //Kursor delay_ms (25) göstərmədən məlumatın göstərilməsini yandırın; ) void lcd_I2C_txt(char *pnt) //Displaya simvollar sətrini çıxarın ( imzasız qısa int i; //müvəqqəti simvollar massivi indeksi dəyişən char tmp_str; //simvolların müvəqqəti massivi, ekranın uzunluğundan 1 uzunluq böyükdür. sətir, çünki siv NULL ASCII simvolu ilə dayandırılmalıdır 0x00 strncpy(tmp_str, pnt, displenth); //(i=0; i) üçün (i=0; i) üçün orijinal sətirin displenth simvollarından çoxunu kopyalayın İndi yeni yaradılmış kitabxananı əsas funksiyamız olan fayla bağlayaq:

#include "i2c_lcd.h" //başlıq faylımızı imzasız int i daxil edin; //müvəqqəti dəyişən sayğac void main() ( lcd_I2C_Init(); //displeyi işə salın lcd_I2C_backlight (1); //arxa işığı yandır lcd_I2C_txt ("Salam habrahabr"); //(1) ( delay_ms() 1000) ; lcd_I2C_Goto (2,1); //2-ci sətirin 1 simvoluna keçin lcd_i2c_int (i); //i++ dəyərini göstərin; //sayğacı artırın ) )

Hər şey düzgün yığılıbsa, onda indikatorda mətn və hər saniyə artan sayğac görməliyik. Ümumiyyətlə, mürəkkəb bir şey yoxdur :)

Növbəti məqalədə i2c protokolunu və onunla işləyən cihazları başa düşməyə davam edəcəyik. EEPROM 24XX yaddaşı və MPU6050 akselerometr/giroskop ilə işləməyi nəzərdən keçirək.

Bu gün əməliyyat masamızda yeni qonaq var, bu Microchip məhsuludur, MCP23008-E port genişləndiricisidir. Bu şey (adından göründüyü kimi) mikrokontrolörün giriş/çıxış ayaqlarının birdən-birə qeyri-kafi olarsa, onların sayını artırmaq üçün nəzərdə tutulub. Təbii ki, ayaqlara və çıxışlara ehtiyacımız varsa, o zaman götürə bilərik və bundan narahat olmayaq. Əgər giriş ayaqlarına ehtiyacınız varsa, o zaman ciddi məntiqə əsaslanan bir həll var. Əgər bizə həm girişlər, həm də çıxışlar və həmçinin girişlər üçün idarə olunan pull-up lazımdırsa, port genişləndiricisi bəlkə də ən normal həll yoludur. Cihazın qiymətinə gəldikdə, o, çox təvazökardır - təxminən bir dollar. Bu yazıda AVR mikrokontrollerindən istifadə edərək bu mikrosxemi necə idarə etməyi ətraflı təsvir etməyə çalışacağam.

Birincisi, xüsusiyyətləri haqqında bir az:

  • 8 müstəqil port pinləri
  • Xarici dünya ilə əlaqə üçün interfeys - I2C (tezlik 1,7 MHz-ə qədər)
  • Girişlər üçün fərdiləşdirilə bilən pull-up
  • Müəyyən girişlərin vəziyyəti dəyişdikdə ayağını sıxa bilər
  • Mikrosxemin ünvanını təyin etmək üçün üç giriş (bir avtobusda 8 cihazı asmaq olar)
  • İş gərginliyi 1,8 ilə 5,5 volt arasındadır
  • Aşağı cərəyan istehlakı
  • Geniş qapaq seçimi (PDIP/SOIC/SSOP/QFN)

I2C interfeysindən istifadə etməyi üstün tuturam, o, məni az sayda naqillə cəlb edir :-) lakin çox sürətli sürətə (10 MHz-ə qədər) ehtiyacınız varsa, MCP23S08-də mövcud olan SPI interfeysindən istifadə etməlisiniz. MCP23S08 və MCP23008 arasındakı fərq, başa düşdüyüm kimi, yalnız interfeysdə və çipin ünvanını təyin etmək üçün ayaqların sayındadır. Kim daha qısa bir şeyi xoşlayır? Mikroruhinin pinoutu məlumat cədvəlindədir, heç bir şəkildə maraqlı deyil və burada nəzərə alınmayacaq. Buna görə də, gəlin dərhal bu cihazla necə işləməyə başlayaq. Bütün işlər mikrosxemin registrlərindən müəyyən məlumatları yazmaq və oxumaqdan ibarətdir. Sevindirici haldır ki, heç bir registr yox idi - cəmi on bir. Registrlərdən məlumatların yazılması və oxunması çox sadədir, bu barədə yazın. Düzdür, biz registrlərdən danışmırıq, amma prinsip eynidir. İndi yalnız hansı registrlərdən nəyi oxumağı və hansı registrdən nəyi yazmağı anlamaq qalır. Əlbəttə ki, məlumat cədvəli bu işdə bizə kömək edəcəkdir. Məlumat vərəqindən öyrənirik ki, mikrosxem aşağıdakı registrlərə malikdir:

IODIR qeydiyyatı
Verilənlərin axdığı istiqaməti müəyyənləşdirir. Müəyyən bir ayağa uyğun olan bit birinə təyin edilirsə, ayaq bir girişdir. Sıfıra dönərsə, çıxın. Bir sözlə, bu registr AVR-də DDRx-in analoqudur (yalnız AVR-də 1 çıxış, 0 isə girişdir).

IPOL reyestri
Əgər registr biti quraşdırılıbsa, müvafiq ayaq üçün giriş inversiyası aktivləşdirilir. Bu o deməkdir ki, bacağa bir log tətbiq etsəniz. sıfır sonra GPIO registrindən bir və əksinə hesab olunur. Bu xüsusiyyətin faydalılığı çox şübhəlidir.

GPINTEN qeydiyyatı
Bu registrin hər biti xüsusi port pininə uyğundur. Bit qurulubsa, giriş kimi konfiqurasiya edilmiş müvafiq port pin kəsməyə səbəb ola bilər. Bit sıfırlanırsa, o zaman port ayağı ilə nə etsəniz də, heç bir fasilə olmayacaqdır. Kesinti şərtləri aşağıdakı iki registr tərəfindən təyin edilir.

DEFVAL qeydiyyatı
Giriş üçün konfiqurasiya edilmiş pinlərin cari dəyəri daim bu registrlə müqayisə edilir. Birdən cari dəyər bu registrdə olandan fərqlənməyə başlayırsa, kəsmə baş verir. Sadə dillə desək, bit qurulubsa, müvafiq ayaqda səviyyə yüksəkdən aşağıya dəyişdikdə kəsmə baş verəcək. Bit sıfırlanırsa, kəsilmə yüksələn kənarda baş verir.

INTCON qeydiyyatı
Bu registrin hər biti xüsusi port pininə uyğundur. Bit aydındırsa, məntiqi səviyyənin əksinə olan hər hansı bir dəyişiklik fasiləyə səbəb olur. Bit qoyulubsa, kəsilmənin baş verməsi DEFVAL registrindən təsirlənir (əks halda ona tamamilə məhəl qoyulmur).

IOCON qeydiyyatı
Bu parametrlər reyestridir. Dörd bitdən ibarətdir:
SEQOP— bit nəzarət ünvan avtomatik artım. Quraşdırılıbsa, avtomatik artım deaktiv edilir, əks halda aktivləşdirilir. Onu söndürsək, ünvanını hər dəfə ötürmək məcburiyyətində qalmadığımız üçün eyni registrin dəyərini çox tez oxuya bilərik. Bütün 11 registrləri növbə ilə tez oxumaq lazımdırsa, avtomatik artım aktivləşdirilməlidir. Hər dəfə registrdən bayt oxunduqda ünvan özü bir artacaq və ötürülməsinə ehtiyac qalmayacaq.
DISSLW- kim bilir bu nədi. Nə qədər büksəm də, yenə də hər şey işləyir. Kimsə izah etsə şad olaram.
HAEN— INT çıxış ayar biti. Quraşdırılıbsa, pin açıq drenaj kimi konfiqurasiya edilir, bit sıfırlanırsa, INT ayağındakı aktiv səviyyə INTPOL bitini təyin edir.
INTPOL— INT ayağında aktiv səviyyəni təyin edir. Əgər təyin edilibsə, aktiv səviyyə birdir, əks halda sıfırdır.

Hələ bir az var HAEN lakin bu çipdə istifadə edilmir (MCP23S08-də aparat ünvanlama pinlərini yandırır/söndürür)

GPPU reyestri
Girişin çəkilməsini idarə edir. Bit qurulubsa, müvafiq pində 100 kOhm rezistor vasitəsilə enerji təchizatı üçün bir çəkiliş görünəcək.

INTF reyestri
Bayraq reyestrini kəsin. Bit qurulubsa, bu, müvafiq port ayağının kəsilməyə səbəb olması deməkdir. Əlbəttə ki, tələb olunan ayaqlar üçün fasilələr GPINTEN reyestrində aktivləşdirilməlidir

INTCAP reyestri
Kesinti baş verdikdə, bütün port bu registrdə oxunur. Bundan sonra daha çox fasilə olarsa, biz onu və ya GPIO-nu oxuyana qədər bu reyestrin məzmunu yeni dəyərlə üzərinə yazılmayacaq.

GPIO qeydiyyatı
Reyestrdən məlumatları oxuyarkən biz portun pinlərindəki məntiqi səviyyələri oxuyuruq. Məlumatları qeyd edərkən məntiqi səviyyələri təyin edirik. Bu registrə yazarkən eyni məlumatlar avtomatik olaraq OLAT registrinə yazılır

OLAT reyestri
Məlumatları bu registrə yazmaqla biz məlumatları porta çıxarırıq. Oradan məlumatları oxusanız, əslində port girişlərində olanları deyil, yazılanları oxuyacaqsınız. Girişləri oxumaq üçün yalnız GPIO-dan istifadə edirik.

Reyestrlərin təsviri, məncə, olduqca normaldır və heç kimin ağlını başından almamalıdır. İndi sadəcə əylənmək üçün port genişləndiricisinə qoşulmuş 4 düyməni sorğulayacaq və vəziyyətindən asılı olaraq eyni genişləndiriciyə qoşulmuş 4 uyğun LED-i yandıracaq və ya söndürəcək kiçik bir demo yazaq. Nümunə heç bir fasilə olmadan mümkün qədər sadə olacaq. Biz sadəcə olaraq düymələrin vəziyyətini daim oxuyuruq və bu vəziyyəti LED-lərdə göstəririk. Cihazımızın diaqramı belə görünəcək:

Kəsmə pininin qoşulması lazım deyil, bura ehtiyacımız yoxdur, lakin sıfırlama ayağı üçün çəkmə tələb olunur! Liman genişləndiricimin bərkidilməməsi səbəbindən vaxtaşırı sıfırlandığını başa düşənə qədər çox vaxt sərf etdim. İndi keçək koda. Təbii ki, yazacağıq. Sadə kod:

Rashiritel proqramı; const AdrR=%01000001; //Oxunma biti ilə çipin ünvanı const AdrW=%01000000; //Bit qeydi olan çipin ünvanı var r:byte; ///Prosedur Dat dəyişənindən verilənləri Adr ünvanında reyestrə yazır Prosedur WriteReg(Dat,Adr:bayt); Başlayın TWI_Start(); TWI_Write(AdrW); TWI_Write(Adr); TWI_Write(Dat); TWI_Stop(); son; ///Funksiya Adr Function ReadReg(Adr:bayt):bayt ünvanında registr dəyərini qaytarır; var a:byte; Başlayın TWI_Start(); TWI_Write(AdrW); TWI_Write(Adr); TWI_Start(); TWI_Write(AdrR); a:=TWI_Read(0); TWI_Stop(); nəticə:=a; son; başlamaq TWI_INIT(200000); ///i2c WriteReg işə salınır (%00001111,0x00); //Ən aşağı 4 bit giriş, qalan 4 bit isə çıxışdır WriteReg(%00001111,0x06); //Açıq 4 giriş üçün pull-up TRUE başladıqda r:=ReadReg(0x09); //Girişlərin vəziyyətini oxuyun r:= DEYİL r; //Bitləri tərsinə çevirmək lazımdır, əks halda düyməni buraxdıqda LED-lər yanır r:= r shl 4; //4 bit sola sürüşdürün... WriteReg(r,0x0A); //Düymələrin son vəziyyətini göstərin; son.

26/10/2016 tarixində dərc olunub

Əvvəlki məqalədə biz Master olaraq STM32-nin I 2 C avtobusu ilə işləməsinə baxdıq. Yəni o, lider olub və sensoru sorğu-sual edib. İndi STM32-ni Slave edək və sorğulara cavab verək, yəni özü sensor kimi işləyir. Biz 0-dan 0xFF-ə qədər ünvanlı registrlər üçün 255 bayt yaddaş ayıracağıq və Master-a onları yazmağa/oxumağa icazə verəcəyik. Nümunənin o qədər də sadə olmaması üçün gəlin STM32-ni I 2 C interfeysli analoqdan rəqəmə çeviriciyə çevirək.ADC 8 kanalı emal edəcək. Nəzarətçi registrlərdən oxuyarkən çevrilmələrin nəticələrini Mastera verəcəkdir. ADC-nin çevrilməsinin nəticəsi 12 bit olduğundan, hər bir ADC kanalı üçün 2 registr (2 bayt) lazımdır.

i2c_slave.h parametrləri ehtiva edir:

I2CLAVE_ADDR– cihazımızın ünvanı;

ADC_ADDR_START– ADC çevrilmələrinin nəticələrinə cavabdeh olan registrlərin başlanğıc ünvanı.

Faylda i2c_slave.c Bizi daha çox funksiyalar maraqlandırır get_i2c1_ramset_i2c1_ram. Funksiya get_i2c1_ram registrlərdən məlumatların oxunmasına cavabdehdir. O, Master-a verilən göstərilən ünvandan məlumatları qaytarır. Bizim vəziyyətimizdə məlumatlar massivdən oxunur i2c1_ram, lakin Master ADC nəticələri üçün ayrılmış diapazondan registr ünvanlarını tələb edərsə, ADC çevrilmə məlumatları göndərilir.

get_i2c1_ram:

Uint8_t get_i2c1_ram(uint8_t adr) ( //ADC data, əgər ((ADC_ADDR_START)<= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) { return ADCBuffer; } else { // Other addresses return i2c1_ram; } }

Funksiya set_i2c1_ram– Masterdən alınan məlumatları göstərilən ünvanla registrlərə yazır. Bizim vəziyyətimizdə verilənlər sadəcə olaraq massivə yazılır i2c1_ram. Amma bu isteğe bağlıdır. Siz, məsələn, çek əlavə edə və müəyyən bir nömrə müəyyən ünvana çatdıqda bəzi hərəkətləri edə bilərsiniz. Bu yolla siz mikrokontrollerə müxtəlif əmrlər göndərə bilərsiniz.

set_i2c1_ram:

Ləğv et set_i2c1_ram(uint8_t adr, uint8_t val) ( i2c1_ram = val; qayıt; )

Başlamaq olduqca sadədir:

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

Əvvəlcə nəzarətçinin maksimum işləmə tezliyini təyin etdik. I 2 C avtobusunda hər hansı gecikmələrin qarşısını almaq lazım olduqda maksimum sürət tələb olunur.Sonra biz DMA-dan istifadə edərək ADC əməliyyatına başlayırıq. HAQQINDA. HAQQINDA. Və nəhayət, I 2 C avtobusunu işə salırıq Qul. Gördüyünüz kimi, mürəkkəb bir şey yoxdur.

İndi STM32 modulumuzu Raspberry Pi ilə birləşdirək. Potensiometrləri ADC kanallarına birləşdirək. Və biz nəzarətçimizdən ADC göstəricilərini oxuyacağıq. Unutmayın ki, I 2 C avtobusunun işləməsi üçün avtobusun hər bir xəttində açılan rezistorlar quraşdırmaq lazımdır.

Raspberry konsolunda cihazımızın I 2 C avtobusunda görünüb-görünmədiyini yoxlayaq (bu barədə):

I2cdetect -y 1

Gördüyünüz kimi, cihazın ünvanı 0x27, baxmayaraq ki, biz 0x4E təyin etdik. Vaxtınız olanda bunun niyə baş verdiyini düşünün.

I 2 C-Slave cihazının registrlərindən oxumaq üçün əmri yerinə yetirin:

I2cget -y 1 0x27 0x00

Harada:
0x27- cihazın ünvanı,
0x00– qeydiyyat ünvanı (0x00…0xFF).

I 2 C-Slave cihazının registrlərinə yazmaq üçün əmri yerinə yetirin:

I2cset -y 1 0x27 0xA0 0xDD

De:
0x27- cihazın ünvanı,
0xA0- qeydiyyat ünvanı
0xDD-8 bit məlumat (0x00…0xFF)

Əvvəlki komanda reyestrə 0xDD rəqəmini yazdı 0xA0(ilk 16 registrə yaza bilərsiniz, amma mənası yoxdur, lakin onlar ADC üçün qorunur). İndi oxuyaq:

I2cget -y 1 0x27 0xA0

ADC kanal məlumatlarının oxunması prosesini asanlaşdırmaq üçün bir skript yazdım:

#!/usr/bin/env python idxal smbus idxal vaxtı avtobus = smbus.SMBus(1) ünvanı = 0x27 isə (1): ADC = (); diapazondakı i üçün(0, 8): LBS = bus.read_byte_data(ünvan, 0x00+i*2) MBS = bus.read_byte_data(ünvan, 0x00+i*2+1) ADC[i] = MBS*256 + LBS çap ADC time.sleep(0.2)

Bütün 8 ADC kanalının nəticələrini sorğulayır və konsola göstərir.

Bənzər bir şəkildə, bir neçə mikro nəzarətçi birləşdirə bilərsiniz. Onlardan biri Master(), digəri Slave olmalıdır.

Sənə uğurlar arzu edirəm!




Üst