Reklama

TWI u AVR 2. Díl – Praktické příklady II

Úvod

V druhém dílu seriálu zaměřeného na jednotku TWI u AVR se podíváme na zbylé dva praktické příklady TWI – přenos více bytů Slave >> Master a TWI – jednoduchá obousměrná komunikace.

Seznam příkladů v celém seriálu:

  • TWI – Přenos 1 byte Master >> Slave (v předchozím díle)
  • TWI – Přenos více bytů Master >> Slave (v předchozím díle)
  • TWI – přenos více bytů Slave >> Master
  • TWI – jednoduchá obousměrná komunikace
TWI – přenos více bytů Slave >> Master

Předchozí dva příklady z minulého dílu nebyly příliš sofistikované a sloužily výhradně k rozdávání pokynů nebo rozesílání dat z Master do Slave. Slave neměl šanci předat informaci do Master mikrokontroléru. V tomto příkladě si vyzkoušíme opačnou situaci. Master bude provádět sběr dat ze Slave, ale nebude jim posílat žádné pokyny. Typické použití najde tento model tam, kde je potřeba provádět sběr dat z autonomních stanic. Slave mikrokontroléry mohou například provádět měření několika fyzikálních veličin na různých místech, provádět předzpracování dat a jeden Master z nich může data vyčítat a třeba přeposílat do PC. Posílat data po jednom byte jako v příkladě: TWI – Přenos 1 byte Master >> Slave (viz minulý díl) není moc užitečné, takže budeme ze Slave číst celá pole. Program na straně Slave nepotřeboval příliš mnoho úprav. Opět používá přerušení, ve kterém se příkazem switch selektuje stav I2C sběrnice a připravují se příslušná data k odeslání. Jak je vidět ve zdrojovém kódu, tak se Slave po přijetí svojí adresy (s příznakem čtení) přepne do role Slave Transmitter a naplní TWDR prvními daty. S každým dalším odeslaným bytem Slave připravuje do TWDR následující datový byte. A přirozeně hlídá, aby pole nepřeteklo. Slušně naprogramovaný Master by se přirozeně neměl pokoušet číst více dat, než je mu k dispozici. Ve všech ostatních situacích Slave žádnou akci nedělá a nechá TWI modul čekat na další zprávu kterou potvrdí. Nijak třeba není pokryta situace, kdy Master nedá potvrzení (což se stane po posledním přijatém bytu). Mohli bychom do switch přidat case “TW_ST_DATA_NACK”: a na tuto situaci nějak zareagovat. Ale náš program je natolik jednoduchý, že žádná speciální reakce pro tento případ není potřeba.

 

001: // Přenos více bytů Slave >> Master (kód pro slave)
002: #include <avr/io.h>
003: #define F_CPU 8000000
004: #include <util/twi.h>
005: #include <util/delay.h>
006: #include <avr/interrupt.h>
007:
008: #define POCET_BYTU 5
009: #define MY_SLAVE_ADDRESS 0x02 // adresa tohoto zařízení, změňte ji pokud budete
010:// nahrávat kód do více slave
011: volatile char data[POCET_BYTU] = {0,1,2,3,4};
012: volatile unsigned int pocet = 0;
013:
014: int main(void)
015: {
016: DDRA = (1<<DDA7); // ledka na PA7
017: // naše slave adresa, zarovnaná
018: // vlevo, General call povoleno
019: TWAR = (MY_SLAVE_ADDRESS<<1) | (1<<TWGCE);
020: // povolit TWI, povolit přerušení, potvrdit příští zprávu
021: TWCR = (1<<TWEN) | (1<<TWEA) | (1<<TWIE) | (1<<TWINT);
022: sei();
023: while (1){}
024: }
025:
026: ISR(TWI_vect){
027: switch(TW_STATUS){ // čteme v jakém stavu se nachází TWI a
028: // podle toho reagujeme
029: case TW_ST_SLA_ACK: // někdo nás adresoval s příznakem čtení (SLA+R),
030: // jsme v roli Slave Transmitter
031: TWDR = data[0];// nahrajeme první data k odeslání
032: pocet = 1;// příště budeme odesílat druhý byte dat
033: // odešli data, očekávej potvrzení
034: TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);
035: break;
036: case TW_ST_DATA_ACK: // odeslali jsme data a master je potvrdil
037: TWDR = data[pocet]; // připravíme další data k odeslání
038: // hlídejme přetečení pole
039: if(pocet < POCET_BYTU-1){pocet++;}
040: // odešli data, očekávej potvrzení
041: TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);
042: break;
043: case TW_SR_STOP: // přišla STOP sekvence nebo opakovaný START ?
044: // to nás nezajímá, čekej na další akci a dávej potvrzení
045: TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
046: break;
047: default:
048: // pokud přijde cokoli nečekaného, čekej na další
049: // akci a dávej potvrzení
050: TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
051: }
052: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

