Nahrání dat do EEPROM hromadne

Wiring, C++, C, Java, ...
Pravidla fóra
Toto subfórum slouží k řešení obecných otázek kolem programování (konstrukce, knihovny, alokace paměti, ...)
soen
Příspěvky: 7
Registrován: 21 bře 2024, 15:25

Nahrání dat do EEPROM hromadne

Příspěvek od soen » 22 bře 2024, 02:59

Ahoj, potřebuji poradit s hromadným nahráním dat na externí EEPROM přes I2c pomocí Arduina.
Mám v textovém souboru popsaná hex data ve formátu
01 02 03 04 05 06 07 08 09 0a 0b 0c ... prostě reprezentaci bytů oddělenou mezerami.
Tenhle blok potřebuji nahrát od určité adresy do EEPROM. Mohu ho poslat přes sériovou linku, ale pak potřebuji byty převzít jako byty, ne jako znaky nula jedna mezera nula dva mezera... a vymazat mezery.
Dokážu zapisovat jeden byte na určitou adresu, ale jak to provést s takovým velikým blokem, který má desítky kB, na to se mi nedaří přijít.
Díky za rady.

Uživatelský avatar
gilhad
Příspěvky: 825
Registrován: 07 bře 2018, 11:22

Re: Nahrání dat do EEPROM hromadne

Příspěvek od gilhad » 22 bře 2024, 03:19

Prostě ho zapíšeš jako sérii menších bloků, které eeprom zvládne.

Většinou to mají popsané v datasheetu, například tady https://ww1.microchip.com/downloads/en/ ... 21189f.pdf a taky mají popsanou největší délku bloku.

Navíc se všude po netu válí mraky návodů, jak to udělat přez arduino, například tady https://navody.dratek.cz/navody-k-prod ... 4c256.html

soen
Příspěvky: 7
Registrován: 21 bře 2024, 15:25

Re: Nahrání dat do EEPROM hromadne

Příspěvek od soen » 22 bře 2024, 06:26

To ano. Ale jak ten soubor přečtu ze sériové linky po znacích, vytvořím bloky a ty pošlu do EEPROM? Návodů, kde posílají jeden znak a jeden znak čtou je opravdu mnoho. To mám vyzkoušené, funguje mi to po jednom znaku dobře. Ale nevím jak dál. Jak číst víc dat z monitoru sériové linky hromadně a jak je zpracovat k hromadnému zaslání do EEPROM.

Uživatelský avatar
gilhad
Příspěvky: 825
Registrován: 07 bře 2018, 11:22

Re: Nahrání dat do EEPROM hromadne

Příspěvek od gilhad » 22 bře 2024, 08:58

Zápis do EEPROM je třeba tady (a měl by fungovat správně)

https://github.com/PaoloP74/extEEPROM/b ... EEPROM.cpp
//Write bytes to external EEPROM.
//If the I/O would extend past the top of the EEPROM address space,
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
//from the Arduino Wire library is passed back through to the caller.
byte extEEPROM::write(unsigned long addr, byte *values, unsigned int nBytes)
Jak číst víc dat z monitoru sériové linky hromadně
Sériová linka je, už podle jména, sériová, takže znaky přenáší po jednom.

Jeden znak ze sériové linky umíš přečíst, tak si udělej nějaké pole a postupně do něj ty znaky dávej za sebe a až jich bude dost (čili buď přečteš vše, co jsi chtěl, nebo bude to pole dost plné) tak ho nech zapsat v délce, kterou jsi do něj načetl.

A samozřejmě buď mužeš ta data interpretovat při odesílání a posílat je jako byty, nebo můžeš ten textový soubor prostě poslat jako text a v arduinu přeskákat mezery a když přijde dvojice znaků, tak z ní vymyslet ten byte (já bych to asi dělal spíš takhle). A vymyslet, jak arduinu oznámíš od jaké adresy a kolik toho bude, nebo jak pozná, že už je to všechno.

