Slave I2C zařízení (konzistence dat)

Odpovědět
MartinL
Příspěvky: 252
Registrován: 24 úno 2013, 14:13
Kontaktovat uživatele:

Slave I2C zařízení (konzistence dat)

Příspěvek od MartinL »

Programuji právě snímač čáry, který má být jako slave I2C zařízení. A narazil jsem na principiální problém jak zajistit konzistenci dat. Do teď jsem všechny zařízení měl připojené přes UART a tam tento problém není. O co mi jde?

a) Připojení přes UART: master požádá o hodnotu, slave - tedy zařízení změří hodnotu a odešle zpět mastrovi. Vše OK.

b) Připojení přes I2C: master požádá o hodnotu (vyšle adresu čtení) a pak čte hodnotu. Nelze tedy měření provést až pro "žádosti" o hodnotu (měření trvá cca 5ms) a I2C master neví jak dlouho čekat. Proto slave provádí měření stále dokola a po přijetí žádosti odešle hodnotu. A tady je zakopaný pes. Pokud přijde požadavek na data v průběhu měření, tak se odešlou nesmysly. Proto jsem udělal bufer, do kterého se data přesunou až po dokončení měřeni (při zakázaném přerušení). Toto většinou funguje, ale principiální problém zůstává. Pokud se kopírování do buferu provede mezi vysílanými daty (odesílá se 2 nebo více bytů), pak to opět bude nesmyslná hodnota. Použití nějakého příznaku odesílání dat je taky problém - při odesílání slave neví, kolik bytů se bude odesílat.

Jak to elegantně vyřešit?

Zatím mě napadá jediné řešení (použité např. na sonarech SFRxx). Tam se odešle požadavek na měření. Master počká předepsanou dobu a pak teprve čte data. Ovšem tam, vše závisí na "slušném" chování mastra.
Uživatelský avatar
adamh
Příspěvky: 62
Registrován: 24 úno 2013, 23:10
Bydliště: Opava / Praha
Kontaktovat uživatele:

Re: Slave I2C zařízení (konzistence dat)

Příspěvek od adamh »

MartinL píše:Pokud se kopírování do buferu provede mezi vysílanými daty (odesílá se 2 nebo více bytů), pak to opět bude nesmyslná hodnota.
Co takhle si před odesíláním udělat ještě jednu kopii?:-) Dá se předpokládat, že master bude chtít z hodnoty rozsekané do více bajtů přečíst vše.

Alternativně mít ring buffer (jak se to řekne česky?), do kterého budu těch vzorků ukládat více a při requestu na čtení prvního bajt si zapamatovat ukazatel. Muselo by se ale počítat s tím, že se zbývající bajty přečtou dřív, než se ukazovaná pozice přepíše novými daty - to už je asi lepší to řešení s kopií.
“We’re all pathetic and creepy and can’t get girls. That’s why we fight robots.” –Kripke (TBBT)
Osobní web: adamh.cz
MartinL
Příspěvky: 252
Registrován: 24 úno 2013, 14:13
Kontaktovat uživatele:

Re: Slave I2C zařízení (konzistence dat)

Příspěvek od MartinL »

Díky za tip, sice mě to také už napadlo, že v bych obsluze přerušení od I2C po přijetí adresy pro čtení celý bufer zkopíroval. Ale je mi to trošku proti srsti, takhle plýtvat pamětí (celý blok dat má cca 60B, a potřeboval bych ho tedy 3x) a já jsem holt vychován ještě postaru (tedy paměť má velikost v B a ne jak je dnes zvykem v GB).

Mám teď ještě jeden nápad, ale ten mi přijde taky trochu krkolomný. (po měření zakázat přerušení, otestovat příznak - probíhajícího odesílaní dat, není-li nastaven zkopírovat data a povolit přerušení; je-li nastaven, povolit přerušení a čekat na uvolnění příznaku probíhajícího odesílání dat. Příznak by se nahazoval v obsluze přerušení I2C po přijetí adresy pro čtení a shazoval po dokončení odesílání dat).
hubacekp
Příspěvky: 259
Registrován: 24 úno 2013, 13:40

Re: Slave I2C zařízení (konzistence dat)

Příspěvek od hubacekp »

Ahoj, nevím, jestli to k něčemu bude, ale měl jsem vyřešené v jednom pokusu tanku čtení ze dvou SRF02 následovně. Posílám jen funkci getRange(), kterou jsem volal v cyklu spolu s jinými servo() a report(). getRange() si sama ošetří kdy měřit a kdy přečíst výsledek. Základ jsem někde zkopíroval a pak upravil pro sebe:

Kód: Vybrat vše

/*
Generic example for the SRF modules 02, 08, 10 and 235.
Only the SRF08 uses the light saensor so when any other 
range finder is used with this code the light reading will 
be a constant value. 
*/

#include <Wire.h>

