I2C (Arduino)

David Obdrzalek David.Obdrzalek na mff.cuni.cz
Neděle Prosinec 13 10:36:22 CET 2020


Obsluha i2c je v Arduinu prevzata z knihovny Wire, ktera je na rozdil od tech 
Arduinonativnich veci docela dobre napsana i popsana (ale to se netyka po webu 
rozsirenych prikladu pouziti). Jako u vseho s Arduinem je pro spravnou funkci velmi 
vhodne az nezbytne vedet, jak se to dela uvnitr; kazdy, kdo pouziva Arduino v 
ne-cloudove verzi, se do tech zdrojaku muze podivat, protoze je ma nainstalovane u 
sebe. 

Pro ATmega328 je vse potrebne napsane v 
(tamkdejeArduinoinstalovane)\hardware\arduino\avr\libraries\Wire\src\Wire.cpp

Z volani Wire.requestFrom(I2C_SLAVE_ADDRESS, 10); se pres dva hopy stane 
requestFrom(I2C_SLAVE_ADDRESS, 10, 0, 0, true) a to je takhle:

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, 
uint8_t isize, uint8_t sendStop)

kde je jadrem tohle:

// perform blocking read into buffer
  uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop);

Funkce twi_readFrom je implementovana v 
hardware\arduino\avr\libraries\Wire\src\utility\twi.c

kde se to nastartuje a pak ceka, az to dobehne:

  startMicros = micros();
  while(TWI_MRX == twi_state){
    if((twi_timeout_us > 0ul) && ((micros() - startMicros) > twi_timeout_us)) {
      twi_handleTimeout(twi_do_reset_on_timeout);
      return 0;
    }
  }

(prenos se dela pres interrupt, ktery kdyz to skonci, nastavi twi_state na 
TWI_READY a diky tomu pak tenhle while skonci)

pricemz ten timeout je docela humorne definovany na zacatku tehoz zdrojaku takhle:

// twi_timeout_us > 0 prevents the code from getting stuck in various while loops 
here
// if twi_timeout_us == 0 then timeout checking is disabled (the previous Wire lib 
behavior)
// at some point in the future, the default twi_timeout_us value could become 25000
// and twi_do_reset_on_timeout could become true
// to conform to the SMBus standard
// http://smbus.org/specs/SMBus_3_1_20180319.pdf
static volatile uint32_t twi_timeout_us = 0ul;
static volatile bool twi_timed_out_flag = false;  // a timeout has been seen
static volatile bool twi_do_reset_on_timeout = false;  // reset the TWI registers 
on timeout

a je tam i funkce twi_setTimeoutInMicros:
/* 
 * Function twi_setTimeoutInMicros
 * Desc     set a timeout for while loops that twi might get stuck in
 * Input    timeout value in microseconds (0 means never time out)
 * Input    reset_with_timeout: true causes timeout events to reset twi
 * Output   none
 */
void twi_setTimeoutInMicros(uint32_t timeout, bool reset_with_timeout){
  twi_timed_out_flag = false;
  twi_timeout_us = timeout;
  twi_do_reset_on_timeout = reset_with_timeout;
}

a tahle funkce je propasirovana i nahoru do TwoWire (ve Wire.cpp) jako 
TwoWire::setWireTimeout s touhle dokumentaci:

