STM32, serijski vmesnik I2C. STM32, serijski vmesnik I2C Nadaljevanje opisa vmesnika Stm32 i2c

Nekateri imajo radi pite, nekateri ne.

Vmesnik i2c je zelo razširjen in uporabljan. V stm32f4 so kar trije moduli, ki implementirajo ta protokol.
Seveda, s popolna podpora ta cela stvar.

Delo z modulom je na splošno enako kot pri drugih krmilnikih: vi mu daste ukaze, on jih izvede in sporoči rezultat:
I> Šli smo na START.
S> Ok, poslal sem.
Jaz> Kul, zdaj pošlji naslov. Takole: 0xXX.
S> V redu, poslal sem. Povedali so mi, da ACK. Gremo naprej.
I> Še vedno živ, dobro. Tukaj je registrska številka: 0xYY, - gremo.
S> Poslano, prejeto ACK.
I> Zdaj mu pošljite podatke, tukaj je bajt: 0xZZ.
S> Poslano, se strinja z več: ACK.
I> Jebi ga, ne še. Šli so STOP.
S> V redu.

In vse je približno v tem duhu.

IN ta krmilnik Zatiči i2c so razpršeni po vratih takole:
PB6: I2C1_SCL
PB7: I2C1_SDA

PB8: I2C1_SCL
PB9: I2C1_SDA

PB10: I2C2_SCL
PB11: I2C2_SDA

PA8: I2C3_SCL
PC9: I2C3_SDA
Na splošno je priročno pogledati pinout zunanjih naprav na strani 59.

Presenetljivo je, da za delo z i2c potrebujete vse njegove registre, na srečo jih je malo:
I2C_CR1- ukazi modulu za pošiljanje ukazov/stanj in izbiro načinov delovanja;
I2C_CR2- nastavitev DMA in prikaz delovne frekvence modula (2-42 MHz);
I2C_OAR1- nastavitev naslova naprave (za podrejeno), velikosti naslova (7 ali 10 bitov);
I2C_OAR2- nastavitev naslova naprave (če sta naslova dva);
I2C_DR- register podatkov;
I2C_SR1- register stanja modulov;
I2C_SR2- statusni register (podrejeni, mora se prebrati, če sta v SR1 nastavljeni zastavici ADDR ali STOPF);
I2C_CCR- nastavitev hitrosti vmesnika;
I2C_TRISE- nastavitev časov robov.

Vendar jih je polovica tipa "zapiši in pozabi".

Plošča STM32F4-Discovery že ima napravo I2C, s katero lahko vadite: CS43L22, audio DAC. Priključen je na pine PB6/PB9. Glavna stvar je, da ne pozabite uporabiti visoke ravni za pin PD4 (~RESET je tam), sicer se DAC ne bo odzval.

Postopek namestitve je približno naslednji:
1 . Dovolite taktiranje vrat in samega modula.
Potrebujemo nožice PB6/PB9, zato moramo nastaviti bit 1 (GPIOBEN) v registru RCC_AHB1ENR, da omogočimo vrata.
In nastavite bit 21 (I2C1EN) v registru RCC_APB1ENR, da omogočite modul I2C. Za drugi in tretji modul sta bitna števila 22 oziroma 23.
2 . Nato se konfigurirajo zatiči: izhod Oped Drain (GPIO->OTYPER), način alternativne funkcije (GPIO->MODER) in številka alternativne funkcije (GPIO->AFR).
Če želite, lahko konfigurirate pull-up (GPIO->PUPDR), če ni na plošči (in je potreben pull-up na napajanje obeh linij v kateri koli obliki). Številka za I2C je vedno enaka: 4. Lepo je, da obstaja ločena številka za vsako vrsto zunanje opreme.
3 . Trenutna urna frekvenca periferne enote Fpclk1 (izražena v MHz) je navedena v registru CR2. Kolikor razumem, je to potrebno za izračun različnih časov protokolov.
Mimogrede, morata biti vsaj dva za običajni način in vsaj štiri za hitri način. In če potrebujete polno hitrost 400 kHz, jo je treba deliti tudi z 10 (10, 20, 30, 40 MHz).
Najvišja dovoljena taktna frekvenca: 42 MHz.
4 . Hitrost vmesnika je konfigurirana v registru CCR in izbran je način (normalno/hitro).
Pomen je: Tsck = CCR * 2 * Tpckl1, tj. obdobje SCK je sorazmerno s CCR (za hitri način je vse malo bolj zapleteno, vendar je opisano v RM).
5 . Največji čas naraščajočega roba v registru TRISE je prilagojen. Za standardni način je ta čas 1 µs. V register morate zapisati število avtobusnih ciklov, ki se prilegajo temu času, plus ena:
če cikel Tpclk1 traja 125 ns, potem zapišite (1000 ns / 125 ns) + 1 = 8 + 1 = 9.
6 . Opcijsko je omogočeno generiranje prekinitvenih signalov (napaka, stanje in podatki);
7 . Modul se vklopi: zastavica PE v registru CR1 je nastavljena na 1.

Potem modul deluje kot mora. Samo implementirati morate pravilen vrstni red ukazov in preveriti rezultate. Na primer vnos v register:
1 . Najprej morate poslati START z nastavitvijo zastavice z istim imenom v registru CR1. Če je vse v redu, se čez nekaj časa v registru SR1 nastavi zastavica SB.
Rad bi opozoril na eno točko - če na črti ni dviga (in so na 0), potem ta zastavica morda sploh ne bo čakala.
2 . Če je zastavica prejeta, pošljemo naslov. Pri sedembitnem naslovu ga preprosto zapišemo v DR točno tako, kot bo na vrstici (7 naslovnih bitov + smerni bit). Za deset bitov je bolj zapleten algoritem.
Če naprava odgovori na naslov z ACK, se v registru SR1 prikaže zastavica ADDR, v nasprotnem primeru se prikaže zastavica AF (Acknowledge failure).
Če se prikaže ADDR, morate prebrati register SR2. Tam vam ni treba ničesar pogledati, samo zaporedno branje SR1 in SR2 ponastavi to zastavico. In medtem ko je zastavica nastavljena, glavni SCL drži nizko, kar je uporabno, če morate od oddaljene naprave zahtevati, da počaka, preden pošlje podatke.
Če je vse v redu, potem modul preklopi v način sprejemanja ali oddajanja podatkov, odvisno od najmanj pomembnega bita poslanega naslova. Za pisanje mora biti nič, za branje pa ena.
vendar gledamo zapis, zato bomo predvidevali, da je bila tam ničla.
3 . Nato pošljemo naslov registra, ki nas zanima. Na enak način zapisovanje v DR. Po prenosu se nastavita zastavici TXE (medpomnilnik prenosa je prazen) in BTF (prenos končan).
4 . Sledijo podatki, ki jih je mogoče poslati, medtem ko se naprava odzove z ACK. Če je odgovor NACK, te zastavice ne bodo nastavljene.
5 . Po končanem prenosu (ali v primeru nepričakovanega stanja) pošljemo STOP: v registru CR1 se nastavi istoimenska zastavica.