Když se podíváš, jak v tom write řeší hranici stránky, tak si ji podobně můžeš řešit i u sebe, a posílat jen stránky - pak ti stačí celkem malý buffer a nepřeteče ti buffer při čekání až to ta EEPROM zapíše. Nebo si tam nahackuj nějaké řízení provozu při té komunikaci Arduino = PC, aby ti to posílalo jen po rozumných počtech naráz - například že ten soubor bude mít řádky, PC vždycky pošle řádku a počká až ji arduino potvrdí. Nebo pošle nějaký počet znaků a počká na potvrzení, nebo že si vyvedeš RTS/CTS řízení komunikace, pak to PC může chclit plnou rychlostí a arduáino si bude říkat, kdy poslouchá a kdy ne. Nebo cokoli jiného.

Možností je spousta a jen ty sám víš, jaká máš ta data, co znamenají, jaké máš arduino, kolik máš na tohle volné paměti, jakou máš EEPROM, jak je rychlá a jak to dělá ona ... a proč to vlastně celé děláš takhle a ne jinak. :D

soen
Příspěvky: 7
Registrován: 21 bře 2024, 15:25

Re: Nahrání dat do EEPROM hromadne

Příspěvek od soen » 22 bře 2024, 09:22

Děkuju moc, proberu ty informace na odkazech. Teď se peru s přijímáním dat ze sériové linky. Potřebuju nejdřív zadat ručně adresu, od které začít zapisovat, a už tohle mi nejde.

Uživatelský avatar
kiRRow
Příspěvky: 1240
Registrován: 07 kvě 2019, 07:03
Bydliště: Opava

Re: Nahrání dat do EEPROM hromadne

Příspěvek od kiRRow » 22 bře 2024, 16:39

Protože tohle už není jen o opisování tutoriálů a jejich slepováním do hromady. Zapřemýšlej, jak ten procesor a program vlastně pozná rozdíl, že zadáváš adresu kam chceš zapisovat a co jsou data, které chceš zapisovat ? Program si to nějak musí umět zjistit, tudíž si to musí i umět nějak zapamatovat a poznamenat. Takže bude třeba nějaká proměnná, kde program bude sledovat v jakém stádiu je a co znamenají ona přijatá data. Něco jako, že program se spustí s tím, že stav programu je 0, tudíž očekává, že přijatá data ze sériové linky budou znamenat adresu, kde chci ukládat - po té co tato data příjmu, uložím je do proměnné s adresou, tak posunu stav programu na 1 ... pak budu očekávat, že další data na sériové lince jsou ty, které chci uložit, naláduji si je do bufferu a na konci přenosu data zapíšu do eeprom a posunu stav programu na 2 - ten třeba bude znamenat, že mám správné data na správné adrese a cokoliv teď jde po sériové lince pracuje s těmi daty, nebo je to nějaký příkaz ovlivňující běh programu, cokoliv ...

peterple
Příspěvky: 158
Registrován: 22 zář 2021, 20:20

Re: Nahrání dat do EEPROM hromadne

Příspěvek od peterple » 22 bře 2024, 17:14

Potrebuješ skonvertovať jeden formát súboru na druhý. V prvom rade treba vedieť že ten prvý sa volá Intel hex a ten druhý je binarny.
Potom už len použiť google, takýchto konvertovacích sw je desiatky (aj pre windows aj pre linux aj online)
Keď to prevedieš na bin potom už máš čo byte to jedno miesto v EEPROM a môžeš použiť to čo poznáš.

Druhú cestu tu už popisovali. Pre Arduino sú aj kódy ktoré vedia prijať alebo prečítať z SDkarty HEX súbor a previesť ho do binárneho tvaru.

soen
Příspěvky: 7
Registrován: 21 bře 2024, 15:25

Re: Nahrání dat do EEPROM hromadne

Příspěvek od soen » 22 bře 2024, 19:25

peterple píše:
22 bře 2024, 17:14
Potrebuješ skonvertovať jeden formát súboru na druhý.
Takový převod už jsem zkoušel pomocí

Kód: Vybrat vše

xxd -r -p input.txt output.bin
Pak jsem ho pomocí cutecom poslal do Arduina, ale jen krátké úseky pro zkoušku. Ještě bych musel vyřešit rozdělení na bloky, ale spíš uvažuji, že do .bin převádět nebudu a budu načítat ze sériové linky data jako řetězce po kouscích, které nepřekročí velikost potřebného bloku. Možná to ani nebude pracnější.