Program na straně Master je o něco komplikovanější. Musí totiž rozpoznat poslední čtený byte a nedat Slave obvodu potvrzení zprávy. Kdyby po posledním bytu zprávu potvrdil, tak by Slave očekával, že bude odesílat další data a ponechal by si ovládání SDA linky. Tím by ale Masteru znemožnil vygenerovat STOP sekvenci a ukončit komunikaci. Celá I2C sběrnice by se tak zablokovala. Ne každý Slave nutně potřebuje, aby poslední byte nebyl potvrzen, ale mikrokontrolér jako Slave nejspíše ano. Proto by to měl program na straně Masteru respektovat. Jinak v programu pro master není žádná záludnost. Po stisku tlačítka se zavolá funkce i2c_precti_pole() jejímž prvním argumentem je Slave adresa, druhým argumentem je ukazatel na pole, kde máme přijatá data uložit a posledním argumentem je počet bytů, které má vyčíst. Uvnitř funkce je generována START sekvence, pak se posílá Slave adresa s příznakem čtení. Pokud Slave svoji adresu potvrdí, tak dáme pokyn TWI modulu, aby provedl příjem jednoho byte a dal Slave obvodu potvrzení. To opakujeme tak dlouho, než dojdeme k poslednímu byte. Ten musíme přijmout, ale nesmíme dát potvrzení, aby Slave pochopil, že komunikace končí a že má SDA linku uvolnit. Po přijetí posledního byte už jen vygenerujeme STOP sekvenci a přenos je dokončen. Program pak ještě zkontroluje poslední přijatá data, a pokud odpovídají předpokládané hodnotě (0x04), tak blikne s LED diodou. Tu máme připojenou na pin PA5. Opět je to jen výuková záležitost, v praxi pak přijatá data zpracujete jinak. Případně si jako poslední byte zprávy necháte poslat CRC kód a provede kontrolu neporušenosti dat. Funkce je napsaná docela krkolomně, za což se vám omlouvám. Jestli mě napadne elegantnější způsob, jak se vypořádat s while smyčkou určitě příklad změním. Kdyby napadl vás, nechte mi vzkaz v komentářích. Potěšíte mě:-)

 

