60 nap alatt Arduino #30-#31 - IIC busz és az EEPROM

A "60 nap alatt Arduino" tanfolyam házi feladatai és közvetlen témái
Avatar
macsek
Bitmanipulátor
Hozzászólások: 121
Csatlakozott: 2008. december 4. csütörtök, 7:00
Tartózkodási hely: Bp

EEPROM írás vagy olvasás működő program

HozzászólásSzerző: macsek » 2012. szeptember 3. hétfő, 8:42

Pár hibát még beletettem az ellenőrzésekkel, aztán kiszedegettem őket és végül itt egy szinte mindent ellenőrző és működő verzió.

/* i2c eeprom iras/olvasas*/
/* Macsek, 2012.09.03 */
/* v 1.02 varakozas modositasa blokkolora */
/* rengeteg (reszben szuksegtelen) hibaellenorzes */

#include "Wire.h" //I2C, azaz TWI miatt kell

#define I2C_EEPROM_CIM 0x50
#define EEPROM_LAPMERET 64  // 64 bajt 1 lap, ez a belso puffermeret is
#define EEPROM_MERET (32U*1024) //24LC256 = 32kB

#define EEPROM_IRAS_LAPATLEPES_HIBA -1
#define EEPROM_IRAS_IRAS_HIBA -2     //254
#define EEPROM_IRAS_ENDTRANSMISSION_HIBA -3
#define EEPROM_IRAS_CIMZES_HIBA -4  //252
#define EEPROM_OLVASAS_HIBA -11
#define EEPROM_OLVASAS_CIMZES_HIBA -12
#define EEPROM_OLVASAS_ENDTRANSMISSION_HIBA -13

//#define DEBUG

void setup(void)
{
  byte b;
  byte tarolo[64]; //
  unsigned int i;
  int valasz;

  Serial.begin(9600);
  Serial.println("EEPROM teszt, Macsek");

  Wire.begin();

  //teszt
#define TESZT_CIM 333

  b = read_eeprom(TESZT_CIM);
  Serial.print("olvasott bajt:");
  Serial.println(b);

  valasz = write_eeprom(TESZT_CIM, b+1);
  if(valasz)
  {
    Serial.print("IRASHIBA!! :");
    Serial.println(valasz);
  }

  b = read_eeprom(TESZT_CIM);
  Serial.print("Iras utan olvasott bajt:");
  Serial.println(b);

  //// blokkokkal ugyanez

#define TESZT_BLOKK_CIM (2*EEPROM_LAPMERET)
#define TESZT_BLOKK_MERET 10

  Serial.println("Bajtonkent kettovel noveljuk a teszt blokk adatait");
  for(i=0; i<TESZT_BLOKK_MERET; i++)
  {
    valasz=read_eeprom(TESZT_BLOKK_CIM+i);
    if(i)
      Serial.print(", ");
    Serial.print(valasz);
    Serial.print("=>");
    write_eeprom(TESZT_BLOKK_CIM+i, valasz+2);

    valasz=read_eeprom(TESZT_BLOKK_CIM+i);
    Serial.print(valasz);

  }
  Serial.println();

  Serial.println("eeprom blokk olvasas");
  valasz = read_eeprom_tobbet(TESZT_BLOKK_CIM, tarolo, TESZT_BLOKK_MERET);
  if(valasz)
  {
    Serial.print("BLOKK OLVASAS HIBA!! :");
    Serial.println(valasz);
  }

  for(i=0; i<TESZT_BLOKK_MERET; i++)
  {
    Serial.print(tarolo[i]);
    if (i<TESZT_BLOKK_MERET-1)
      Serial.print(", ");
    else
      Serial.println("");
  }

  for(i=0; i<TESZT_BLOKK_MERET; i++)
    if(tarolo[i])
      //tarolo[i] *= i+2;
      tarolo[i]++;
    else
      tarolo[i]=millis(); // ha nulla volt tegyunk bele valami ertelmeset

  Serial.println("==>");
  for(i=0; i<TESZT_BLOKK_MERET; i++)
  {
    Serial.print(tarolo[i]);
    if (i<TESZT_BLOKK_MERET-1)
      Serial.print(", ");
    else
      Serial.println("");
  }

  valasz = write_eeprom_lap(TESZT_BLOKK_CIM, tarolo, TESZT_BLOKK_MERET);
  if(valasz)
  {
    Serial.print("IRASHIBA!! :");
    Serial.println(valasz);
  }

  Serial.println("eeprom blokk iras utani ujraolvasas");
  valasz = read_eeprom_tobbet(TESZT_BLOKK_CIM, tarolo, TESZT_BLOKK_MERET);
  if(valasz)
  {
    Serial.print("BLOKK OLVASAS HIBA!! :");
    Serial.println(valasz);
  }

  for(i=0; i<TESZT_BLOKK_MERET; i++)
  {
    Serial.print(tarolo[i]);
    if (i<TESZT_BLOKK_MERET-1)
      Serial.print(", ");
    else
      Serial.println("\n");
  }

  ;
}


void loop(void)
{
  ;
}


byte read_eeprom(unsigned int cim)
{
  byte valaszkod=0;

  Wire.beginTransmission(I2C_EEPROM_CIM);

  if( !Wire.write(cim>>8) )
  {
    valaszkod = EEPROM_OLVASAS_CIMZES_HIBA;
  }
  else if( !Wire.write(cim&0xff) )
  {
    valaszkod = EEPROM_OLVASAS_CIMZES_HIBA;
  }
  else if(Wire.endTransmission())
  {
    valaszkod = EEPROM_OLVASAS_ENDTRANSMISSION_HIBA; 
  }
  // hiba lehet 1 cimre nem valaszolt 2 adatra nem valaszolt?
  // lehetne egy globalis valtozoval jelezni vagy egy cim szerint atadott parameterben

  Wire.requestFrom(I2C_EEPROM_CIM, 1); // // requestFrom nem ad hibakodot
  // requestFrom mar beolvasta, csak akkor jott tovabb
  // while( !Wire.available() ) // kivarjuk, meg ha blokkol is
  // ;

#ifdef DEBUG
  if(valaszkod)
  {
    Serial.print("read_eeprom(");
    Serial.print(cim);
    Serial.print(") : HIBA!!! : ");
    Serial.println((int)valaszkod);
  }
#endif

  return( Wire.read() ); // read nem ad hibakodot
}