Pri branju je vse enako. Spremembe šele po vpisu naslova registra.
Namesto zapisovanja podatkov se znova pošlje START (ponovni zagon) in naslov se pošlje z nastavljenim najmanj pomembnim bitom (predznak branja).
Modul bo čakal na podatke iz naprave. Da ga spodbudite k pošiljanju naslednjih bajtov, morate pred sprejemom v CR1 nastaviti zastavico ACK (tako da bo modul po prejemu poslal ta isti ACK).
Ko se tega naveličate, odstranite zastavico, naprava bo videla NACK in utihnila. Nato na običajen način pošljemo STOP in se razveselimo prejetih podatkov.

Tukaj je ista stvar v obliki kode:
// Inicializirajte modul void i2c_Init(void) ( uint32_t Clock = 16000000UL; // Frekvenca modula (system_stm32f4xx.c se ne uporablja) uint32_t Speed ​​​​= 100000UL; // 100 kHz // Omogoči taktiranje vrat GPIOB RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; // Nastavite zatiče PB6, PB9 // Odpri odtok! GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_9; // Vlečenje je zunanje, zato ga tukaj ni mogoče konfigurirati! // če je potrebno, glej register GPIOB->PUPDR // Številka alternativne funkcije GPIOB ->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; // Na tej točki mora biti I2C izklopljen // Ponastavi vse (SWRST == 1, ponastavi) I2C1->CR1 = I2C_CR1_SWRST; // PE == 0, to je glavna stvar I2C1->CR1 = 0; // Predvidevamo, da delujemo iz RC (16 MHz) // V taktnem sistemu ni preddelilnikov (vsi 1) // Na prijateljski način bi morali vse to izračunati iz // dejanske taktne frekvence modula I2C1->CR2 = Ura / 1000000UL; // 16 MHz // Prilagodite frekvenco ( // Tclk = (1 / Fperiph); // Stegno = Tclk * CCR; // Tlow = Stegno; // Fi2c = 1 / CCR * 2; // CCR = Fperiph / ( Fi2c * 2); uint16_t Vrednost = (uint16_t)(Ura / (Hitrost * 2)); // Najmanjša vrednost: 4 if(Vrednost< 4) Value = 4; I2C1->CCR = vrednost; ) // Nastavite mejni čas vzpona // V standardnem načinu je ta čas 1000 ns // Enostavno prištejemo frekvenci, izraženi v MHz (glejte RM str. 604). I2C1->TRISE = (ura / 1000000UL) + 1; // Omogoči modul I2C1->CR1 |= (I2C_CR1_PE); // Zdaj lahko naredite nekaj ) // Pošljite bajt bool i2c_SendByte(uint8_t Address, uint8_t Register, uint8_t Data) ( if(!i2c_SendStart()) return false; // Naslov čipa if(!i2c_SendAddress(Address)) return i2c_SendStop (); // Naslov registracije if(!i2c_SendData(Register)) return i2c_SendStop(); // Podatki if(!i2c_SendData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) // Receive byte bool i2c_ReceiveByte(uint8_t Address, uint8_t Register, uint8_t * Data) ( if(!i2c_SendStart()) return false; // Naslov čipa if(!i2c_SendAddress(Address)) return i2c_SendStop(); // Register naslov if(! i2c_SendData(Register)) vrni i2c_SendStop(); // Znova zaženi if(!i2c_SendStart()) vrni false; // Naslov čipa (branje) if(!i2c_SendAddress(Address | 1)) vrni i2c_SendStop(); // Prejmi bajt if(!i2c_ReceiveData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) Uporaba: ( uint8_t ID = 0; i2c_Init(); // Predvidevamo, da je PD4 nastavljen na visoko raven in DAC deluje (to je treba nekako narediti) // Pošlji bajt napravi z naslovom 0x94, da registrira 0x00 z vrednostjo 0x00. i2c_SendByte(0x94, 0x00, 0x00); // Sprejmi bajt od naprave z naslovom 0x94 iz registra 0x01 (ID) v spremenljivko medpomnilnika i2c_ReceiveByte(0x94, 0x01, &ID); )
Seveda tega ne morete storiti, razen v primeru usposabljanja. Čakanje na dokončanje akcije je predolgo za tako hiter krmilnik.

(Vodnik za razvijalce mikrokrmilnikov družine HCS08)

Za krmiljenje modula I2C se uporablja 6 posebnih funkcijskih registrov:

  • IICC - prvi kontrolni register modula I2C;
  • IICC2 - drugi kontrolni register modula I2C;
  • IICS - register statusa modula I2C;
  • IICF - register hitrosti prenosa modula I2C;
  • IICA - register naslovov modula I2C;
  • IICD je podatkovni register modula I2C.

MCU serije QE vsebuje 2 modula I2C in v skladu s tem dva krmilna registra vsake vrste. Na primer, prvi statusni register je IIC1S, drugi statusni register pa IIC2S.

11.2.8.1. Nadzorni register IICC

Za serijo MK AC. AW, Dx, EL, GB, GT, JM, LC, QE. Q.G. SG, SH, SL
Registrirajte se Način D7 D6 D5 D4 D3 D2 D1 D0
IICC Branje IICEN IICIE MST TX TXAK 0 0 0
Zapis RSTA
Ponastaviti 0 0 0 0 0 0 0 0
Opis bitov:
Bitno ime Opis Simbol v jeziku C
IICEN Bit za omogočanje modula I2C:
0 — krmilnik I2C je onemogočen;
1 - krmilnik I2C je omogočen.
bIICEN
IICIE Bit za omogočanje prekinitve modula iz I2C:
0 - prekinitve zahtev I2C so onemogočene;
1 - prekinitve zahtev I2C so omogočene.
bHCIE
MST Bit za izbiro načina delovanja krmilnika I2C:
0 - krmilnik I2C deluje v podrejenem načinu;
1 - krmilnik I2C deluje v glavnem načinu.
Ko se ta bit spremeni iz 0 v 1, se ustvari stanje zagona. Nasprotno, ko se bit spremeni iz 1 v 0, se ustvari pogoj za zaustavitev.
bMST
TX Bit za izbiro smeri prenosa na podatkovni liniji SDA:
0 — vrstica deluje za vnos;
1 - vrstica deluje za izhod.
bTX
TXAK Potrditveni bit v načinu sprejema:
0 - po prejemu bajta se generira potrditveni bit;
1 - generiran je nepotrjen bit bajta.
Ta bit nadzoruje generiranje potrditvenega bita po prejemu podatkovnega bajta, ne glede na to, ali je podrejeni ali nadrejeni.
bTXAK
RSTA Če modul I2C deluje v glavnem načinu, potem pisanje 1 v ta bit povzroči ponovno generiranje stanja Začetek - »Ponovni zagon«. bRSTA