001: // Přenos více bytů Slave >> Master (kód pro master)
002: #include <avr/io.h>
003: #define F_CPU 8000000
004: #include <util/delay.h>
005: #include <util/twi.h>
006:
007: #define SLV1_ADDRESS 0x2// adresy různých slave zařízení na sběrnici
008: #define SLV2_ADDRESS 0x3
009: #define POCET_BYTU 5
010:
011: #define TW_SEND_START TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN)
012: #define TW_SEND_STOP TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)
013: #define TW_SEND_DATA TWCR = (1<<TWINT) | (1<<TWEN)
014: #define TW_RECEIVE_DATA TWCR = (1<<TWINT) | (1<<TWEN)
015: #define TW_WAIT while(!(TWCR & (1<<TWINT)))
016: #define TLAC1 (PINA & (1<<PINA7))
017: #define TLAC2 (PINA & (1<<PINA6))
018:
019: char i2c_precti_pole(char adresa, char *pole, unsigned int pocet_bytu);
020: char tlac1 = 0, tlac2 = 0;
021: char data[POCET_BYTU] = {}; // do tohoto pole přijímáme byty ze slave
022:
023: int main(void){
024: TWBR = 32; // žádná předdělička, F_SCL = 8MHz/(16+2*TWBR) = 100kHz.
025: // zapnout TWI modul
026: TWCR = (1<<TWEN);
027: // LEDka
028: DDRA |= (1<<DDA5);
029: // dvě tlačítka
030: DDRA &= ~((1<<DDA7) | (1<<DDA6));
031: // pull-up pro tlačítka
032: PORTA = (1<<PORTA7) | (1<<PORTA6);
033: while(1){
034: // po stisku tlačítka 1
035: if(!TLAC1 && tlac1 == 0){
036: tlac1 = 1; // zamči tlačítko (do uvolnění)
037: // přečti ze slave1 POCET_BYTU
038: i2c_precti_pole(SLV1_ADDRESS,data,POCET_BYTU);
039: // pokud jsou data korektní blikni LED
040: if(data[4] == 4){PORTA ^= (1<<PORTA5);}
041: }
042: if(!TLAC2 && tlac2 == 0){
043: // zamči talčítko (do uvolnění)
044: tlac2 = 1;
045: // přečti ze slave2 POCET_BYTU
046: i2c_precti_pole(SLV2_ADDRESS,data,POCET_BYTU);
047: // pokud jsou data korektní blikni LED
048: if(data[4] == 4){PORTA ^= (1<<PORTA5);}
049: }
050: // tlačítka uvolněna
051: if(TLAC1 && TLAC2){
052: tlac1 = 0; // odemči talčítka
053: tlac2 = 0;
054: }
055: _delay_ms(100); // ošetření zákmitů
056: }
057: }
058:
059:
060: char i2c_precti_pole(char adresa, char *pole, unsigned int pocet_bytu){
061: unsigned int i;
062: // zarovnání adresy na 8bit formát
063: adresa = adresa << 1;
064: // vygenerovat START sekvenci
065: TW_SEND_START;
066:
067: // počkat na odezvu TWI
068: TW_WAIT;
069: // pokud nebyl start vygenerován, máme error a končíme
070: if ((TW_STATUS) != TW_START){ return 1;}
071: // nahrát adresu slave s příznakem čtení (SLA+R)
072: TWDR = adresa | 1;
073: // odeslat adresu
074: TW_SEND_DATA ;
075:
076: // počkat na odezvu TWI
077: TW_WAIT;
078: if ((TW_STATUS) != TW_MR_SLA_ACK){TW_SEND_STOP; return 2;}
079: // jsme v režimu master–receiver, poslali jsme slave
080: // adresu s příznakem čtení a slave nám dal potvrzení
081: // vyčti 1 byte ze slave a dávej potvrzení
082: TW_RECEIVE_DATA | (1<<TWEA);
083: // vynulovat počítadlo přijatých bytů
084: i = 0;
085: // dokud jsme nepřijali všechny byty
086: while(i < pocet_bytu){
087: // počkáme na odezvu TWI
088: TW_WAIT;
089: switch(TW_STATUS){
090: // přijali jsme byte a potvrdili zprávu
091: case TW_MR_DATA_ACK:
092: // nahrát i–tý prvek pole
093: pole[i] = TWDR;
094: // inkrementovat počítadlo přijatých bytů, pokud
095: // nedosáhlo maxima
096: if (i < pocet_bytu-1){i++;}
097: // přijímáme-li poslední byte přijmi 1 byte a
098: // NEDÁVEJ potvrzení
099: if (i >= (pocet_bytu-1)){TW_RECEIVE_DATA;
100: // vyčti 1 byte a DEJ potvrzení
101: else {TW_RECEIVE_DATA | (1<<TWEA);}
102: break;
103:
104: // přijali jsme data a nedali potvrzení (poslední byte)
105: case TW_MR_DATA_NACK:
106: // nahrát i–tý prvek pole (poslední)
107: pole[i] = TWDR;
108: // inkrementovat počítadlo přijatých bytů – ukončí
109: // while cyklus
110: i++;
111: break;
112:
113 default:
114: // pokud nastal jakýkoli problém, posíláme STOP a
115: // končíme fci
116: TW_SEND_STOP;
117: return 2;
118: }
119: }
120: // konec komunikace
121: TW_SEND_STOP;
122: return 0;
123: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 
TWI – jednoduchá obousměrná komunikace

Tento příklad bude asi tím nejdůležitějším z celého návodu, protože se podobá typické komunikaci s čidly a jinými I2C obvody. Připravíme si Slave, který má v paměti uloženo pět bytů. Slave bude schopen přijmout jeden byte, který bude fungovat jako “ukazatel” na některý z těchto pěti bytů. Slave nejprve odešle ten byte, který si Master vyžádal a pak popořadě všechny další. Pošle-li Master například 0x03, Slave mu nejprve odešle hodnotu data[3], pak hodnotu data[4]. Master si díky této vlastnosti může vybírat, co chce ze Slave číst. Stejný postup se používá při vyčítání dat například z ADXL345, LM75, DS3231 a mnoha dalších obvodů. Nejprve odešlete jeden byte do čidla. A čidlo díky tomu ví, která část paměti vás zajímá. Když pak z něj začnete vyčítat, tak vám posílá obsah své paměti od místa, které jste specifikovali.

Stejně jako v předchozích příkladech master komunikuje se Slave po stisku tlačítka. Změn doznala komunikační funkce transakce(). Jejím prvním argumentem je Slave adresa obvodu, se kterým chceme komunikovat. Druhý argument je byte, který chceme do Slave poslat (adresa dat, které z něj chceme vyčítat). Třetí argument je ukazatel na pole, do kterého budeme přijatá data ukládat. Poslední argument je počet bytů, které chceme přijmout. Ten nesmí být menší jak dva. Funkce prostě není chytře napsaná, aby mohla přijímat pouze jeden byte. Uvnitř funkce probíhá vše podle očekávání. Generuje se START sekvence, která je následovaná Slave adresou s příznakem zápisu. Následně je do Slave zapsán jeden byte. Potom se generuje opakovaná START sekvence následovaná Slave adresou s příznakem čtení. Dále funkce přijme požadovaný počet bytů a po posledním z nich nedá potvrzení. Komunikace končí STOP sekvencí.

 

001: // Jednoduchá obousměrná komunikace (kód pro master)
002: #include <avr/io.h>
003: #define F_CPU 8000000
004: #include <util/delay.h>
005: #include <util/twi.h>
006:
007: #define SLV1_ADDRESS 0x2// adresy různých slave zařízení na sběrnici
008: #define SLV2_ADDRESS 0x3
009: #define POCET_BYTU 2
010:
011: #define TW_SEND_START TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN)
012: #define TW_SEND_STOP TWCR = (1<<TWINT) |(1<<TWEN) | (1<<TWSTO)
013: #define TW_SEND_DATA TWCR = (1<<TWINT) | (1<<TWEN)
014: #define TW_RECEIVE_DATA TWCR = (1<<TWINT) | (1<<TWEN)
015: #define TW_WAIT while(!(TWCR & (1<<TWINT)))
016: #define TLAC1 (PINA & (1<<PINA7))
017: #define TLAC2 (PINA & (1<<PINA6))
018:
019: char transakce(char slave_adresa, char adresa, char *data, unsigned int pocet_bytu);
020: char tlac1 = 0, tlac2 = 0;
021: char data[POCET_BYTU] = {}; // do tohoto pole přijímáme byty ze slave
022:
023: int main(void){
024: TWBR = 32; // žádná předdělička, F_SCL = 8MHz/(16+2*TWBR) = 100kHz.
025: // zapnout TWI modul
026: TWCR = (1<<TWEN);
027: // dvě tlačítka
028: DDRA &= ~((1<<DDA7) | (1<<DDA6));
029: // pull-up pro tlačítka
030: PORTA = (1<<PORTA7) | (1<<PORTA6);
031: while (1){
032: // po stisku tlačítka 1
033: if (!TLAC1 && tlac1 == 0){
034: tlac1 = 1; // zamči tlačítko (do uvolnění)
035: // přečti dva byty ze slave1 od adresy 1
036: transakce(SLV1_ADDRESS,1,data,POCET_BYTU);
037: }
038: if (!TLAC2 && tlac2 == 0){
039: tlac2 = 1; // zamči tlačítko (do uvolnění)
040: // přečti dva byty ze slave2 od adresy 1
041: transakce(SLV2_ADDRESS,1,data,POCET_BYTU);
042: }
043: // tlačítka uvolněna
044: if (TLAC1 && TLAC2){
045: tlac1 = 0; // odemči tlačítka
046: tlac2 = 0;
047: }
048: _delay_ms(100); // ošetření zákmitů
049: }
050: }
051:
052: // arg1 – adresa slave zařízení se kterým
053: // chceme komunikovat
054: // arg2 – byte který bude do slave odeslán (info o tom
055: // co z něj chceme číst)
056: // arg3 – pole do kterého má fce přijatá data ukládat
057: // arg4 – počet bytů která má fce ze zařízení
058: // vyčíst, musí být větší jak jeden
059: char transakce(char slave_adresa, char adresa, char *pole, unsigned int pocet_bytu){
060: unsigned int i;
061: // zarovnání adresy na 8bit formát
062: slave_adresa = slave_adresa << 1;
063: // vygenerovat START sekvenci
064: TW_SEND_START;
065:
066: // počkat na odezvu TWI
067: TW_WAIT;
068: // pokud nebyl start vygenerován, máme error a končíme
069: if ((TW_STATUS) != TW_START){return 1;}
070: // nahrát adresu slave s příznakem zápisu (SLA+W)
071: TWDR = slave_adresa;
072: // odešli adresu
073: TW_SEND_DATA;
074:
075: // počkat na odezvu TWI
076: TW_WAIT;
077: // Slave nám nedal potvrzení, případně nastal jiný
078: // problém ? končíme komunikaci STOP sekvencí
079: if ((TW_STATUS) != TW_MT_SLA_ACK){TW_SEND_STOP; return 2;}
080: // nahrát data která chceme poslat do slave
081: TWDR = adresa;
082: // odešli data
083: TW_SEND_DATA;
084:
085: // počkat na odezvu TWI
086: TW_WAIT;
087: // Slave nám nedal potvrzení, případně nastal jiný
088: // problém ? končíme komunikaci STOP sekvencí
089: if ((TW_STATUS) != TW_MT_DATA_ACK){TW_SEND_STOP; return 2;}
090: // vygeneruj opakovaný START
091: TW_SEND_START;
092:
093: TW_WAIT;
094: // nepodařilo se vygenerovat START ?
095: if ((TW_STATUS) != TW_REP_START){return 2;}
096: // slave adresa s příznakem čtení (SLA+R)
097: TWDR = slave_adresa | 1;
098: // odešli slave adresu
099: TW_SEND_DATA;
100:
101: // počkat na odezvu TWI
102: TW_WAIT;
103: if ((TW_STATUS) != TW_MR_SLA_ACK){TW_SEND_STOP; return 2;}
104: // jsme v režimu master–receiver, poslali jsme slave
105: // adresu s příznakem čtení a slave nám dal potvrzení
106: // vyčti 1 byte ze slave a potvrď mu zprávu
107: TW_RECEIVE_DATA | (1<<TWEA);
108: i = 0; // vynulovat počítadlo přijatých bytů
109:
110: // dokud jsme nepřijali všechny byty
111: while(i < pocet_bytu){
112: // počkáme na odezvu TWI
113: TW_WAIT;
114: switch(TW_STATUS){
115: // přijali jsme byte a dali slave potvrzení
116: case TW_MR_DATA_ACK:
117: // nahrát i–tý prvek pole
118: pole[i] = TWDR;
119: // inkrementovat počítadlo přijatých bytů, pokud
120: // nedosáhlo maxima
121: if (i < pocet_bytu-1){i++;}
122: // přijímáme-li poslední byte, přijmi 1 byte a
123: // NEDÁVEJ potvrzení
124: if (i >= (pocet_bytu-1)){TW_RECEIVE_DATA;}
125: // vyčti 1 byte ze slave DEJ potvrzení
126: else {TW_RECEIVE_DATA | (1<<TWEA);}
127: break;
128: // přijali jsme byte a nedali slave potvrzení
129: // (poslední byte)
130: case TW_MR_DATA_NACK:
131: // nahrát i–tý prvek pole (poslední)
132: pole[i] = TWDR;
133: // inkrementovat počítadlo přijatých bytů – ukončí
134: // while cyklus
135: i++;
136: break;
137: default:
138: // pokud nastal jakýkoli problém, posíláme STOP a
139: // končíme fci
140: TW_SEND_STOP;
141: return 2;
142: }
143: }
144: // konec komunikace
145: TW_SEND_STOP;
146: return 0;
147: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

Slave má opět jednodušší roli jako Master. V podstatě stačí potvrzovat zprávy a sledovat tři situace. Pokud nám Master poslal datový byte, tak vyčteme přijatá data a uložíme je jako “ukazatel” pocet, který specifikuje, jaká data se budou odesílat. Dále kontrolujeme, zda nás někdo volal s příznakem čtení. V takovém případě připravíme první data k odeslání. A jako poslední stačí kontrolovat, zda jsme odeslali data a dostali potvrzení. V takovém případě se od nás čeká odeslání dalších dat, takže je stačí připravit a nahrát do registru TWDR. Všechny ostatní situace nás v podstatě nezajímají. I když jsou v programu “ošetřeny” nemáme žádnou zajímavou akci, kterou bychom při nich mohli udělat. Takže pouze čekáme na některou ze tří výše jmenovaných. Ukázka komunikace je na Obr. 1

 

001: // Jednoduchá obousměrná komunikace (kód pro slave)
002: #include <avr/io.h>
003: #define F_CPU 8000000
004: #include <util/twi.h>
005: #include <util/delay.h>
006: #include <avr/interrupt.h>
007:
008: #define POCET_BYTU 5
009: #define MY_SLAVE_ADDRESS 0x02// adresa tohoto zařízení, změňte ji pokud budete
010: // nahrávat kód do více slave
011: volatile char data[POCET_BYTU] = {0,1,2,3,4};
012: volatile unsigned int pocet;
013:
014: int main(void)
015: {
016: // naše slave adresa, zarovnaná vlevo, General
017: // call povoleno
018: TWAR = (MY_SLAVE_ADDRESS<<1) | (1<<TWGCE);
019: // povolit TWI, povolit přerušení, potvrdit příští zprávu
020: TWCR = (1<<TWEN) | (1<<TWEA) | (1<<TWIE) | (1<<TWINT);
021: sei();
022: while (1){}
023: }
024:
025: ISR(TWI_vect){
026: // čteme v jakém stavu se nachází TWI a podle
027: // toho reagujeme
028: switch(TW_STATUS){
029: // někdo nás adresoval s příznakem zápisu (SLA+W)
030: case TW_SR_SLA_ACK:
031: // to nás nezajímá, čekej na další akci a dávej potvrzení
032: TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
033: break;
034: // někdo nám poslal data a my mu zprávu potvrdili
035: case TW_SR_DATA_ACK:
036: // uložme přijatý byte, chápeme ho jako ukazatel na data,
037: // která po nás master chce
038: pocet = TWDR;
039: // čekej na další akci a dávej potvrzení
040: TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
041: break;
042: // někdo nás adresoval s příznakem čtení (SLA+R)
043: case TW_ST_SLA_ACK:
044: // nahrajeme první data k odeslání
045: TWDR = data[pocet];
046: // odešli data, očekávej potvrzení
047: TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);
048: break;
049: // odeslali jsme data a master je potvrdil
050: case TW_ST_DATA_ACK:
051: // připravíme další data k odeslání (a hlídáme přetečení)
052: if (pocet < POCET_BYTU-1){pocet++;}
053: // další data k odeslání
054: TWDR = data[pocet];
055: // odešli data, očekávej potvrzení
056: TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);
057: break;
058: // přišla STOP sekvence nebo opakovaný START ?
059: case TW_SR_STOP:
060: // to nás nezajímá, čekej na další akci a dávej potvrzení
061: TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
062: break;
063: default:
064: // pokud přijde cokoli jiného, čekej na další akci
065: // dávej potvrzení
066: TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
067: }
068: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

Obr. 1: Zápis do Slave následovaný vyčtením dvou bajtů. Červený otazník znamená nepotvrzení zprávy (což je správně).

Obr. 1: Zápis do Slave následovaný vyčtením dvou bajtů. Červený otazník znamená nepotvrzení zprávy (což je správně).

Na tomto místě bych vám doporučil udělat si malý domácí úkol. Zkuste naprogramovat funkci, která ze Slave vyčte jeden byte z požadované adresy. Prostě buď napište úplně novou jednoúčelovou funkci, nebo vylepšete tu moji, tak aby nebyla limitovaná vyčítáním dvou a více bytů. Určitě se vám v budoucnu bude hodit.

Pár slov závěrem

Tento seriál asi patřil k těm složitějším. Nejspíš to je tím, že se setkáváte s velkým množstvím nových pojmů a že většina z nich nemá ustálené české ekvivalenty. Kromě toho je problematika dosti komplexní a nemá “limity”, takže se vám může zdát, že je ještě hodně věcí, ve kterých nemáte jasno. Nejspíš budete muset věnovat ještě dost času k získání zkušeností s provozem I2C a já doufám, že vás návod posunul co nejdál. Přiznám se, že sám s I2C čas od času zápasím a to ji používám pouze k jednoduchým úkolům. Tento návod tedy posloužil i mě abych si v některých otázkách udělal jasno.

 
Autor: Michal Dudka
 

Zajímavé odkazy:

 
Příklad z avr-libc
Programming AVR I2C interface
Tajned.cz implementace EEPROM
Video tutorial [EN]
AVR315 Using the TWI Module as I2C Master

 

Hotové knihovny:

 
Peter fleury (TWI i bit-bang)
Procyon AVRlib
Květákov.NET

Následující a předchozí příspěvek v kategorii:

 
Předchozí: TWI u AVR 1. Díl – Teorie a první příklady
 

Tajned facebook
 

Za případné chyby v textu, ve zdrojovém kódě, nebo ve schématickém zapojení se omlouváme.
AUTOŘI NEBEROU ŽÁDNOU ODPOVĚDNOST ZA PŘÍPADNÉ ÚJMY NA ZDRAVÍ ČI MAJETKU.