byte write_eeprom(unsigned int cim, byte kiirando)
{
  byte valaszkod=0;

  Wire.beginTransmission(I2C_EEPROM_CIM);
  if( !Wire.write(cim>>8) )
  {
    valaszkod = EEPROM_IRAS_CIMZES_HIBA;
  }
  else if( !Wire.write(cim&0xff) )
  {
    valaszkod = EEPROM_IRAS_CIMZES_HIBA;
  }
  else if( !Wire.write(kiirando) )
  {
    valaszkod = EEPROM_IRAS_IRAS_HIBA;
  }
  else if(Wire.endTransmission())
  {
    valaszkod = EEPROM_IRAS_ENDTRANSMISSION_HIBA; 
  }

  if(valaszkod == 0) // nem volt hiba
    delay(5);

  return valaszkod;
}

// max 32 byte olvashato egyszerre: Wire.h, twi.h
byte read_eeprom_tobbet(unsigned int cim, byte *beolvasohely, unsigned int olvasando_db)
{
  byte valaszkod=0;
  unsigned int i;

  Wire.beginTransmission(I2C_EEPROM_CIM);
  if( !Wire.write(cim>>8) )
  {
    valaszkod = EEPROM_OLVASAS_CIMZES_HIBA;
  }
  else if( !Wire.write(cim&0xff) )
  {
    valaszkod = EEPROM_OLVASAS_CIMZES_HIBA;
  }
  else if(Wire.endTransmission())
  {
    valaszkod = EEPROM_OLVASAS_ENDTRANSMISSION_HIBA; 
  }

  if(valaszkod == 0) // nincs hiba a cimzesnel
  {
    //if(Wire.requestFrom(I2C_EEPROM_CIM, olvasando_db) != olvasando_db) // nem dokumentalt, hagyjuk inkabb
    Wire.requestFrom(I2C_EEPROM_CIM, olvasando_db); // beolvassa az osszes adatot aztan tovabbmegy
    // azt az esetet nem kezeltuk ha NACK miatt abbamarad a beolvasas
    // available() megmondhatna, h annyi bajt jott-e be

    for(i=0; i<olvasando_db; i++)
    {
      // nem kell, mert mar tudjuk, h bejott az osszes kert adat
      // while( !Wire.available() ) // kivarjuk, meg ha blokkol is
      // ;
      beolvasohely[i] = Wire.read(); // beolvasas bajtonkent
#ifdef DEBUG
      //if(valaszkod)
      {
        Serial.print(beolvasohely[i]);
        if(i<olvasando_db-1) Serial.print(", ");
        else Serial.println();
      }
#endif
    } // for

  } // if valaszkod

#ifdef DEBUG
  //if(valaszkod)
  {
    Serial.print("read_eeprom_tobbet(");
    Serial.print(cim);
    Serial.print(", ");
    Serial.print((long)beolvasohely);
    Serial.print(", ");
    Serial.print(olvasando_db);
    Serial.print(") : HIBA!!! : ");
    Serial.println(valaszkod);
  }
#endif


  return( valaszkod );
}

// max 32 byte irhato egyszerre: Wire.h, twi.h
// A rutin tudNA max 64 byte kiirasat egy lapra (64 bajtos lapon belul)
byte write_eeprom_lap(unsigned int cim, byte *kiirandok, byte kiirando_db)
{
  byte valaszkod = 0;
  unsigned int i;

#define EEPROM_LAPMASK (0xffff - EEPROM_LAPMERET +1) // azaz (-EEPROM_LAPMERET)
  if(cim&EEPROM_LAPMASK != (cim+kiirando_db)&EEPROM_LAPMASK) // Lapon belul marad?
    valaszkod = EEPROM_IRAS_LAPATLEPES_HIBA;  
  else
  {
    Wire.beginTransmission(I2C_EEPROM_CIM);

    if( !Wire.write(cim>>8) )
    {
      valaszkod = EEPROM_IRAS_CIMZES_HIBA;
    }
    else if( !Wire.write(cim&0xff) )
    {
      valaszkod = EEPROM_IRAS_CIMZES_HIBA;
    }
    else
    {
      for(i=0; i<kiirando_db; i++)
      {
#ifdef debug
        Serial.print(kiirandok[i]);
        Serial.print(" ");
#endif  

        if( !Wire.write(kiirandok[i]) ) // hibakezeles: 1=ok, 0=hiba
        {
          valaszkod = EEPROM_IRAS_IRAS_HIBA;
          break; // ne is irjunk tovabb, mar hibas
        }
      }
#ifdef debug
      Serial.println();
#endif  

      //// if (!valaszkod) // meg nem volt hiba
      if(Wire.endTransmission())
        valaszkod = EEPROM_IRAS_ENDTRANSMISSION_HIBA; 

      if(!valaszkod) // nem volt hiba
        delay(5); // 5ms a max lap kiirasi ido
    } // cimzes jo
  } // lap atlepes
  return valaszkod;
}


Reset-nyomogatás után az első futási eredmények:

Kód: Egész kijelölése

EEPROM teszt, Macsek
olvasott bajt:0
Iras utan olvasott bajt:1
Bajtonkent kettovel noveljuk a teszt blokk adatait
2=>4, 2=>4, 2=>4, 2=>4, 2=>4, 2=>4, 2=>4, 2=>4, 2=>4, 2=>4
eeprom blokk olvasas
4, 4, 4, 4, 4, 4, 4, 4, 4, 4
==>
5, 5, 5, 5, 5, 5, 5, 5, 5, 5
eeprom blokk iras utani ujraolvasas
5, 5, 5, 5, 5, 5, 5, 5, 5, 5

EEPROM teszt, Macsek
olvasott bajt:1
Iras utan olvasott bajt:2
Bajtonkent kettovel noveljuk a teszt blokk adatait
5=>7, 5=>7, 5=>7, 5=>7, 5=>7, 5=>7, 5=>7, 5=>7, 5=>7, 5=>7
eeprom blokk olvasas
7, 7, 7, 7, 7, 7, 7, 7, 7, 7
==>
8, 8, 8, 8, 8, 8, 8, 8, 8, 8
eeprom blokk iras utani ujraolvasas
8, 8, 8, 8, 8, 8, 8, 8, 8, 8