11.2.8.2. Statusni register IICS

Za serije MK AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL
Registrirajte se Način D7 D6 D5 D4 D3 D2 D1 D0
IICS Branje TCF IAAS ZASEDEN ARBL 0 SRW IICIF RXAK
Zapis
Ponastaviti 0 0 0 0 0 0 0 0
Opis bitov:
Bitno ime Opis Simbol v jeziku C
TCF Bit za dokončanje izmenjave. Nastavi po končani izmenjavi enega bajta:
0 — izmenjava ni zaključena;
1 - menjava končana.
Zastavica TCF se počisti na 0, ko je podatkovni register IICD prebran (v sprejemnem načinu) ali ko je podatkovni register IICD zapisan (v oddajnem načinu).
bTCF
IAAS Zastavica podrejenega naslova. Nastavite, če naprava deluje v podrejenem načinu in je naslov, poslan v glavnem sporočilu, enak podrejenemu naslovu, ki je shranjen v naslovnem registru IICA.
Zastavica se počisti pri zapisovanju v register IICC.
bIAAS
ZASEDEN Zastavica zasedene linije. Ta zastavica je nastavljena, če je modul I2C prepoznal začetni bit na liniji. Zastavica se počisti, ko modul zazna stop bit na liniji.
0 — vodilo I2C je brezplačno;
1 - vodilo I2C je zasedeno.
bZASEDEN
ARBL Oznaka arbitražnega poraza:
0 - ni kršitev pri delovanju vodila I2C;
1 – pride do izgube arbitraže. Modul I2C mora nekaj časa počakati in nato znova začeti prenos.
bARBL
SRW Podrejeni bit smeri prenosa. Ta bit označuje stanje bita R/W v naslovnem polju:
0 - suženj sprejme. Vodja posreduje sužnju;
1 - podrejeni oddaja. Vodja prejema od sužnja.
bSRW
IICIF Zastavica neservisiranih prekinitvenih zahtev za modul I2C Nastavite na 1, če je nastavljena ena od zastavic: TCF, IAAS ali ARBL.
0 - ni neobdelanih prekinitev;
1 - obstajajo neobdelane prekinitve.
Zastavo ponastavite tako, da vanjo zapišete 1.
bIICIF
RXAK Glavni potrditveni bit:
0—podrejeni potrjen sprejem podatkov;
1 — pomožna enota ni potrdila sprejema podatkov.
Ta bit odraža stanje polja ASK na časovnem diagramu izmenjave.
bRXAK

11.2.8.3. Naslovni register IICA

Registrirajte se Način D7 D6 D5 D4 D3 D2 D1 D0
IICA Branje NASLOV
Zapis
Ponastaviti 0 0 0 0 0 0 0 0

Ta register shranjuje 7-bitni podrejeni naslov, ki ga je dodelil razvijalec to napravo pri razvoju sistema. Ta naslov se samodejno primerja z naslovno kodo, ki jo je podrejena enota prejela v naslovnem polju na vodilu I2C. Če se naslova ujemata, se nastavi bit IAAS v statusnem registru IICS.

11.2.8.4. Register hitrosti prenosa IICF

Za serije MK AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL
Registrirajte se Način D7 D6 D5 D4 D3 D2 D1 D0
IICF Branje MULT ICR
Zapis
Ponastaviti 0 0 0 0 0 0 0 0
Opis bitov:

Vrednosti koeficientov SCL_DIV in SDA_HV

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

Ta register shranjuje dve bitni polji, ki določata hitrost in časovne parametre izmenjave I2C. Frekvenca sinhronizacijskih signalov je določena s formulo:

Čas poravnave podatkov SDA_hold_time na vodilu I2C je časovni interval med trenutkom, ko je signal SCL nastavljen na 0, in spremembo podatkov na liniji SDA. Dodeljeno s parametrom SDA_HV (SDA_Hold_Value) iz tabele za faktor ICR registra hitrosti prenosa:

.

11.2.8.5. register podatkov IICD

Za serije MK AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL
Registrirajte se Način D7 D6 D5 D4 D3 D2 D1 D0
IICD Branje PODATKI I2C
Zapis
Ponastaviti 0 0 0 0 0 0 0 0

Če modul I2C deluje v glavnem načinu, potem operacija pisanja v ta register sproži komunikacijo I2C (vendar le, če je bit smeri komunikacije v nadzornem registru IICC pravilno nastavljen, tj. TX = 1). Prvi bajt po stanju Start, ki ga program zapiše v podatkovni register, podrejeni interpretirajo kot naslov naprave. Zato mora program pravilno oblikovati vsebino prvega bajta. Operacija branja registra vrne zadnji bajt, prejet prek I2C. Operacija branja registra sproži tudi začetek prejemanja naslednjega bajta, vendar le, če je bit smeri komunikacije v krmilnem registru IICC pravilno nastavljen, tj. pri TX = 0! Pri TX = 1 operacija branja registra ne bo povzročila prejema novega bajta prek I2C od podrejenega.

Če modul I2C deluje v podrejenem načinu, bodo podatki, zapisani v ta register, preneseni v linijo SDA vodila I2C, ko glavna naprava izvede sprejemni cikel od tega podrejenega. Operacija branja registra vrne zadnji bajt, prejet od nadrejenega.

11.2.8.6. Nadzorni register IICC2

Za MK serije AC, Dx, EL, JM, QE, SG, SH, SL
Registrirajte se Način D7 D6 D5 D4 D3 D2 D1 D0
IICC Branje GCAEN ADEXT 0 0 0 AD10 AD9 AD8
Zapis
Ponastaviti 0 0 0 0 0 0 0 0

Prvi koraki s prevajalnikom STM32 in mikroC za arhitekturo ARM - 4. del - LCD povezava na osnovi I2C, pcf8574 in HD4478

Naslednji članek bi rad posvetil delu s skupnim vmesnikom i2c, ki se pogosto uporablja v različnih mikrovezjih, povezanih z mikrokrmilnikom.

I2C je vodilo, ki deluje prek dveh fizičnih povezav (poleg skupne žice). Na internetu je o tem veliko napisanega, na Wikipediji so dobri članki. Poleg tega je zelo jasno opisan algoritem delovanja vodila. Skratka, vodilo je dvožilno sinhrono vodilo. Na vodilu je lahko hkrati do 127 naprav (naslov naprave je 7-bitni, k temu se bomo vrnili kasneje). Spodaj je tipičen diagram za povezovanje naprav na vodilo i2c, pri čemer je MK glavna naprava.


