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.
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.
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.
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.
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
#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
#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!