Uživatelský avatar
gilhad
Příspěvky: 825
Registrován: 07 bře 2018, 11:22

Re: Nahrání dat do EEPROM hromadne

Příspěvek od gilhad » 22 bře 2024, 21:26

Naopak to bude jednodušší, protože IHEX je dobře dokumentovaný a obsahuje i tu adresu v paměti, takže to nemusíš řešit zvlášť i kontrolní součet, takže se to tak snadno nerozbije, aby to tam psalo nesmysly. A to vyhodnocování je celkem přímočasré :)
https://en.wikipedia.org/wiki/Intel_HEX

Takhle to mám já v programu, co mi vypaluje EEPROMky do 8biťáků (trojice složených závorek {{{ }}} mám v komentářích pro foldování a jsem líný je vyhazovat, ignoruj je.)
Je to psáno specificky pro AT28C256 a plně kompatibilní (AT28C128, AT28C64)

Kód: Vybrat vše

void doIhexLine() {	// {{{
	IHEX_size = parseHexValue(2);
	IHEX_addr = parseHexValue(4);
	IHEX_type = parseHexValue(2);
	
	if (IHEX_type == 01) { // EndOfFile - ignoruju
		Serial.println("END >");
		return;
	};
	if (IHEX_type != 0) { // not data - ignoruju
		Serial.println("?>");
		return;
	};
	// {{{ Data record
	uint16_t checksum = IHEX_size + (IHEX_addr >> 8) + (IHEX_addr & 0xFF) + IHEX_type;
	for (int i = 0; i < IHEX_size; i++) {
		IHEX_data[i]  = parseHexValue(2);
		checksum += IHEX_data[i];
	};
	IHEX_checksum = parseHexValue(2);
	checksum += IHEX_checksum;
	if ((checksum & 0xFF) == 0) {
		Serial.print("*");
	} else {
		Serial.print("!"); // nesedi soucet
		if (check_CRC) { // zajima me to?
			snprintf(charbuff_5, sizeof(charbuff_5), "%04X", IHEX_addr);
			Serial.println(charbuff_5);
			Serial.print(">");
			return;
		};
	};
	
	int32_t address = IHEX_addr - eeprom_offset + data_offset;  // tady delam nejake dalsi triky , staci int32_t address = IHEX_addr ;
	address &= 0xFFFF;	// Truncate to 16 bits
	for (int i = 0; i < IHEX_size; i++) {
		set_addr(address); // sem se to ma ulozit
		uint8_t data=get_data(); // prectu si, zda to tam nahodou uz neni
		if (data==IHEX_data[i]) { // a pokud je, tak to nezapisuju znova
			Serial.print("=");
			address++;
			continue;
		};
		write_data(IHEX_data[i]); // jinak to tam zapisu
		check_value(IHEX_data[i]); // a cekam az to EEPROM zpracuje a zase zacne naslouchat
		address++;
	};
	//}}}
	Serial.println(">");
}	// }}}
A tady jsou nějaké ty použité funkce - já to tam zapisuju přez manipulaci pinů, ty přez I2C, takže to budeš mít zcela jinak, ale někdy je dobré vidět, jak je to deklarované a co to asi tak vrací

Kód: Vybrat vše