Za i2c vse naprave (tako glavne kot podrejene) uporabljajo izhode z odprtim odtokom. Preprosto povedano, pnevmatiko lahko SAMO pritegnejo k TEM. Visok nivo vodila zagotavljajo pull-up upori. Vrednost teh uporov je običajno izbrana v območju od 4,7 do 10 kOhm. i2c je precej občutljiv na fizične linije, ki povezujejo naprave, tako da če se uporablja povezava z veliko kapacitivnostjo (na primer dolg tanek ali oklopljen kabel), lahko vpliv te kapacitivnosti "zabriše" robove signala in moti normalno delovanje pnevmatike. Manjši kot je pull-up upor, manj vpliva ta kapacitivnost na karakteristike robov signala, vendar VEČJA JE OBREMENITEV izhodnih tranzistorjev na vmesnikih i2c. Vrednost teh uporov je izbrana za vsako specifično izvedbo, vendar ne smejo biti manjša od 2,2 kOhm, sicer lahko preprosto zažgete izhodne tranzistorje v napravah, ki delujejo z vodilom.

Vodilo je sestavljeno iz dveh linij: SDA (podatkovna linija) in SCL (ura signal). Ure glavno napravo vodila, običajno naš MK. Ko je SCL visok, se informacije preberejo iz podatkovnega vodila. Stanje SDA je mogoče spremeniti le, ko je signal ure nizek.. Ko je SCL visok, se signal na SDA spremeni pri generiranju signalov START (ko je SCL visok, se signal na SDA spremeni iz visokega v nizek) in STOP - ko je raven SCL visoka, se signal na SDA spremeni iz nizke v visoko).

Ločeno je treba povedati, da je v i2c naslov naveden kot 7-bitno število. 8 - najmanj pomemben bit označuje smer prenosa podatkov 0 - pomeni, da bo podrejeni posredoval podatke, 1 - sprejemal.. Na kratko, algoritem za delo z i2c je naslednji:

  • Visoka raven na SDA in SCL- avtobus je brezplačen, lahko začnete z delom
  • Mojstrska dvigala SCL na 1 in spremeni stanje S.D.A. od 1 do 0 - pritegne k tlom - nastane signal START
  • Glavni posreduje 7-bitni podrejeni naslov z usmerjevalnim bitom (podatki o S.D.A. so razstavljene, ko SCL potegnjen na tla in prebere suženj, ko je izpuščen). Če suženj nima časa "zgrabiti" prejšnjega bitja, pritegne SCL na tla, s čimer pove poveljniku jasno, da stanja podatkovnega vodila ni treba spreminjati: "Še vedno berem prejšnjega." Ko je mojster sprostil pnevmatiko, preveri jo je suženj izpustil?.
  • Po prenosu 8 bitov naslova nadrejena enota ustvari 9. takt in sprosti podatkovno vodilo. Če je suženj slišal njegov nagovor in ga sprejel, potem je bo pritisnil S.D.A. na tla. Tako nastane signal VPRAŠAJ- sprejeto, vse je OK. Če suženj ničesar ne razume ali ga preprosto ni tam, potem ne bo nikogar, ki bi pritisnil pnevmatiko. mojster bo počakal na časovno omejitev in razumel, da ni bil razumljen.
  • Po prenosu naslova, če smo določili smer od gospodarja do sužnja(8 bitov naslova je enakih 1), nato glavni posreduje podatke podrejenemu, ne da bi pozabil preveriti prisotnost VPRAŠAJ od podrejene naprave, ki čaka, da podrejena naprava obdela dohodne informacije.
  • Ko nadrejeni prejme podatke od podrejenega, nadrejeni sam ustvari signal VPRAŠAJ po prejemu vsakega bajta, podrejeni pa nadzoruje svojo prisotnost. Poveljnik morda ne bo posebej poslal VPRAŠAJ preden pošljete ukaz STOP, s čimer je podrejenemu običajno jasno, da ni treba zagotoviti več podatkov.
  • Če je po pošiljanju podatkov s strani masterja (način zapisovanja) potrebno prebrati podatke iz pomožne enote, potem master ponovno ustvari signal START , pošiljanje podrejenega naslova z zastavico za branje. (če je pred ukazom START ni bil prenesen STOP potem se oblikuje ekipa PONOVNI ZAGON). To se uporablja za spremembo smeri komunikacije nadrejeni-podrejeni. Na primer, posredujemo naslov registra podrejeni in nato preberemo podatke iz njega.)
  • Po končanem delu s podrejeno enoto poveljnik ustvari signal STOP- pri visoki ravni signala ure tvori prehod podatkovnega vodila od 0 do 1.
STM 32 ima strojno implementirane oddajnike-sprejemnike vodila i2c. Takšnih modulov v MK je lahko 2 ali 3. Za njihovo konfiguracijo se uporabljajo posebni registri, opisani v referenci za uporabljeni MK.

V MicroC je treba pred uporabo i2c (kot tudi katere koli zunanje naprave) pravilno inicializirati. Za to uporabimo naslednjo funkcijo (inicializacija kot glavni):

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

  • n- številko uporabljenega modula, npr I2C1 oz I2C2.
  • I2C_ClockSpeed- hitrost vodila, 100000 (100 kbs, standardni način) ali 400000 (400 kbs, hiter način). Drugi je 4-krat hitrejši, vendar ga ne podpirajo vse naprave
  • *modul- na primer kazalec na periferni modul &_GPIO_MODULE_I2C1_PB67, ne pozabimo tega Pomočnik za kodo (ctrl-presledek ) zelo pomaga.
Najprej preverimo, ali je avtobus prost, za to obstaja funkcija I2Cn_Je_V mirovanju(); vrne 1, če je avtobus prost, in 0, če je na njem menjava.

I2Cn_Start();
Kje n- številka uporabljenega modula i2c našega mikrokontrolerja. Funkcija bo vrnila 0, če je na vodilu napaka, in 1, če je vse v redu.

Za prenos podatkov v podrejeni uporabljamo funkcijo:

I2Cn_Write(nepodpisani char slave_address, nepodpisani char *buf, nepodpisano dolgo štetje, nepodpisano dolgo END_mode);

  • n- številka uporabljenega modula
  • podrejeni_naslov- 7-bitni podrejeni naslov.
  • *buf- kazalec na naše podatke - bajt ali niz bajtov.
  • štetje- število prenesenih podatkovnih bajtov.
  • END_mode- kaj storiti po prenosu podatkov v podrejeni, END_MODE_STOP - prenašati signal STOP, oz END_MODE_RESTART Pošlji ponovno START, ustvarjanje signala PONOVNI ZAGON in dal oddelku jasno vedeti, da seja z njim ni končana in da bodo podatki zdaj prebrani z njega.