EEPROM teszt, Macsek
olvasott bajt:2
Iras utan olvasott bajt:3
Bajtonkent kettovel noveljuk a teszt blokk adatait
8=>10, 8=>10, 8=>10, 8=>10, 8=>10, 8=>10, 8=>10, 8=>10, 8=>10, 8=>10
eeprom blokk olvasas
10, 10, 10, 10, 10, 10, 10, 10, 10, 10
==>
11, 11, 11, 11, 11, 11, 11, 11, 11, 11
eeprom blokk iras utani ujraolvasas
11, 11, 11, 11, 11, 11, 11, 11, 11, 11

Avatar
normen
DrótVégénSzéndarab
Hozzászólások: 26
Csatlakozott: 2012. május 27. vasárnap, 6:00

Re: EEPROM írás vagy olvasás működő program

HozzászólásSzerző: normen » 2012. szeptember 3. hétfő, 12:11

macsek írta:Pár hibát még beletettem az ellenőrzésekkel, aztán kiszedegettem őket és végül itt egy szinte mindent ellenőrző és működő verzió.



Szia!

Esetleg olvasásnál a felolvasott bájtot egy paraméterként kapott memóriacímre írhatnád, míg a függvényed az olvasási művelet eredményességével térhetne vissza, pl:

#define EEREAD_NODATA                 1
#define EEREAD_OK                     0
#define EEREAD_ADDRESS_ERROR         -1 
#define EEREAD_ENDTRANSMISSION_ERROR -2

#define I2C_EEADDR 0x50

byte errorHandler(byte errorCode, byte addr) {
  switch(errorCode) {
    case EEREAD_ADDRESS_ERROR:
      Serial.print("HIBA - EEPROM címzési hiba a 0x%02x címen\n", addr);
      break;
    case EEREAD_ENDTRANSMISSION_ERROR:
      Serial.print("HIBA - EEPROM komunikációs hiba\n");
      break;
  }
  return errorCode;
}

byte readEE(unsigned int cim, byte *byteReaded) {
  byte valaszkod=0;

  Wire.beginTransmission(I2C_EEADDR);

  if( !Wire.write(cim>>8) || !Wire.write(cim&0xff) )
    return EEREAD_ADDRESS_ERROR;

  if(Wire.endTransmission())
    return EEREAD_ENDTRANSMISSION_ERROR;

  Wire.requestFrom(I2C_EEPROM_ADDR, 1);

  if (!Wire.available())
     return EEREAD_NODATA;

  *byteReaded=Wire.read();
  return EEREAD_OK;


void setup(void) {
  //Soros Port inicalizálás
  Serial.begin(9600);

  ////////////////////
  //olvasás tesztelése
  
  byte b; //ide olvassunk
  byte ec; //Hibakód
  byte ea = 0x01; //EEPROM cím, ahonnan olvasunk
  while ((ec=errorHandler(readEE(ead, &b),ea))==EEREAD_NODATA); //olavsunk, amíg hibát vagy adatot nem kapunk vissza
  if (ec==EERAD_OK) //Ha sikeres az olvasás,
    Serial.print("Olvasott bájt: 0x%02x\n", b); //akkor kiírjuk

  //Soros Port lezárása
  Serial.end();
  
}

loop () {}



Üdv.
NorMen

Avatar
macsek
Bitmanipulátor
Hozzászólások: 121
Csatlakozott: 2008. december 4. csütörtök, 7:00
Tartózkodási hely: Bp

Re: EEPROM írás vagy olvasás működő program

HozzászólásSzerző: macsek » 2012. szeptember 3. hétfő, 12:58

Köszi az ötletet.
Tetszik a programod, tömörebben írtad le, mint én :)

normen írta:Esetleg olvasásnál a felolvasott bájtot egy paraméterként kapott memóriacímre írhatnád, míg a függvényed az olvasási művelet eredményességével térhetne vissza, pl:


Igen, hasonlóra én is gondoltam, csak en fordítva: a hibakód ment volna máshová és a beolvasott érték meg visszatérési értéknek:
macsek írta:// lehetne egy globalis valtozoval jelezni vagy egy cim szerint atadott parameterben


normen írta:
  if( !Wire.write(cim>>8 ) || !Wire.write(cim&0xff) )
    return EEREAD_ADDRESS_ERROR;


Ezzel kapcsolatban előjön a (bocs, nem tudom magyarul) "short circuiting" és a kiértékelési sorrend kérdése. (Te jól használtad, csak nem kezdő tudásszint :))
A C99 szabvány írta:6.5.14 Logical OR operator

(4) Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares unequal to 0, the second operand is not evaluated.

Tehát magyarul: a logikai vagy művelet ( || ) biztosítja, h balról jobbra történjen a kiértékelés. Ha az első tag nem nulla (értsd: IGAZ), akkor a második (illetve további) tag(ok) nem értékelődnek ki (hisz az eredményt nem befolyásolják, az már IGAZ)

Avatar
normen
DrótVégénSzéndarab
Hozzászólások: 26
Csatlakozott: 2012. május 27. vasárnap, 6:00

Re: EEPROM írás vagy olvasás működő program

HozzászólásSzerző: normen » 2012. szeptember 3. hétfő, 15:19

macsek írta:Igen, hasonlóra én is gondoltam, csak en fordítva: a hibakód ment volna máshová és a beolvasott érték meg visszatérési értéknek:
macsek írta:// lehetne egy globalis valtozoval jelezni vagy egy cim szerint atadott parameterben



Szerintem globális változó mindenféleképpen felejtős. Elég bonyodalmakat okozhat, ha valahol nem nullázod ki, vagy véletlen módosítod az értékét kiértékelés előtt.

macsek írta:
normen írta:
  if( !Wire.write(cim>>8 ) || !Wire.write(cim&0xff) )
    return EEREAD_ADDRESS_ERROR;


Ezzel kapcsolatban előjön a (bocs, nem tudom magyarul) "short circuiting" és a kiértékelési sorrend kérdése. (Te jól használtad, csak nem kezdő tudásszint :))
A C99 szabvány írta:6.5.14 Logical OR operator