/***
 * Sets the TWI timeout.
 *
 * This limits the maximum time to wait for the TWI hardware. If more time passes, 
the bus is assumed
 * to have locked up (e.g. due to noise-induced glitches or faulty slaves) and the 
transaction is aborted.
 * Optionally, the TWI hardware is also reset, which can be required to allow 
subsequent transactions to
 * succeed in some cases (in particular when noise has made the TWI hardware think 
there is a second
 * master that has claimed the bus).
 *
 * When a timeout is triggered, a flag is set that can be queried with 
`getWireTimeoutFlag()` and is cleared
 * when `clearWireTimeoutFlag()` or `setWireTimeoutUs()` is called.
 *
 * Note that this timeout can also trigger while waiting for clock stretching or 
waiting for a second master
 * to complete its transaction. So make sure to adapt the timeout to accomodate for 
those cases if needed.
 * A typical timeout would be 25ms (which is the maximum clock stretching allowed 
by the SMBus protocol),
 * but (much) shorter values will usually also work.
 *
 * In the future, a timeout will be enabled by default, so if you require the 
timeout to be disabled, it is
 * recommended you disable it by default using `setWireTimeoutUs(0)`, even though 
that is currently
 * the default.
 *
 * @param timeout a timeout value in microseconds, if zero then timeout checking is 
disabled
 * @param reset_with_timeout if true then TWI interface will be automatically reset 
on timeout
 *                           if false then TWI interface will not be reset on 
timeout

 */


Takze kdyz bych se vratil k uplne puvodnimu dotazum, je to myslim takhle:

> >> Funkce Wire.requestFrom(I2C_SLAVE_ADDRESS, 10); má vrátit počet byte, 
> >> tj v mém případně bych měl dostat  číslo 10. Už tato funkce načte 
> >> těch 10 byte do bufru? 
Ano (resp. pokud vse probehne v poradku, tak ano).

> >> A nebo jen dá pokyn "otroku posílej data" a na vlastní přenos nečeká?
Ne.

> >> Má smysl testovat návratovou hodnotu z této funkce?
> >>
> >> Když Slave neodpoví, kde se to zasekne, bude se někde na něco čekat? 
> >> Je tam nějaký timeout?
Timeout tam muze byt, ale defaultne nastaveny neni.

Takze s tim testovanim to vidim takhle: kdyz nenastavis timeout, tak nema smysl 
testovat, protoze kdyz Slave neodpovi, zasekne se to, a to jeste predtim, nez by se 
ta navratova hodnota mohla otestovat a kdyz se to nezasekne, tak v bufferu bude ten 
pozadovany pocet bajtu, ktere je naslednym volanim read() mozno ziskat. (*)

Pokud si timeout nastavis, tak testovat smysl ma, protoze kdyz Slave neodpovi, tak 
se to po case probere a requestFrom misto ocekavaneho poctu vrati nulu.


(*) Tedy pokud jsem nepozadoval vic, nez se do bufferu vejde, v takovem pripade by 
se nechalo precist jen co se vejde. Test pomoci available() je tak trochu zbytecne 
komplikovany, ale treba to je pro lidi srozumitelnejsi, nez testovat si, co read 
vrati (data 0-255 kdyz tam nejaka jsou, nebo -1, kdyz uz jsem precetl vsechno, co 
prislo). Dale myslim, ze break pri i>=10 je uplne zbytecny, protoze vic, nez se 
pozadovalo, tam z principu byt nemuze.
(mimochodem, nevyzvednute zbozi propada, ten requestFrom plni buffer vzdy od 
zacatku, i kdyz si predchozi data nekdo jeste uplne neprecetl)


Ohledne clock stretching bug:

U AVR je tenhle bug prokazatelne u tinyAVR a to v HW implementaci Slave a jeho 
dusledkem je, ze slave hodiny nenatahuje. Ale kdyz se SW povede napsat tak, aby 
clock stretching nebylo potreba, problem nevznikne. Je na to appnote AVR290.

Myslim, ze u ATmega328 v rezimu Master bug s clock stretching neni, ale samozrejme 
se mohu mylit (filozoficky vzato tohle se tezko dokazuje, mnohem snaz se dokaze, ze 
nekde nejaky bug je, kdyz je k dispozici priklad...).

Ten bug co je v RPi, se odviji od bugu v BCM2835/6/7, tedy vsechna RPi generace 1-3 
a Zero (jak u 4, to nevim, tam je nejaky jiny BCM). Uvedene BCM spatne resi, kdyz 
slave natahne hodiny. S bugem u AVR to nijak nesouvisi, kdyz je RPi master, tak i 
korektni slave clock strechting funguje jen za stastnych okolnosti bez ohledu na 
protistranu. Rada pro pouziti RPi jako i2c master je "u zadneho slave nepouzivejte 
clock stretching, anebo si i2c na RPi naprogramujte bitbangingem cele sami, bez 
HW".