#define srfAddress1 0x70                          // Address of the N°1 SRF02
#define srfAddress2 0x71                          // Address of the N°2 SRF02
#define cmdByte 0x00                              // Command byte
#define rangeByte 0x02                            // Byte for start of ranging data

byte highByte = 0x00;                             // Stores high byte from ranging
byte lowByte = 0x00;                              // Stored low byte from ranging

int rangeData1 = 0;
int rangeData2 = 0;
unsigned long lastRangeMeasure1 = 0;
unsigned long lastRangeMeasure2 = 0;
int lastRangePeriod = 200;

#include <Servo.h>
Servo myservo1;
Servo myservo2; 
int pos1 = 90;
int pos2 = 90;
unsigned long lastReport = 0;

void setup(){
  Wire.begin();
  
  myservo1.attach(8);  // attaches the servo on pin 9 to the servo object 
  myservo2.attach(9);
  myservo1.write(90);
  myservo2.write(90);
  
  Serial.begin(57600);
  //setAddress();
  delay(100);                                  // Waits to make sure everything is powered up before sending or receiving data
}

void loop(){
  getRange();                                  // Calls a function to get range
  servo();
  report();
  //delay(100);                                  // Wait before looping
}

void getRange(){

  if( (millis() - lastRangeMeasure1) > lastRangePeriod ){
    measureRange1();
    lastRangeMeasure1 = millis();
  }
  
  if( (millis() - lastRangeMeasure2) > lastRangePeriod ){
    measureRange2();
    lastRangeMeasure2 = millis();
  }
  
  if( (millis() - lastRangeMeasure1) > 100 ){
    rangeData1 = readRange1();                     // Calls a function to get range
//    Serial.print("r1: ");Serial.println(rangeData1);
  }

  if( (millis() - lastRangeMeasure2) > 100 ){
    rangeData2 = readRange2();                     // Calls a function to get range
//    Serial.print("r2: ");Serial.println(rangeData2);
  }
}

void measureRange1(){                             // This function gets a ranging from the SRF08
  Wire.beginTransmission(srfAddress1);            // Start communticating with SRF08
  Wire.write(cmdByte);                             // Send Command Byte
  Wire.write(0x51);                                // Send 0x51 to start a ranging
  Wire.endTransmission();
  //Serial.print(" m1 ");
}

void measureRange2(){                             // This function gets a ranging from the SRF08
  Wire.beginTransmission(srfAddress2);            // Start communticating with SRF08
  Wire.write(cmdByte);                             // Send Command Byte
  Wire.write(0x51);                                // Send 0x51 to start a ranging
  Wire.endTransmission();
  //Serial.print(" m2 ");
}

int readRange1(){
  int range = 0;
  Wire.beginTransmission(srfAddress1);            // start communicating with SRFmodule
  Wire.write(rangeByte);                           // Call the register for start of ranging data
  Wire.endTransmission();
  Wire.requestFrom(srfAddress1, 2);               // Request 2 bytes from SRF module
  while(Wire.available() < 2);                    // Wait for data to arrive
  highByte = Wire.read();                      // Get high byte
  lowByte = Wire.read();                       // Get low byte
  range = (highByte << 8) + lowByte;              // Put them together
  return(range);                                  // Returns Range
//  Serial.println("r1");
}

int readRange2(){
  int range = 0;
  Wire.beginTransmission(srfAddress2);            // start communicating with SRFmodule
  Wire.write(rangeByte);                           // Call the register for start of ranging data
  Wire.endTransmission();
  Wire.requestFrom(srfAddress2, 2);               // Request 2 bytes from SRF module
  while(Wire.available() < 2);                    // Wait for data to arrive
  highByte = Wire.read();                      // Get high byte
  lowByte = Wire.read();                       // Get low byte
  range = (highByte << 8) + lowByte;              // Put them together
  return(range);                                  // Returns Range
//  Serial.println("r2");
}


petr-kubac
Příspěvky: 96
Registrován: 24 úno 2013, 15:43
Bydliště: Frydek - Mistek
Kontaktovat uživatele:

Re: Slave I2C zařízení (konzistence dat)

Příspěvek od petr-kubac »

Ahoj Martine

Což takhle něco velice nesportovního tedy

1. Master odešle požadavek na čtení
2. Slave mu odešle obsah bufferu
3. Slave přečte nové hodnoty
4. Slave uloží hodnoty do bufferu
5. Slave čeká na požadavek Mastera

Nesportovnost jasně tkví v tom, že se odesílají data získaná při minulém cyklu - je otázka jak moc to vadí při pravidelné aktualizaci se tato varianta jen nepatrně liší od varianty se periodickým čtením a synchronizací nějakým semaforem - dokonce je o něco lepší neb je explicitně jasné z které doby data pocházejí
"The best computer language is a solder" - "Nejlepší programovací jazyk je pájka" - Bob Pease
http://petr-kubac.blog.cz/
Odpovědět