(4) Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares unequal to 0, the second operand is not evaluated.

Tehát magyarul: a logikai vagy művelet ( || ) biztosítja, h balról jobbra történjen a kiértékelés. Ha az első tag nem nulla (értsd: IGAZ), akkor a második (illetve további) tag(ok) nem értékelődnek ki (hisz az eredményt nem befolyásolják, az már IGAZ)


Na kezdünk egyre mélyebbre menni a témában, amit itt nem feltétlen fog mindenki érteni/kedvelni :D

Az alábbi kifejezés:
normen írta: if( !Wire.write(cim>>8 ) || !Wire.write(cim&0xff) )


eléggé összetett. Egyfelől áll 2db unáris (egyoperandusú) műveletből (negálás, NOT), melyek utána egy kétoperandusú művelettel (OR) vannak kiértékelve.

Unáris műveletek mindig jobbról balra értékelődnek ki. Tehát, először lefut a Wire.write() függvényhívás, majd annak visszatérési értékét negáljuk (!).

A kétoperandusú műveletek esetén, ha nem értékadással egybekötött, akkor mindig balról jobbra kerül kirtékelésre, tehát a fenti kód a következőképpen hajtódik végre:
1. Wirte.write(cim >> 8)
2. első lépés negálása (!)

Itt jön képbe a Rövidzár (Short Circruit), ha 2. lépés nem igaz, akkor a feltétel tovább nem értékelődik ki. Ha viszont igaz:

3. com&0xff
4. Wire.write( 3. lépés eredménye)
5. 4. lépés eredményének negálása (!)
6. feltétel kiértékelése


Ha kétoperendusú (szándékosan nem binárist írok) művelet értékadásal egybekötött, akkor jobbról balra kerül kiértékelésre (az értékadás miatt). Erre is van példa a fenti kódomban, na ki találja meg? :)

A Rövidzár (Short Circuit) fogalma nem a kiértékelés sorrendjét határozza meg (ez a művelet típusától függ), hanem azt, hogyha az első feltétel nem teljesül, akkor a többit már nem is vizsgálja (ellenben a függvényhívási kiértékeléssel). Short Circuit alkalmazásának szerintem a futószallagú (és szuperskalár) processzorok megjelenésével lett nagy jelentősége spekulatív elágazások kezelésénél. Minnél tovább értékelsz ki egy feltételt, annál több előfeldolgozott utasítást kell visszagörgetni egy téves elágazásbecslés esetén.

Üdv.
NorMen

Avatar
normen
DrótVégénSzéndarab
Hozzászólások: 26
Csatlakozott: 2012. május 27. vasárnap, 6:00

Re: EEPROM írás vagy olvasás működő program

HozzászólásSzerző: normen » 2012. szeptember 4. kedd, 8:31

Short Circuit alkalmazásának szerintem a futószallagú (és szuperskalár) processzorok megjelenésével lett nagy jelentősége spekulatív elágazások kezelésénél. Minnél tovább értékelsz ki egy feltételt, annál több előfeldolgozott utasítást kell visszagörgetni egy téves elágazásbecslés esetén.


Tegnapi postom elég architektúra szintű volt, ezért kiegészítem, hogy programozási szempontból miért fontos a short-circuit fogalma:

1. Feltételes elágazások vizsgálatakor (amennyiben a összetett feltételekből áll) fölösleges lépésektől mentessítjük a processzort, ezzel gyorsulhat a programunk, mert:

- AND esetén a kiértékelésben balról jobbra haladva, ha valamelyik feltételünk nem teljesül a többi már nem kerül kiértékelésre.
- OR esetén balról jobbra haladva, ha valamelyik feltételünk igaz, akkor a többi már nem kerül kiértékelésre

2. Short-Circuit-nak köszönhetően elkerülhetjük a "Null Pointer Derefencing" hibajelenséget, ami akkor keletkezik, amikor egy érvénytelen memória címre hivatkozunk:

Abszurd rossz példa:

Kód: Egész kijelölése

  int *x=NULL;  //x pointer nem mutat sehova
  if (*x==5) {    //Programunk itt elszáll, mert érvénytelen memória címre mutatunk
    //program kód
  }


Fenti példa helyesen, Short-Circuit-nak köszönhetően:

Kód: Egész kijelölése

  int *x=NULL;
  if (x && x==5) { //Nem száll el, mert ha x érvénytelen címre mutat, akkor x==5 ki sem értékelődik
    //program kód
  }


Üdv.
NorMen

Avatar
SzLacus
Tranzisztorgyógyász
Hozzászólások: 175
Csatlakozott: 2012. május 20. vasárnap, 6:00
Tartózkodási hely: Budapest

HozzászólásSzerző: SzLacus » 2012. szeptember 5. szerda, 15:57

Sziasztok.

Hozzatok képbe légyszíves. Próbálom elkapni a fonalat, de nem annyira sikerül. Már rögtön az elején van némi gondom.
b = read_eeprom(TESZT_CIM);
Serial.print("olvasott bajt:");
Serial.println(b);

valasz = write_eeprom(TESZT_CIM, b+1);
if(valasz)
{
Serial.print("IRASHIBA!! :");
Serial.println(valasz);
}