Če želite prebrati podatke iz podrejenega, uporabite funkcijo:

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

  • n- številka uporabljenega modula
  • podrejeni_naslov- 7-bitni podrejeni naslov.
  • *buf- kazalec na spremenljivko ali matriko v katero prejmemo podatke tip char ali short int
  • štetje- število prejetih podatkovnih bajtov.
  • END_mode- kaj storiti po prejemu podatkov od podrejenega - END_MODE_STOP - prenašati signal STOP, oz END_MODE_RESTART poslati signal PONOVNI ZAGON.
Poskusimo nekaj povezati z našim MK. Za začetek: razširjeno mikrovezje PCF8574(A), ki je razširjevalnik vhodno/izhodnih vrat, krmiljenih prek vodila i2c. Ta čip vsebuje samo en notranji register, ki je njegova fizična V/I vrata. Se pravi, če ji posredujete bajt, bo takoj izpostavljen njenim zaključkom. Če odštejete bajt od njega (Prenos START naslov z zastavico za branje, signal PONOVI, prebere podatke in končno ustvari signal STOP), potem bo odražal logična stanja na svojih izhodih. Povežimo naše mikrovezje v skladu s podatkovnim listom:


Naslov mikrovezja se oblikuje iz stanja zatičev A0, A1, A2. Za mikrovezje PCF8574 naslov bo: 0100A0A1A2. (Na primer, imamo A0, A1, A2 na visoki ravni, zato bo naslov našega mikrovezja 0b0100 111 = 0x27). Za PCF8574A - 0111A0A1A2, ki bo z našim povezovalnim diagramom dal naslov 0b0111 111 = 0x3F. Če je recimo A2 priključen na maso, potem je naslov za PCF8574A volja 0x3B. Skupno je mogoče na eno vodilo i2c hkrati namestiti 16 mikrovezij, po 8 PCF8574A in PCF8574.

Poskusimo nekaj prenesti, inicializirati vodilo i2c in nekaj prenesti na naš PCF8574.

#define PCF8574A_ADDR 0x3F //Naslov našega PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // Generiraj signal START I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); // Prenesi 1 bajt podatkov in ustvarite STOP signal) char PCF8574A_reg; // spremenljivka, ki jo zapišemo v PCF8574 void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Zagon I2C delay_ms(25); // Počakaj malo PCF8574A_reg.b0 = 0; // prižgi prvo LED PCF8574A_reg.b1 = 1; // izklopi drugo LED med (1) ( delay_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // obrne stanje LED I2C_PCF8574_WriteReg (PCF8574A_reg) ; // prenos podatkov na naš PCF8574 ) )
Prevedemo in zaženemo naš program ter vidimo, da LED-lučke izmenično utripajo.
Katodo LED na naš PCF8574 sem povezal z razlogom. Stvar je v tem, da ko je na izhod dovedena logična 0, mikrovezje pošteno potegne svoj izhod na tla, ko pa je uporabljena logična 1, ga poveže na + napajanje preko tokovnega vira 100 μA. To pomeni, da na izhodu ne morete dobiti "poštene" logične 1. In ne morete prižgati LED s 100 µA. To je bilo storjeno, da bi konfigurirali izhod PCF8574 na vhod brez dodatnih registrov. Preprosto pišemo v izhodni register 1 (v bistvu nastavimo stanja pinov na Vdd) in ga lahko preprosto kratko povežemo z maso. Trenutni vir ne bo dovolil, da bi izhodna stopnja našega I/O ekspanderja "izgorela". Če nogo potegnemo k tlom, potem je na njej potencial ozemljitve in se odčita logična 0. Če nogo potegnemo na +, se odčita logična 1. Po eni strani je preprosto, po drugi pa tega se morate vedno spomniti, ko delate s temi mikrovezji.


Poskusimo prebrati stanje zatičev našega razširitvenega čipa.

#define PCF8574A_ADDR 0x3F //Naslov našega PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // Generiraj signal START I2C1_Write(PCF8574A_ADDR, &wData, 1, END_MODE_STOP); // Prenesi 1 bajt podatkov in ustvarite STOP signal ) void I2C_PCF8574_ReadReg (nepodpisani char rData) ( I2C1_Start(); // Generiraj signal START I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // Preberi 1 bajt podatkov in ustvari signal STOP) char PCF8574A_reg; //spremenljivka, ki jo zapišemo v PCF8574 char PCF8574A_out; // spremenljivka, v katero beremo, in PCF8574 char lad_state; //naša LED je vklopljena ali izklopljena void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Zagon I2C delay_ms(25); // Počakajte malo PCF8574A_reg.b0 = 0; // prižgi prvo LED PCF8574A_reg.b1 = 1; // izklop druge LED PCF8574A_reg.b6 = 1; // Povlecite nožice 6 in 7 na napajanje. PCF8574A_reg.b7 = 1; medtem ko (1) ( delay_ms(100); I2C_PCF8574_WriteReg (PCF8574A_reg); // zapis podatkov v PCF8574 I2C_PCF8574_ReadReg (PCF85 74A_out); // branje iz PCF8574 if (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0; // Če je pritisnjen 1 gumb (6. bit bajta za branje iz PCF8574 je 0, potem vklop/izklop naše LED), če (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // podobno za 2 gumba in 2 LED ) )
Zdaj s pritiskom na gumbe prižgemo ali ugasnemo našo LED. Mikrovezje ima še en izhod INT. Na njem se generira impulz vsakič, ko se spremeni stanje zatičev našega I/O ekspanderja. Tako, da ga povežete z zunanjim prekinitvenim vhodom našega MK (v enem od naslednjih člankov vam bom povedal, kako konfigurirati zunanje prekinitve in kako delati z njimi).

Uporabimo naš razširjevalnik vrat, da prek njega povežemo prikaz znakov. Teh je zelo veliko, vendar so skoraj vsi zgrajeni na osnovi krmilnega čipa HD44780 in njegovi kloni. Na primer, uporabil sem zaslon LCD2004.


Podatkovni list zanj in krmilnik HD44780 je mogoče zlahka najti na internetu. Povežimo naš zaslon s PCF8574, njenega pa z našim STM32.