int32_t parseHexValue(uint8_t max_dig) {	// {{{
	int32_t value = 0;
	char c;
	int sign = 1;
	while (!Serial.available()) {};
	if (Serial.peek() == '-') {
		c = Serial.read();
		sign = -1;
	};
	
	while (max_dig) {
		while (!Serial.available()) {};
		c = Serial.peek();
		--max_dig;
		if (isHexadecimalDigit(c)) {
			Serial.read();
			value = value * 16 + hexCharToInt(c);
			Serial.print(c);
		} else {
			break;
		}
	}
	
	return sign * value;
}	// }}}
int hexCharToInt(char c) {	// {{{
	if (c >= '0' && c <= '9') {
		return c - '0';
	} else if (c >= 'A' && c <= 'F') {
		return 10 + c - 'A';
	} else if (c >= 'a' && c <= 'f') {
		return 10 + c - 'a';
	}
	return 0;
}	// }}}
bool isHexadecimalDigit(char c) {	// {{{
	return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'));
}	// }}}
void set_addr(uint16_t address) {	// {{{ Set address pins
	for (int i = 0; i < 16-1; i++) {
		digitalWrite(pins_addr[i], (address & (1 << i)) ? HIGH : LOW);
	};
}	// }}}
void write_data(uint8_t data) {	// {{{ Set data pins
	for (int i = 0; i < 8; i++) {
		pinMode(pins_data[i], OUTPUT);
		digitalWrite(pins_data[i], (data & (1 << i)) ? HIGH : LOW);
	};
	digitalWrite(OE, HIGH);
	digitalWrite(CE, LOW);
	digitalWrite(WE, LOW);
	digitalWrite(WE, LOW);
	//
	digitalWrite(WE, HIGH);
}	// }}}
uint8_t get_data() {	// {{{ Read data pins
	digitalWrite(WE, HIGH);
	digitalWrite(CE, LOW);
	digitalWrite(OE, LOW);
	uint8_t retval=0;
	for (int i = 0; i < 8; i++) {
		pinMode(pins_data[i], INPUT);
		retval |= digitalRead(pins_data[i]) ?  (1 << i): 0;
	};
	digitalWrite(OE, HIGH);
	digitalWrite(CE, HIGH);
	return retval;
}	// }}}

void check_value(uint8_t wanted) {	// {{{ check, that reading last address gives 4 same values in line and that it is value wanted (lets check every byte and report result, giveup after many fails)
	int same=0;
	int data=0,data2;
	uint8_t fails=0;
	data=get_data();
	while (same < 4) {
		data2 = get_data();
		if ( (data == data2) && (data2 == wanted)) { 
			same++;
		} else {
			same = 0;
			data = data2;
			fails++;
			if (fails>100) {
				Serial.print("@");
				return;
			}
		};
	};
	Serial.print("#");
} 	// }}}

Zbouchal jsem to kdysi ve spěchu a podle toho to taky vypadá
- Je to poměrně jednoučelová záležitost, chyby se řeší jen lehce
- přetečení adres je v pořádku, prostě se ta EEPROM bere jako zrcadlená takže první a (poslední+1) adresy jsou plně totožné
- ty EEPROMKY byly laciné z alíka, takže asi notně ojeté, zápisy moc nefungovaly - proto nezapisuju, pokud není třeba, detekuju úspěch každého bytu a klidně to točím víckrát. Některé kusy potřebovaly i 4 přepisy, než seděly opravdu všechny byty.
- vyházel jsem blikání kontrolníma LEDkama
- je to ukecané
- je to poskládané z anglických datasheetů, tak tam mám komentáře anglicky. Pro tebe jsem místy připsal i něco česky.
- přístup k pinům je nepřímý kvůli použitému shieldu a přebírání z jiných programů - ale ty to budeš mít stejně jinak
- stejně tak ignoruj další zápisy - pro čtení či zápis je potřeba nastavit jednotlivé piny správně a případně s nima zacvičit
- některé deklarace jsem taky nekopíroval
- z loopu je to volané takto (takže úvodní dvojtečka je už sežraná)

Kód: Vybrat vše

		} else if (c == ':') {	// {{{ IHEX write
			Serial.print(":");
			doIhexLine();
		}	// }}}

soen
Příspěvky: 7
Registrován: 21 bře 2024, 15:25

Re: Nahrání dat do EEPROM hromadne

Příspěvek od soen » 23 bře 2024, 15:37

Vysvětlím k čemu to bude. Dělám zařízení, které bude mít složitější menu, bude ovládané rotačním enkodérem s tlačítkem a zobrazovat bude na 2*16LCD.
Moje idea je mít v EEPROM strukturu, kde budou uložené texty i adresy pro skok na další po dané volbě.
Třeba (po zjednodušení):

text 1. řádku, text 2. řádku, adresa co zobrazit při otočení tam, adresa co zobrazit při otočení zpět, adresa kam skočit při stisknutí.

od adresy 0 bude:

0: vyber z nabídky, _popis volby__1:, 69, 69, 138
69: vyber z nabídky, _popis volby__2:, 0, 0, 207
138: tohle je volba 1, a nic víc, 138, 138, 138
207: tohle je volba 2, - chceš nazpátek ? -, 207, 207, 0