Azt értem, pontosabban kikövetkeztettem, hogy a write_eeprom() azon kívül, hogy oda ír ahová a cím mutat, a művelet sikerességének függvényében visszaad valamit, amit mi kiértékelünk, és ha hibát jelet, akkor jajgatunk, hogy "IRÁSHIBA". De honnan lehet azt megtudni, hogy visszavárhatunk ilyen infót ettől a függvénytől. A kérdésem messzebb vezet, honnan lehet megtudni, hogy milyen funkciókat lehet meghívni egy könyvtárból. Látom, az arduino\libraries alatti könyvtárakban, hogy mindenhol van egy "H" fájl, meg egy forrásfájl, és még egy "keywords" is lapul ott, ami némi útbaigazítást ad. De az a tudatlanságom okán nekem nem derül ki belőle, hogy vissza is fog adni valamit. És hogy azt hogyan kell értelmezni. Mert el tudok képzelni olyat, hogy nem csak sikerült, vagy sem állapota lehet egy dolognak.
Az is érdekelne, ha valaki tud segíteni, hogy hol lehet meglátni azt a mélységű programot, ami az SCL SDA lábakat az adott ütemben rángatja. Mert az IIC működése az én agyamban most úgy néz ki, hogy elindítunk egy órajelet a mesterből, és az adatvonalat rángatva kiszólunk a buszra valakinek, esetleg többeknek is. Aztán, ebbe a rángatásba néha a szolga bele nyugtáz, és ha választ is várunk, akkor a kérdés kiküldését követően ketyegtetjük az órajelet, aminek ütemében a szolga rángatja az adatvonalat, így beküldve a választ. Ebbe az adatfolyamba amikor kell, akkor a mester bele- bele nyugtáz, és a végén lezárjuk a diskurzust. Hol van az a mélységű kód, ahol ezt látni lehet. Tudom, hogy ez nem az én kezdő szintem, mert még egy for ciklust ne írtam le életemben az előtt, hogy ebbe az arduinó tanfolyamba bele kezdtem volna, de érdekel, mint ahogy az is érdekel, és itt Macsekra számítok, hogy mi volt az előző kódod effektív hibája. Azt látom, hogy átírtad, de az nem látom, hogy miért nem működött korrekten az előzőben a bájtonkénti írás olvasás teszt. A Robi által felfedett (i) globál/lokál volt a megfejtés? Mert úgy gondolom, hogy a buffer hossz (aminek a nyomozásába komoly energiát fektettél) az nem érintette ezt a dolgot, mert mindig csak egy bájtot akartál olvasni, és írni. Én arra tippeltem volna, hogy olyan gyorsan fordulsz újból az eszközhöz, hogy néha még el van magával foglalva. Mert ha jól emlékszem az olvasás és a vissza írás, között nem volt várakozás.
Elnézést, ha bugyuták a kérdések, de most még csak ilyenre futja a tudásból.

Avatar
Robert
Elektronbűvölő
Hozzászólások: 9908
Csatlakozott: 2005. december 9. péntek, 7:00
Tartózkodási hely: Budapest
Kapcsolat:

HozzászólásSzerző: Robert » 2012. szeptember 5. szerda, 16:07

Lépésenként!
Ne ugrálj sokat előre.

A for ciklus nélkül - csak szivatóág van. Bár van aki a kihívásokat keresi:).
Az Arduino belsű föüggvényei több helyen vannak: core* és a library-kban. A függvényekre itt rákereshetsz. A függvény_készítője_ dönti el, hogy van e visszatérési érték... És ezt dokumentálja valahol... Ránézésre nem megy, hacsak....


Kérdés: leckékben merre jársz?
Van egy komplett témakör ami ezt vesézi ki. Void : nincs visszatérési érték. Egyéb: van.

A fonal elkapása a leckék egymásra épülésében van!
A fórumban igyekszem úgy terelni mindenkit, hogy _NE_ haladó leckéket írjon ide.... Vagy tessék magyarázni. Ekkor viszont a fórum _első_ bejegyzésétől kell adott témát végigjárni....

Avatar
macsek
Bitmanipulátor
Hozzászólások: 121
Csatlakozott: 2008. december 4. csütörtök, 7:00
Tartózkodási hely: Bp

HozzászólásSzerző: macsek » 2012. szeptember 5. szerda, 20:36

SzLacus írta:Azt értem, pontosabban kikövetkeztettem, hogy a write_eeprom() azon kívül, hogy oda ír ahová a cím mutat, a művelet sikerességének függvényében visszaad valamit, amit mi kiértékelünk, és ha hibát jelet, akkor jajgatunk, hogy "IRÁSHIBA". De honnan lehet azt megtudni, hogy visszavárhatunk ilyen infót ettől a függvénytől.

Nem muszáj kódbogarászásba fognod, ezek mind hivatalosan dokumentált, könnyen hozzáférhető információk.
Mióta tart a tanfolyam a kezdő könyvjelzőim között van az Arduino referencia. (Remélem tudsz angolul, sokat segít. Ha nem: az Arduino referencia MAGYARUL topikban leírtam, hol lehet ilyet találni)


SzLacus írta:A kérdésem messzebb vezet, honnan lehet megtudni, hogy milyen funkciókat lehet meghívni egy könyvtárból.

Ugyaninnen. Illetve innen egy kattintásnyira, a Reference Language | Libraries | Comparison | Changes sorban az általam kiemelt Libraries alatt.

SzLacus írta:Látom, az arduino\libraries alatti könyvtárakban, hogy mindenhol van egy "H" fájl, meg egy forrásfájl, és még egy "keywords" is lapul ott, ami némi útbaigazítást ad. De az a tudatlanságom okán nekem nem derül ki belőle, hogy vissza is fog adni valamit. És hogy azt hogyan kell értelmezni. Mert el tudok képzelni olyat, hogy nem csak sikerült, vagy sem állapota lehet egy dolognak.

Sokat lehet tanulni mások jól működő kódjainak bogarászásából. Jó helyen nézted, leginkább a .cpp , .c kiterjesztésű fájlok érdekesek, meg a .h végűek. (A keywords fájl szerintem nem igazán használható ember számára)
De ez csak adalék, a hivatalos doksi ott van, ahol az imént mutattam.

És azok kedvéért, akik vannak olyan lelkesek és elolvasnak minden hozzászólást, mutatok egy használható újdonságot, amit szintén itt, a Changes fülre kattintva lehet olvasni:

"Changes in Arduino 1.0.1
Added INPUT_PULLUP argument to pinMode() function. The INPUT mode now explicitly disables the pullup resistors."


SzLacus írta:Az is érdekelne, ha valaki tud segíteni, hogy hol lehet meglátni azt a mélységű programot, ami az SCL SDA lábakat az adott ütemben rángatja.