HD44780 uporablja vzporedni vmesnik. Podatki se prenašajo z 8 (v enem taktu) ali 4 (v 2 taktih) impulzov vrat na izhodu E. (prebere krmilnik zaslona na padajočem robu, prehod od 1 do 0). Zaključek R.S. označuje, ali pošiljamo podatke na naš zaslon ( RS = 1) (znaki, ki jih mora prikazati, so pravzaprav kode ASCII) ali ukaz ( RS = 0). RW označuje smer prenosa podatkov, pisanja ali branja. Običajno zapisujemo podatke na zaslon, torej ( RW=0). Upor R6 nadzoruje kontrast zaslona. Vhoda za prilagajanje kontrasta ne morete preprosto povezati z maso ali napajanjem, sicer ne boste videli ničesar.. VT1 se uporablja za vklop in izklop osvetlitve zaslona glede na ukaze MK. MicroC ima knjižnico za delo s takimi zasloni prek vzporednega vmesnika, vendar je običajno drago porabiti 8 nog na zaslonu, zato skoraj vedno uporabljam PCF8574 za delo s takšnimi zasloni. (Če koga zanima, bom napisal članek o delu z zasloni na osnovi HD44780, ki so vgrajeni v MicroC prek vzporednega vmesnika.) Protokol izmenjave ni posebno zapleten (uporabili bomo 4 podatkovne linije in prenesli informacije v 2 taktih), to je jasno prikazano v naslednjem časovnem diagramu:


Preden prenesemo podatke na naš zaslon, ga moramo inicializirati s posredovanjem servisnih ukazov. (opisano v podatkovnem listu, tukaj predstavljamo samo najbolj uporabljene)

  • 0x28- komunikacija z indikatorjem preko 4 linij
  • 0x0C- omogoči izpis slike, onemogoči prikaz kazalca
  • 0x0E- omogoči izpis slike, omogoči prikaz kazalca
  • 0x01- počistite indikator
  • 0x08- onemogoči izpis slike
  • 0x06- po prikazu simbola se kazalec premakne za 1 znano mesto
Ker bomo s tem indikatorjem morali delati precej pogosto, bomo ustvarili knjižnico vtičnikov "i2c_lcd.h" . V ta namen v Vodja projekta Datoteke glave in izberite Dodaj novo datoteko . Ustvarimo našo datoteko glave.

#define PCF8574A_ADDR 0x3F //Naslov našega PCF8574 #define DB4 b4 // Ujemanje med pini PCF8574 in indikatorjem #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 #define RW b2 #define RS b1 #define BL b0 // nadzor osvetlitve ozadja #define displenth 20 // število znakov v naši prikazni vrstici statični nepredznačeni char BL_status; // spremenljivka, ki shranjuje stanje osvetlitve ozadja (vklop/izklop) void lcd_I2C_Init(void); // Funkcija inicializacije zaslona in PCF8574 void lcd_I2C_txt(char *pnt); // Prikaže vrstico besedila, parameter je kazalec na to vrstico void lcd_I2C_int(int pnt); // Prikaže vrednost celoštevilske spremenljivke, parameter je izhodna vrednost void lcd_I2C_Goto(unsigned short row, unsigned short col); // premakne kazalec na določen položaj, parametra row - line (od 1 do 2 ali 4, odvisno od prikaza) in col - (od 1 do displenth)) void lcd_I2C_cls(); // Počisti praznino zaslona lcd_I2C_backlight (nepodpisano kratko int stanje); // Omogoči (pri oddaji 1 in onemogoči - pri oddaji 0 osvetlitev zaslona)
Zdaj pa opišimo naše funkcije, spet gremo k Vodja projekta desni klik na mapo Viri in izberite Dodaj novo datoteko . Ustvarite datoteko "i2c_lcd.с" .

#include "i2c_lcd.h" //vključi našo datoteko glave char lcd_reg; //registracija za začasno shranjevanje podatkov, poslanih na PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) //funkcija za pošiljanje podatkov prek i2c na čip PCF8574 ( I2C1_Start(); I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); ) void LCD_COMMAN D ( char com) / /funkcija pošiljanja ukaza na naš zaslon ( lcd_reg = 0; //zapišemo 0 v začasni register lcd_reg.BL = BL_status.b0; //nastavimo pin osvetlitve ozadja v skladu z vrednostjo spremenljivke, ki shranjuje stanje osvetlitve ozadja lcd_reg.DB4 = com.b4; // nastavimo 4 najpomembnejše bite našega ukaza na podatkovno vodilo indikatorja lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; / /ponastavite stroboskopski impulz na 0, indikator prebere podatke I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.DB4 = com.b0; //enako za najmanj 4 pomembni biti 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 (unsigned char com) //pošiljanje podatkov (koda znakov ASCII) indikatorju ( lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; //pošiljanje znaka se razlikuje od pošiljanja ukazov tako, da se bit RS nastavi na 1 lcd_reg.DB4 = com.b4; //nastavi 4 najpomembnejše bite na vhodih 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; // ponastavi stroboskopski impulz na 0, indikator prebere podatke I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg = 0; lcd_reg .BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; lcd_reg.DB4 = com.b0; //nastavi 4 najmanj pomembne bite na vhodih lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; I2C_PCF8574_WriteReg ( lcd_reg); delay_us (300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); ) void lcd_I2C_Init(void) ( I2C1_ Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67 ); //inicializiramo naš modul I2c za MK delay_ms(200); lcd_Command(0x28); // Prikaz v načinu 4 bitov na takt delay_ms (5); lcd_Command(0x08); //Onemogoči izhod podatkov na zaslon delay_ms (5); lcd_Command(0x01); //Počisti prikaz delay_ms (5); lcd_Command(0x06); //Omogoči samodejni premik kazalca po prikazu simbola delay_ms (5); lcd_Command(0x0C); //Vklopi prikaz informacij brez prikaza kazalca delay_ms (25); ) void lcd_I2C_txt(char *pnt) //Izpis niza znakov na zaslon ( unsigned short int i; //začasna niz znakov indeksna spremenljivka char tmp_str; //začasna niz znakov, dolžina 1 večja od dolžine zaslona vrstico, ker je treba vrstico končati siv z znakom NULL ASCII 0x00 strncpy(tmp_str, pnt, displenth); // kopiraj največ znakov displenth izvirnega niza v naš začasni niz za (i=0; i Zdaj povežimo novo ustvarjeno knjižnico z datoteko z našo glavno funkcijo:

#include "i2c_lcd.h" //vključi našo datoteko glave unsigned int i; //začasna spremenljivka števec void main() ( lcd_I2C_Init(); //inicializacija zaslona lcd_I2C_backlight (1); //vklop osvetlitve ozadja lcd_I2C_txt ("Hello habrahabr"); //prikaži vrstico while (1) ( delay_ms( 1000) ; lcd_I2C_Goto (2,1); //pojdi na znak 1 vrstice 2 lcd_i2c_int (i); //prikaži vrednost i++; //povečaj števec ) )

Če je vse pravilno sestavljeno, bi morali videti besedilo na indikatorju in števec, ki se povečuje vsako sekundo. Na splošno nič zapletenega :)

V naslednjem članku bomo nadaljevali z razumevanjem protokola i2c in naprav, ki z njim delujejo. Razmislimo o delu s pomnilnikom EEPROM 24XX in merilnikom pospeška/žiroskopom MPU6050.