Budu potřebovat:
na každý text 33 bytů, protože délka řádku LCD je 16 a některé znaky jsou v ASCII dvoubajtové. V případě všech dvoubajtových by to bylo 32 a pak 1, který tam dává kód sám, asi Enter, nevadí mi, beru to tak, že délka dvou řádků zabere 33 + 33 bytů. A pak ty 3 ovládací. Takže
33+33+3 = 69 bytů na 1 volbu menu.

Na LCD se vypíše:
vyber z nabídky
_popis volby__1:

Rotačním spínačem budu skákat na popis druhé volby a zpět (adresa 69 a 0), a po potvrzení skočím buď na volbu 1, tedy na adresu 138 nebo 2, což je adresa 207. Na volbě 1 tahle ukázka už zůstane (138 je u její řádky možnost pro otočení tam i zpět i pro stisknutí), u volby 2 je možnost po stisku skočit zase na začátek.

A takových řádek bude v přístroji několik desítek. Převáděl jsem pokusně část do HEX ručně, ale připadá mi teď lepší části po větách a číslech adres zadávat přes sériovou linku rovnou do Arduina a přes něj rovnou do paměti. Z té bych si pak mohl udělat dump.

Zatím zkouším následující jednoduchý, neohrabaný a nedodělaný kód, protože se učím od začátku.
Zatím umí jen přebrat znaky čísla a textu, zapsat do pole, zkontrolovat je a doplnit na délku 33.
Teď bych chtěl vypsat to pole zpět jako textovou větu pro kontrolu, pak doplnit o zadané adresy skoků a posílat do EEPROM.
Nejdřív jsem pracoval fyzicky na desce Arduina, a pak našel tuhle stránku, která mi šetří hodně času při kompilaci a uploadu a taky flash Atmegy :-)
https://wokwi.com/projects/new/arduino-uno

Pořád hledám nejelegantnější řešení.

Kód: Vybrat vše

unsigned int vlozenyByte = 0;
unsigned int CisloAdresy = 0;
int vlozenyTextPole[34]; // to je pozice 0 az 33, nultou nepouziji (nebo později jinak)
unsigned int pocetBytuRetezce = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Vlož číslo adresy v DEC a stiskni Enter");
}

void loop() {
    if (Serial.available() > 0)
    {
      if (CisloAdresy==0)
      {
        CisloAdresy = Serial.parseInt();
        Serial.print("Zadal jsi číslo adresy: ");
        Serial.println(CisloAdresy, DEC);
        
        if(CisloAdresy==0)
          {
          Serial.println("Vlož číslo adresy znovu:");
          }
        else
          {
          Serial.println("Teď vlož text a stiskni Enter");
          }
      }
      
      else
      {
        vlozenyByte = Serial.read();
        // načítat tak dlouho, až se objeví znak enteru (10), do té doby spojovat do jednoho řetězce a ten pak najednou vypsat
        
      if(vlozenyByte==10)
      {
        Serial.println("Vložil jsi ENTER");
          if (pocetBytuRetezce==33)
          {
            Serial.println("Počet Bytů vloženého řetězce je 33, což je správně");
          }
          else if (pocetBytuRetezce<33)
          {
            Serial.println("Počet Bytů vloženého řetězce je " + (String)(pocetBytuRetezce) + ", zbytek do 33 zůstává 0");

            for (int i = 1; i <= 32; i++)
            {
              Serial.print(vlozenyTextPole[i]);
              Serial.print(";");
            }
          }
          else
          {
          Serial.println("Počet Bytů vloženého řetězce je > 33, vlož znovu");
          }

        Serial.println("\n- - - - - ");

        // vymazani promenne a pole
          pocetBytuRetezce=0;
          for (int i = 0; i <= 33; i++)
            {
              vlozenyTextPole[i]=0;
            }
        // /vymazani promenne a pole
        
      }
      else
        {
          pocetBytuRetezce++;
          vlozenyTextPole[pocetBytuRetezce]=vlozenyByte;
        }
      }
    }
    else
    {

    }

}

Odpovědět

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 1 host