Az előbb leírtak alapján a Wire.cpp-ben, ami alapesetben a c:\Program Files\Arduino\arduino-1.0.1\libraries\Wire\Wire.cpp helyen lakik. Ami viszont hivatkozik a twi.c fájl függvényeire (c:\Program Files\Arduino\arduino-1.0.1\libraries\Wire\utility\twi.c) Ezek nagyon szépen olvasható, kommentezett programok, tanítani lehetne őket. Viszont ha a lábrángatást keresnéd benne akkor előre szólok: nincs benne ilyen. Az ATMEGA328 (vagy 8 vagy 168) hardverből kezeli az I2C (TWI) csatlakozást, csak fel kell tölteni a megfelelő regisztereket és beállítani az üzemmód-biteket és megy "magától" (persze azért az a c++ meg az a c program sem véletlenül van ott mellette segíteni 8) )


SzLacus írta: Mert az IIC működése az én agyamban most úgy néz ki, hogy elindítunk egy órajelet a mesterből, és az adatvonalat rángatva kiszólunk a buszra valakinek, esetleg többeknek is. Aztán, ebbe a rángatásba néha a szolga bele nyugtáz, és ha választ is várunk, akkor a kérdés kiküldését követően ketyegtetjük az órajelet, aminek ütemében a szolga rángatja az adatvonalat, így beküldve a választ. Ebbe az adatfolyamba amikor kell, akkor a mester bele- bele nyugtáz, és a végén lezárjuk a diskurzust.

Tökéletesen érted, tényleg ez a helyzet. Ha valamiért nem tiszta minden akkor olvasd el sorban a tanfolyam által hivatkozott i2c doksikat és megérted. Vagy kérdezz itt, de konkrétat ;)

SzLacus írta:Hol van az a mélységű kód, ahol ezt látni lehet.

Tényleg úgy érzed ezek után is, hogy szükséged van rá? Akkor az Arduino játszótéren olvashatsz mélyebben erről (is)

SzLacus írta: Tudom, hogy ez nem az én kezdő szintem, mert még egy for ciklust ne írtam le életemben az előtt, hogy ebbe az arduinó tanfolyamba bele kezdtem volna, de érdekel, mint ahogy az is érdekel, és itt Macsekra számítok, hogy mi volt az előző kódod effektív hibája

Hát visszaadtad a hitemet, úgy gondoltam már senki sem fogja megkérdezni. (Mint ahogy az sem érdekelt senkit, hogy a grafikus kivezérlésjelző programom nem 16 szintet jelez ki, hanem 80-at, pedig 16 karakter széles az LCD)

SzLacus írta:Azt látom, hogy átírtad, de az nem látom, hogy miért nem működött korrekten az előzőben a bájtonkénti írás olvasás teszt. A Robi által felfedett (i) globál/lokál volt a megfejtés?

Nem. Amúgy nincs is a programban globál változóm.
Talán arra gondolhatott Robi, h vannak csak a for ciklusban élő változóim ( for(int i=0; i<10; i++) ) meg vannak adott függvényen belüli lokális változóim. Viszont ezek nem keveredtek.

SzLacus írta: Mert úgy gondolom, hogy a buffer hossz (aminek a nyomozásába komoly energiát fektettél) az nem érintette ezt a dolgot, mert mindig csak egy bájtot akartál olvasni, és írni.

Igen, annak nincs köze hozzá. Az a blokkos írás miatt volt érdekes, tehát, hogy legföljebb mennyi adatot adhatunk oda a Wire.print-nek mielőtt Wire.endTransmission() következik.

SzLacus írta:Én arra tippeltem volna, hogy olyan gyorsan fordulsz újból az eszközhöz, hogy néha még el van magával foglalva. Mert ha jól emlékszem az olvasás és a vissza írás, között nem volt várakozás.

Olvasás után nem kell várni. Az író rutin végén meg várok 5ms-et. Az előző olvasás meg nem lehet, mert reset után indul. És ott sem rögtön, mert először soros porti kiírás jön.
Ha meg ottani várakozás hiba lenne, akkor ez a kód kivárná, max 10 ms-ig:

Kód: Egész kijelölése

Wire.requestFrom(I2C_EEPROM_CIM, 1);
  for(int i=0; i<10 && !Wire.available(); i++)
    delay(1);

Szóval teljesen őszintén: nem tudom. A blokkos olvasásba később került egy hiba, hogy hiányzott a Wire.endTransmission(), de ez EBBEN a "2012. Szeptember 1. Szombat, 23:24" időbélyegű hsz-ban szereplő kódnál még jó volt.
Több órán keresztül vadásztam a hibát, aztán lefeküdtem és másnap meglett. De ezek az utólagosan beletett bogarak voltak.

SzLacus írta:Elnézést, ha bugyuták a kérdések, de most még csak ilyenre futja a tudásból.

Nincs bugyuta kérdés csak fel nem tett kérdés. :)

Avatar
SzLacus
Tranzisztorgyógyász
Hozzászólások: 175
Csatlakozott: 2012. május 20. vasárnap, 6:00
Tartózkodási hely: Budapest

HozzászólásSzerző: SzLacus » 2012. szeptember 5. szerda, 21:32

Én rájöttem, mert minden karakter 5 oszlopból áll, és te ezt ki is használtad. De hogy hogyan csináltad, azt még majd eltanulom. :-)
Visszatérve az IIC függvény tárra, meg egyáltalán az include-olt mástól kapott cuccok használatára. Olyan ez mint a legós doboz. Turkálunk benne, hátha találunk olyan darabot ami nekünk kell. Erre irányult a kérdésem, hogy ha kapok, veszek, letöltök, stb egy új legós dobozt, akkor hol találom az egyes kockáknak a tulajdonságait leíró használati utasítást. Ja és mi garantálja, hogy a kiválasztott kocka azt teszi és jól teszi amit a leírásában hirdet magáról. A választ is tudom, SEMMI. Viszont így a tanulmányok kezdetén feltétlen bizalom van a "gyári" legóban. De az a mélységű tudás messze nincs meg amivel felismerném, ha a kockának hiányzik az a sarka amit én használni szeretnék.
Na de itt még nem tartunk. Legózzunk tovább, mert legózni jó dolog. :-)

Avatar
SzLacus
Tranzisztorgyógyász
Hozzászólások: 175
Csatlakozott: 2012. május 20. vasárnap, 6:00
Tartózkodási hely: Budapest

HozzászólásSzerző: SzLacus » 2012. szeptember 5. szerda, 22:01