Danes je na naši operacijski mizi nov gost, to je izdelek Microchip, MCP23008-E port expander. Ta stvar je namenjena (kot že ime pove) povečanju števila V/I krakov mikrokrmilnika, če nenadoma postanejo nezadostni. Seveda, če potrebujemo noge in izhode, potem lahko to vzamemo in ne skrbimo za to. Če potrebujete vhodne noge, potem obstaja rešitev, ki temelji na strogi logiki. Če potrebujemo tako vhode kot izhode in tudi nadzorovan dvig za vhode, potem je port expander morda najbolj normalna rešitev. Kar se tiče cene naprave, je zelo skromna - približno dolar. V tem članku bom poskušal podrobno opisati, kako krmiliti to mikrovezje z mikrokrmilnikom AVR.

Najprej nekaj o značilnostih:

  • 8 neodvisnih priključnih zatičev
  • Vmesnik za komunikacijo z zunanjim svetom - I2C (frekvenca do 1,7 MHz)
  • Prilagodljiv dvig za vhode
  • Lahko trzne z nogo, ko se spremeni stanje določenih vnosov
  • Trije vhodi za nastavitev naslova mikrovezja (na eno vodilo lahko obesite 8 naprav)
  • Delovna napetost od 1,8 do 5,5 voltov
  • Nizka poraba toka
  • Velika izbira ohišij (PDIP/SOIC/SSOP/QFN)

Raje uporabljam vmesnik I2C, privlači me z majhnim številom žic :-) če pa potrebujete zelo hitro hitrost (do 10 MHz), potem morate uporabiti vmesnik SPI, ki je prisoten v MCP23S08. Razlika med MCP23S08 in MCP23008, kolikor razumem, je le v vmesniku in številu nog za nastavitev naslova čipa. Kdo ima rad kaj krajšega? Pinout mikruhi je v podatkovnem listu, na noben način ni zanimiv in tukaj ne bo obravnavan. Zato takoj preidimo na to, kako začeti delati s to napravo. Vse delo se zmanjša na pisanje in branje določenih podatkov iz registrov mikrovezja. Na moje veselje registrov sploh ni bilo veliko - le enajst. Pisanje in branje podatkov iz registrov je zelo preprosto, objavite o tem. Res je, da ne govorimo o registrih, a princip je enak. Zdaj ostane le še ugotoviti, iz katerih registrov kaj brati in v katere registre kaj pisati. Seveda nam bo pri tem v pomoč podatkovni list. Iz podatkovnega lista izvemo, da ima mikrovezje naslednje registre:

register IODIR
Določa smer, v kateri tečejo podatki. Če je bit, ki ustreza določenemu kraku, nastavljen na ena, potem je krak vhod. Če se ponastavi na nič, zapustite. Skratka, ta register je analogen DDRx v AVR (samo v AVR je 1 izhod, 0 pa vhod).

register IPOL
Če je bit registra nastavljen, je vhodna inverzija omogočena za ustrezen krak. To pomeni, da če na nogo nanesete hlod. nič, potem se iz registra GPIO šteje za eno in obratno. Uporabnost te funkcije je zelo vprašljiva.

register GPINTEN
Vsak bit tega registra ustreza določenemu zatiču vrat. Če je bit nastavljen, potem lahko ustrezen pin vrat, konfiguriran kot vhod, povzroči prekinitev. Če je bit ponastavljen, potem ne bo prekinitve, ne glede na to, kaj naredite z vratno nogo. Pogoje prekinitve določata naslednja dva registra.

register DEFVAL
Trenutna vrednost zatičev, konfiguriranih za vnos, se nenehno primerja s tem registrom. Če se trenutna vrednost nenadoma začne razlikovati od tistega, kar je v tem registru, pride do prekinitve. Preprosto povedano, če je bit nastavljen, se bo prekinitev zgodila, ko se raven spremeni z visoke na nizko na ustreznem kraku. Če je bit počiščen, se prekinitev pojavi na naraščajočem robu.

register INTCON
Vsak bit tega registra ustreza določenemu zatiču vrat. Če je bit prazen, potem vsaka sprememba logične ravni v nasprotno povzroči prekinitev. Če je bit nastavljen, na pojav prekinitve vpliva register DEFVAL (sicer je popolnoma prezrt).

register IOCON
To je register nastavitev. Sestavljen je iz štirih bitov:
SEQOP— bit nadzoruje samodejno povečanje naslova. Če je nameščen, je samodejno povečanje onemogočeno, sicer pa omogočeno. Če ga izklopimo, lahko zelo hitro preberemo vrednost istega registra, saj nam ni treba vsakič posredovati njegovega naslova. Če morate hitro prebrati vseh 11 registrov po vrsti, mora biti omogočeno samodejno povečanje. Vsakič, ko se bajt prebere iz registra, se sam naslov poveča za eno in ga ne bo treba prenašati.
DISSLW— kdo ve, kaj je to bit. Kakor koli obračam, še vedno vse deluje. Vesel bi bil, če bi kdo pojasnil.
HAEN— Bit nastavitve izhoda INT. Če je nastavljen, je zatič konfiguriran kot odprt odtok, če je bit ponastavljen, potem aktivni nivo na nogi INT določa bit INTPOL
INTPOL— določa aktivno raven na kraku INT. Če je nastavljen, je aktivna stopnja ena, sicer nič.

Ostalo je še malo HAEN vendar se ne uporablja v tem čipu (vklopi/izklopi zatiče za naslavljanje strojne opreme v MCP23S08)

register GPPU
Krmi vhodni dvig. Če je bit nastavljen, se bo na ustreznem zatiču prikazal vlečenje do napajanja prek upora 100 kOhm.

register INTF
Register zastavic prekinitve. Če je bit nastavljen, to pomeni, da je ustrezen del vrat povzročil prekinitev. Seveda morajo biti prekinitve za zahtevane krake omogočene v registru GPINTEN

register INTCAP
Ko pride do prekinitve, se celotna vrata preberejo v ta register. Če je po tem več prekinitev, potem vsebina tega registra ne bo prepisana z novo vrednostjo, dokler je ne preberemo ali GPIO.

register GPIO
Pri branju podatkov iz registra odčitavamo logične nivoje na pinih porta. Pri zapisovanju podatkov postavljamo logične nivoje. Pri zapisovanju v ta register se isti podatki samodejno zapišejo v register OLAT

register OLAT
S pisanjem podatkov v ta register izpišemo podatke v vrata. Če berete podatke od tam, boste prebrali tisto, kar je bilo zapisano, in ne tisto, kar je dejansko na vhodih vrat. Za branje vnosov uporabljamo samo GPIO.