D.O.

On 12 Dec 2020 at 22:59, Pavel Hudecek wrote:
> Nevím jak to řeší Arduino knihovny, ale HW to má takto:
>    Po každém přenosu byte přijímající strana odpoví bitem ACK=0 a to už
> včetně adresy. Tzn. první na čem to může zhavarovat je, že slave neodpoví
> na adresu. Pak když master zapisuje do slave, zase slave může říct dost tím
> ACK. Při čtení ze slavu, musí naopak master potvrzovat ACK. U nekonečných
> věcí typu ADC se takto obvykle má čtení zarazit. Nakonec se posílá stop bit,
> ale to je mimo běžnou komunikaci, stejně jako start.
>    No a pak je ještě clock stretching: Když slave nestíhá, může SCL podržet
> v 0 a master by měl na základě toho čekat (libovolně dlouho). A tady je to
> zajímavý, protože některý (nebo všechny, nevím) ATmegy mají v implementaci
> chybu, která většinou nevadí, ale RPI zero (a možná i nějaký další,
> nevím) mají taky chybu a to zrovna takovou, že jejich setkání vede
> k zátuhům:-)
> 
> PH
> 
> Od: Petr Zapadlo
> Však já tam tuhle funkci používám, ale byl jsem svědkem toho, že I2C 
> slave byl nějaký zaseklý a Atmegu mi resetoval WDT, protože se čekalo na 
> nějakou operaci na I2C sběrnici.
> 
> Snažím se tomu nějak předejít a když nad tím tak přemýšlím, tak podle 
> mě  už Wire.requestFrom(I2C_SLAVE_ADDRESS, 10); musí zajistit ten přenos 
> dat do bufrů, které jsou pak testovány pomocí Wire.available()
> 
> Jelikož nikde nejsou nějaké pauzy na přenos, a funkce RequestFrom a 
> available jdou hned za sebou, tak podle RequestFrom končí až v okamžiku, 
> kdy je v bufru 10 byte a pokud zahapruje Slave, tak se to pozná už v 
> rámci RequestFrom.
> 
> A tem jsem možnost nějakého timeout nenašel.
> 
> Myslím si to správně?
> 
> 
> Dne 12. 12. 20 v 21:33 Petr Labaj napsal(a):
> > Já o Arduinu vím pendrek. Ale podle dokumentace je tam funkce 
> > available(),
> > která je určitě neblokující a říká, kolik dat je k dispozici.
> > Takže si timeout a ošetření kolapsu partnera snadno uděláte podle 
> > libosti sám.
> >
> >
> > ***********************
> > Dne 12.12.2020 v 21:04 Petr Zapadlo napsal(a):
> >> Zdravím,
> >>
> >> měl bych dotaz k použití I2C sběrnice, resp k její obsluze.
> >>
> >> Je to na ATmega328. Příkladový kousek kodu:
> >>
> >> Wire.requestFrom(I2C_SLAVE_ADDRESS, 10);
> >> while (Wire.available()) {
> >>       pole[i] = Wire.read();
> >>       i++;
> >>       if (i>=10){break;}
> >>     }
> >>
> >> A můj dotaz, jak se to bude chovat když Slave nějak zahapruje.
> >>
> >> Funkce Wire.requestFrom(I2C_SLAVE_ADDRESS, 10); má vrátit počet byte, 
> >> tj v mém případně bych měl dostat  číslo 10. Už tato funkce načte 
> >> těch 10 byte do bufru? A nebo jen dá pokyn "otroku posílej data" a na 
> >> vlastní přenos nečeká?
> >>
> >> Má smysl testovat návratovou hodnotu z této funkce?
> >>
> >> Když Slave neodpoví, kde se to zasekne, bude se někde na něco čekat? 
> >> Je tam nějaký timeout?
> 





Další informace o konferenci Hw-list