Ja és majd el felejtettem megköszönni az útbaigazítást. Arra kell rájöjjek, hogy a legós dobozban ott van a használati utasítás, csak azt nem vettem észre. Bár én a kétszárnyú ajtón is mindig a lezárt szárnyat szoktam rángatni és csak amikor bemennek mellettem a nyitott szárnyon, akkor jövök rá a figyelmetlenségemre, de legalább jót mulatok magamon. Ami az angolt illeti, mehetne jobban is, akkor jobban haladnék. Még egyszer köszönöm.

Avatar
glezmen
Bitmanipulátor
Hozzászólások: 139
Csatlakozott: 2012. augusztus 9. csütörtök, 6:00
Tartózkodási hely: Noszlop
Kapcsolat:

HozzászólásSzerző: glezmen » 2012. szeptember 21. péntek, 10:08

kotekedes kovetkezik, szigoruan konstruktiv szandekkal! :)

"Ilyen pontatlanság termosztátvezérlést ne bízzunk rá" -> itt szerintem kimaradt valami, pl. egy 'mellett' szo

"A bit/byte váltószámot legalább nem írták át, mint a winchesterek esetén..." -> a bit/byte valtot SOHA nem irtak at, az amiota vilag a vilag, bizony 8. amire te gondolsz, az a szorzo elotagok (kilo, mega, giga, stb) ertelmezese, ahol a 2^10 helyett tenyleg a vinyogyartok kezdek el a mindenhol mashol (IT-n kivul) is hasznalt 10^3 hasznalatat - ami azota viszont szabvany is lett, szoval a 'kilobit' az hivatalosan 1000 bitet jelent, az 1024 az 'kibibit' kene hogy legyen. persze ezt senki nem hasznalja igy a marketinganyagokon kivul :D aki egyszer megszokta hogy a kilo az 1024, az valoszinuleg (remelhetoleg) elete vegeig igy hasznalja :)

Avatar
llori
DrótVégénSzéndarab
Hozzászólások: 18
Csatlakozott: 2011. március 12. szombat, 7:00
Tartózkodási hely: Százhalombatta

Re: 60 nap alatt Arduino #30-#31 - IIC busz és az EEPROM

HozzászólásSzerző: llori » 2013. január 12. szombat, 18:07

Sziasztok!

Gyakorlás képpen megírtam a saját hőmérő / loggoló programomat.
Úgy tervezem, hogy csak akkor menti el az eepromba (később a terv SD kártyára) a hőmérséklet értéket, ha változik. Természetessen idő (relatív / abszolú most mindegy) bélyeggel együtt.
Az saját eepromba tudok írni, és olvasni is (egyenlőre csak a hőmérséklet értékeket).

De a IIC buszon lévő eepromba nem tudok írni, csak olvasni belőle, de szerintem ez is csak szerencse mert nem értem a parancsokat.
Nem értem, nem látom át, hogy melyik paraméter mondja meg, hogy melyik memória címre akarok írni, mit akarok írni, vagy honnan akarok olvasni.
Kérek egy kis segítséget!

Az eddigi program így néz ki:

Kód: Egész kijelölése

/*
TC75 hőmérő adatainak kiírása, ha v áltozik (sokat változik, így átlagolunk) az LCD-re és sériál monitorra és eepromba.

*/

#include <LiquidCrystal.h> // LCD meghívás
#include <Wire.h> // IIC busz
// #include <EEPROM.h> // az AVR saját eepromjába tesztképpen

LiquidCrystal lcd (4, 5, 6, 7, 8, 9); // LCD inicializálás

int LEDpin = 13; // LCD háttévilágításhoz
int tc75_cim = 0x48; // EXT1 panelen lévő TC75 címe
int eeprom_cim = 0x50; // EXT1 panelen lévő eeprom címe

byte data1, eep; // mért érték, data1, és eepromból visszaolvasott érték eep
int data11, j; // összehasonlító érték, és számláló
int dataTOMB[250]; // átlagoláshoz a tömb
int data_atlag; // átlag változó

void setup()
{

  Serial.begin(9600); // serial beállítás
  Wire.begin(); // IIC busz
  lcd.begin(16,2); // LCD kalibrálás
 
  pinMode(LEDpin, OUTPUT); // LCD háttérvillágításhoz kimentet állítás
  digitalWrite(LEDpin, HIGH); // LCD háttérvillágítás bekapcsolás
 
 
  lcd.clear(); // LCD törlés
  lcd.print("Homero: ");
   
 
  Serial.println("START");
  j = 1; // számláló egyről indul
 
  Serial.print("sza'mla'lo': ");
  Serial.println(j);
}

void loop()
{
  Wire.beginTransmission(tc75_cim); // TC75 lekéréshez címbeállítás
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(tc75_cim, 1); // egy byte elég most, csak egész hőmérséklettek...
  data1 = Wire.read(); // érték átadás
 
  lcd.setCursor(0, 1); // második sorra állítás
  lcd.print(data1); // hőmérsékelt kiirás az LCD-re
  Serial.print(j); // számláló kiírás
  Serial.print(" - ");
  Serial.print(data1); // hőmérséklet kiirás a serial monitorra
  Serial.print(" - ");
  Serial.print(data_atlag); // átlag érték kiírás
  Serial.print(" - ");
  Serial.print(millis()/1000); // eltelt idő kiírás mp-ben, tudjam, hogy tellik az idő
  Serial.print(" - ");
  Serial.println(eep); // vissza olvasott érték az eepromból, az első sorban mindig nulla
 
  data_atlag = 0; // átlag érték kinullázása
 
  do // hátultesztelős ciklus a változás érzékeléséhez.
  {
   
    for (int i = 0; i < 250; i++)
    {
 
      Wire.beginTransmission(tc75_cim); // TC75 lekéréshez címbeállítás
      Wire.write(0);
      Wire.endTransmission();
      Wire.requestFrom(tc75_cim, 1); // egy byte elég most, csak egész hőmérséklettek...
      dataTOMB[i] = Wire.read(); // érték átadás, tömbfeltőltés
      delay(3); // várunk kicsit
      data_atlag = data_atlag + dataTOMB[i]; // átlag összeg képzés
     
     
      }
   
   
     data_atlag = data_atlag / 250; // átlag kiszámítása
     
     
     /* egyenlőre elég az integerben képzett tizedestört nélküli átlag, matematikalilag nem helyes.
     későbbi fejlesztés itt folytatódik*/
 
    data11 = int(data1); // eredeti hőmérséklet byte-ból integer-esítés
  }while ( data11 == data_atlag ); // programozásban nem szabad egyenlőságet adni feltételként, de szerencsémre a hőmérséklet C fokonként nem változik olyan gyorsan. 2, számú fejlesztés itt
 
 
  // EEPROM.write (j, data1); // teszteltem a saját epromot, működött a visszaolvasás is!!!
  // eep = EEPROM.read (j);
 
   
  /*innen van a szívás*/
 
  Wire.beginTransmission(eeprom_cim);
  Wire.write(data1);
  // Wire.write(data1);
  Wire.endTransmission();
  // delay(10);
 
  Wire.beginTransmission(eeprom_cim);
  Wire.write(j);
  Wire.endTransmission();
  Wire.requestFrom(eeprom_cim,1);
  // eep = Wire.read();
 
  j++; // számláló eggyel több
  // if (j > 1024) { j = 1; } // az AVR belső eeprom nem csorduljon túl
  delay(100); // várunk kicsit
}