Opis registrov je po mojem mnenju povsem običajen in ne bi smel nikogar preseliti. Sedaj pa samo za zabavo napišimo majhno predstavitev, ki bo anketirala 4 gumbe, povezane z razširjevalnikom vrat, in glede na njihovo stanje prižgala ali ugasnila 4 ustrezne LED diode, povezane z istim razširjevalnikom. Primer bo čim bolj preprost, brez prekinitev. Preprosto nenehno odčitavamo stanje gumbov in to stanje prikazujemo na LED diodah. Diagram naše naprave bo videti takole:

Ni nujno, da je prekinitveni pin priključen, tukaj ga ne potrebujemo, je pa potreben poteg za reset nogo! Precej časa sem porabil, dokler nisem ugotovil, da je bil moj razširjevalnik vrat občasno ponastavljen zaradi pomanjkanja zategovanja. Zdaj pa pojdimo k kodi. Seveda bomo pisali naprej. Preprosta koda:

Program rashiritel; const AdrR=%01000001; //Naslov čipa z bitom za branje const AdrW=%01000000; //Naslov čipa z bitnim zapisom var r:byte; ///Procedura zapiše podatke iz spremenljivke Dat v register na naslovu Adr Procedure WriteReg(Dat,Adr:byte); Začni TWI_Start(); TWI_Write(AdrW); TWI_Write(Adr); TWI_Write(Dat); TWI_Stop(); Konec; ///Funkcija vrne vrednost registra na naslov Adr Function ReadReg(Adr:byte):byte; var a:byte; Začni TWI_Start(); TWI_Write(AdrW); TWI_Write(Adr); TWI_Start(); TWI_Write(AdrR); a:=TWI_Read(0); TWI_Stop(); rezultat:=a; Konec; začetek TWI_INIT(200000); ///Inicializacija i2c WriteReg(%00001111,0x00); //Najnižji 4 biti so vhodi, preostali 4 biti pa izhodi WriteReg(%00001111,0x06); //Vklopljeno pull-up za 4 vnose Medtem ko je TRUE do Begin r:=ReadReg(0x09); //Branje stanja vhodov r:= NE r; //Potrebno je obrniti bite, sicer bodo LED diode zasvetile, ko spustite gumb r:= r shl 4; //Premik za 4 bite v levo ... WriteReg(r,0x0A); //Prikaži stanje konca gumbov; konec.

Objavljeno 26.10.2016

V prejšnjem članku smo si ogledali delovanje STM32 z vodilom I 2 C kot Master. To pomeni, da je bil vodja in je zasliševal senzor. Zdaj pa naredimo STM32 podrejenega in se odzivamo na zahteve, to pomeni, da sam deluje kot senzor. Za registre z naslovi od 0 do 0xFF bomo dodelili 255 bajtov pomnilnika in dovolili Masterju, da jih piše/bere. In da primer ne bo tako preprost, naredimo naš STM32 analogno-digitalni pretvornik z vmesnikom I 2 C. ADC bo obdelal 8 kanalov. Krmilnik bo pri branju iz registrov posredoval rezultate transformacij Masterju. Ker je rezultat pretvorbe ADC 12 bitov, potrebujemo 2 registra (2 bajta) za vsak kanal ADC.

i2c_slave.h vsebuje nastavitve:

I2CSLAVE_ADDR– naslov naše naprave;

ADC_ADDR_START– začetni naslov registrov, ki so odgovorni za rezultate pretvorb ADC.

V datoteki i2c_slave.c najbolj nas zanimajo funkcije get_i2c1_ram in set_i2c1_ram. funkcija get_i2c1_ram skrbi za branje podatkov iz registrov. Vrne podatke z navedenega naslova, ki je dan Masterju. V našem primeru se podatki berejo iz polja i2c1_ram, če pa Master zahteva naslove registrov iz obsega, dodeljenega za rezultate ADC, se pošljejo podatki o pretvorbi ADC.

get_i2c1_ram:

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

funkcija set_i2c1_ram– zapisuje podatke, prejete od poveljnika, v registre z navedenim naslovom. V našem primeru se podatki preprosto zapišejo v matriko i2c1_ram. Ampak to ni obvezno. Lahko na primer dodate ček in, ko določena številka prispe na določen naslov, izvedete nekaj dejanj. Tako lahko pošiljate različne ukaze mikrokontrolerju.

set_i2c1_ram:

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

Inicializacija je precej preprosta:

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

Najprej nastavimo maksimalno delovno frekvenco regulatorja. Največja hitrost je potrebna, ko se je treba izogniti kakršnim koli zakasnitvam na vodilu I 2 C. Nato zaženemo delovanje ADC z uporabo DMA. O . O . In končno, inicializiramo vodilo I 2 C kot Suženj. Kot lahko vidite, nič zapletenega.

Zdaj povežimo naš modul STM32 z Raspberry Pi. Priključimo potenciometre na ADC kanale. In prebrali bomo indikatorje ADC iz našega krmilnika. Ne pozabite, da morate za delovanje vodila I 2 C namestiti vlečne upore na vsako linijo vodila.

V konzoli Raspberry preverimo, ali je naša naprava vidna na vodilu I 2 C (o tem):

I2cdetect -y 1

Kot lahko vidite, naslov naprave 0x27, čeprav smo določili 0x4E. Ko boš imel čas, razmisli, zakaj se je to zgodilo.

Za branje iz registrov naprave I 2 C-Slave izvedite ukaz:

I2cget -y 1 0x27 0x00

Kje:
0x27– naslov naprave,
0x00– naslov registra (0x00…0xFF).

Za pisanje v registre naprave I 2 C-Slave izvedite ukaz:

I2cset -y 1 0x27 0xA0 0xDD

De:
0x27– naslov naprave,
0xA0– naslov registracije
0xDD-8-bitni podatki (0x00…0xFF)

Prejšnji ukaz je v register zapisal številko 0xDD 0xA0(lahko pišeš v prvih 16 registrov, vendar ni smiselno, ampak so rezervirani za ADC). Zdaj pa preberimo:

I2cget -y 1 0x27 0xA0

Za poenostavitev postopka branja podatkov kanala ADC sem napisal skript:

#!/usr/bin/env python import smbus import time bus = smbus.SMBus(1) naslov = 0x27 medtem ko (1): ADC = (); za i v območju (0, 8): LBS = bus.read_byte_data(naslov, 0x00+i*2) MBS = bus.read_byte_data(naslov, 0x00+i*2+1) ADC[i] = MBS*256 + LBS natisni ADC time.sleep(0,2)

Anketira in prikaže rezultate vseh 8 ADC kanalov na konzoli.

Na podoben način lahko združimo več mikrokontrolerjev. Eden od njih bi moral biti Master(), drugi Slave.

Želim ti uspeh!




Vrh