Köszönöm!
Lóri

Avatar
Robert
Elektronbűvölő
Hozzászólások: 9908
Csatlakozott: 2005. december 9. péntek, 7:00
Tartózkodási hely: Budapest
Kapcsolat:

Re: 60 nap alatt Arduino #30-#31 - IIC busz és az EEPROM

HozzászólásSzerző: Robert » 2013. január 12. szombat, 18:18

Az I2C használat byte alapon - lépései:

i2c init
i2cwrite: addressírás
i2cwrite memóriacímHigh
i2cwrite memóriacímLow
i2cwrite adatbyte
i2cvége

Olvasás:

i2c init
i2cwrite addresswrite
i2cwrite memóriacímHigh
i2cwrite memóriacímLow
i2cinit
i2creadrite adatbyte
i2cvége



Programban az olvasás (ez a bonyolultabb):

Kód: Egész kijelölése

byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
byte rdata = 0xFF;

Wire.beginTransmission(deviceaddress);
Wire.send((int)(eeaddress >> 8)); // MSB
Wire.send((int)(eeaddress & 0xFF)); // LSB
Wire.endTransmission();
Wire.requestFrom(deviceaddress,1);
  if (Wire.available()) rdata = Wire.receive();
return rdata;
}

Avatar
Robert
Elektronbűvölő
Hozzászólások: 9908
Csatlakozott: 2005. december 9. péntek, 7:00
Tartózkodási hely: Budapest
Kapcsolat:

Re: 60 nap alatt Arduino #30-#31 - IIC busz és az EEPROM

HozzászólásSzerző: Robert » 2013. január 12. szombat, 18:21

Az írásod hibás:
Nem code / code közt, látszódjon a hiba:


/*innen van a szívás*/

Wire.beginTransmission(eeprom_cim);
Wire.write(data1);
Nono: az eeprom memóriacíme 2byte-s!
// Wire.write(data1);
Wire.endTransmission();
// delay(10);

Wire.beginTransmission(eeprom_cim);
Ez minek? Nincs újraindítás íráskor! Csak olvasáskor!
Wire.write(j);
Wire.endTransmission();
Wire.requestFrom(eeprom_cim,1);
// eep = Wire.read();

j++; // számláló eggyel több
// if (j > 1024) { j = 1; } // az AVR belső eeprom nem csorduljon túl
delay(100); // várunk kicsit
}

Avatar
llori
DrótVégénSzéndarab
Hozzászólások: 18
Csatlakozott: 2011. március 12. szombat, 7:00
Tartózkodási hely: Százhalombatta

Re: 60 nap alatt Arduino #30-#31 - IIC busz és az EEPROM

HozzászólásSzerző: llori » 2013. január 12. szombat, 19:22

Köszönöm!

Sajnos nem értem azt amit írtál...
de nem adom fel

Vissza a kályhához.
Beírtam a leckében talált sorokat:

Kód: Egész kijelölése

void writeEEPROM(int deviceaddress, unsigned int
eeaddress, byte data )
{
  Wire.beginTransmission(deviceaddress);
  //Cim magas helyierteke
  Wire.send((int)(eeaddress >> 8));
  //Cim alacsony helyierteke
  Wire.send((int)(eeaddress & 0xFF));
  //Es a beirando adat
  Wire.send(data);
  Wire.endTransmission();
  delay(5);
}
 
byte readEEPROM(int deviceaddress, unsigned int
eeaddress )
{
  byte rdata = 0xFF;
  Wire.beginTransmission(deviceaddress);
  //Magas helyiertek
  Wire.send((int)(eeaddress >> 8));
  //Alacsony helyiertek
  Wire.send((int)(eeaddress & 0xFF));
  Wire.endTransmission();
  //Egy byte olvasando
  Wire.requestFrom(deviceaddress,1);
  //Ha sikerult...
  if (Wire.available()){
    rdata = Wire.receive();
  }
 
  return rdata;
}


miért adjuk a rdata = 0xFF értéket?

ártírtam, hogy értsem:

Kód: Egész kijelölése

//  void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data )

  Wire.beginTransmission(eeprom_cim);
  Wire.write((int)(j >> 8)); //Cim magas helyierteke
  Wire.write((int)(j & 0xFF)); //Cim alacsony helyierteke
  Wire.write(data1); //Es a beirando adat
  Wire.endTransmission();

 // byte readEEPROM(int deviceaddress, unsigned int eeaddress )

 
  Wire.beginTransmission(eeprom_cim);
  Wire.write((int)(j >> 8)); //Magas helyiertek
  Wire.write((int)(j & 0xFF));   //Alacsony helyiertek
  Wire.endTransmission();
 
  Wire.requestFrom(eeprom_cim,1); //Egy byte olvasando
 
  eep = Wire.read();
 
  ;


De nem működik.
A visszatérő értéka az eep változónak 255.

Valami rossz a címben? j változó?


Vissza: “60 nap alatt Arduino - Házi feladatok”

Ki van itt

Jelenlévő fórumozók: nincs regisztrált felhasználó valamint 1 vendég