Zápis a čtení na EEPROM ATmega32A

Ahoj, obracím se na vás s otázkou ohledně zápisu a čtení z EEPROM. Potřebuji do ní na začátku programu uložit sekvenci čísel, kterou budu číst a případně dále upravovat při jeho běhu. Problém je, že se už druhý den pokouším najít nějaké informace ohledně nastavení EEARL a EEARH. Všude se odkazují na datasheet a ten jen říká, že se potřeba registry správně nastavit. Zkusil jsem snad vše možné a nemůžu se pohnout z místa. Do Atmel Studia jsem si překopíroval vzorový kód a pokoušel se mít odkazy na dvě rozdílné čtecí a zapisovací procedury pro vyloučení chyb. Vždy dojde k načtení z paměti poslední uložené hodnoty a nezáleží, že mám v každé zapisovací rutině jiné adresy. Už jsem naprostou zoufalý, protože nemůžu nikde nic najít. Velice bych ocenil osvětlení, případně nějakou pomoc. Díky

V datasheetu je to popsané jasně a srozumitelně. Dej sem kód a zkusíme najít chybu.

úspešne používam niečo takéto (úryvok z rozsiahlejšieho cylu):


#define DATA_READ 0
#define DATA_WRITE 1
#define DATA_COMPARE 2

// praca s EEPROM
#define EEPROM_ADR_REGISTER EEAR
#define EEPROM_MAX_ADR E2END
#define EEPROM_DATA_REGISTER EEDR
#define EEPROM_POVOL_PRERUSENIE SET(EECR,EERIE)
#define EEPROM_ZAKAZ_PRERUSENIE RES(EECR,EERIE)

#define EEPROM_WR_BYTE SET(EECR,EEMWE); \
                      SET(EECR,EEWE)
						  
#define EEPROM_RD_BYTE SET(EECR,EERE)
#define EEPROM_BUSY TST(EECR,EEWE)
#define EEPROM_MASKA_ERASE_WRITE (EECR &= 0x0f)

// DATA_READ, DATA_WRITE, DATA_COMPARE
uint8_t fn_int_rd_wr_eeprom(uint8_t prikaz, uint16_t *p_ciel, uint16_t index, uint8_t pocet)
{

volatile LOCAL uint16_t data_eep[DATA_EEP_MAX] EEMEM;

volatile LOCAL uint8_t beh_programu = FALSE;
uint16_t eep_number;
uint8_t *p_pointer_eep_eep, *p_pointer_eep_ram;

if (beh_programu == TRUE) return(FALSE);
if (index >= DATA_EEP_MAX) return(FALSE);
beh_programu = TRUE;

// ******************** SPRACOVANIE PAMATOVEJ OBLASTI **************************************
if ((index + pocet) >= DATA_EEP_MAX) {
pocet = (uint8_t)(DATA_EEP_MAX - index);
}
eep_number = 2 * pocet;
p_pointer_eep_eep = (uint8_t *)&data_eep[index];
p_pointer_eep_ram = (uint8_t *)p_ciel;

// ******************** SPRACOVANIE PRIKAZU **************************************
switch (prikaz) {

	case DATA_READ : {
		while (eep_number) {
			// cakaj na pripadne uvolnenie prace s EEPROM
			while(EEPROM_BUSY);
			EEPROM_ADR_REGISTER = (uint16_t)p_pointer_eep_eep;
			EEPROM_RD_BYTE;
			*p_pointer_eep_ram = EEPROM_DATA_REGISTER;
			p_pointer_eep_eep++;
			p_pointer_eep_ram++;
			eep_number--;
		}
		break;
	}
	case DATA_COMPARE : {
		while (eep_number) {
			// cakaj na pripadne uvolnenie prace s EEPROM
			while(EEPROM_BUSY);
			EEPROM_ADR_REGISTER = (uint16_t)p_pointer_eep_eep;
			EEPROM_RD_BYTE;
			if (*p_pointer_eep_ram != EEPROM_DATA_REGISTER) return(FALSE);
			p_pointer_eep_eep++;
			p_pointer_eep_ram++;
			eep_number--;
		}
		break;
	}
	case DATA_WRITE : {
		while (eep_number) {
			while(EEPROM_BUSY);
			EEPROM_ADR_REGISTER = (uint16_t)p_pointer_eep_eep;
			EEPROM_DATA_REGISTER = (uint8_t)*p_pointer_eep_ram;
			EEPROM_WR_BYTE;
			p_pointer_eep_ram++;
			p_pointer_eep_eep++;
			eep_number--;
		}
		break;
	}
}	
beh_programu = FALSE;
return(TRUE);

}


v zásade je to okopčené z datasheetu a fungovalo to na prvý šum.
Pravdepodobne robíš nejakú elementárnu chybu. Ale to nie si sám :slight_smile:

Asi jsem nezmínil, že pracuji v assembleru. Datasheet jsem pročetl několikrát a stejně mi není jasné to nastavení EEARL/H.

	.INCLUDE "m32Adef.inc"
	.org 0
	.def EEPROM = r16
	ldi r18,2
	ldi r20,4	
main:
	cpi r30,1
	breq EEPROM_WRITE
	cpi r30,2
	breq EEPROM_WRITE2
	cpi r30,3
	breq EEPROM_READ	
	cpi r30,4
	breq EEPROM_READ2
	rjmp main

EEPROM_WRITE:				
	SBIC EECR, EEWE			
	RJMP EEPROM_WRITE		

	LDI EEPROM,0x00
	OUT EEARH,EEPROM		
	LDI EEPROM,0x17
	OUT EEARL,EEPROM

	OUT EEDR, r18			
	SBI EECR, EEMWE			
	SBI EECR, EEWE			
	rjmp main

EEPROM_WRITE2:
	SBIC EECR, EEWE			
	RJMP EEPROM_WRITE2	

	LDI EEPROM,0x18
	OUT EEARL,EEPROM

	OUT EEDR, r20		
	SBI EECR, EEMWE			
	SBI EECR, EEWE			
	rjmp main				

EEPROM_READ:				
	SBIC EECR, EERE			
	RJMP EEPROM_READ		  

	CLR EEPROM
	OUT EEARH, EEPROM
	LDI EEPROM, 0x17
	OUT EEARL, EEPROM

	SBI EECR,EERE			
	IN r19, EEDR			
	rjmp main

EEPROM_READ2:
	SBIC EECR, EERE			
	RJMP EEPROM_READ2		  

	LDI EEPROM, 0x18
	OUT EEARL, EEPROM

	SBI EECR,EERE			
	IN r21, EEDR		
	rjmp main

EEAR je adresný register. Dvojbajtový. Horný bajt je EEARH a dolný bajt je EEARL. Tam nie je čo špeci nastavovať.

Pre začiatok nastav EEARH = 0
A meň adresu iba v EEARL. Napríklad EEARL = 12.
Potom tam niečo zapíš, vypni zapni zaraidenie a to “niečo” načítaj.

Toť vsjo :slight_smile:

A netuším, či Ti to pomôže, ale ASM preklad toho môjho C-čka vyzerá nasledovne:

// DATA_READ, DATA_WRITE, DATA_COMPARE
uint8_t fn_int_rd_wr_eeprom(uint8_t prikaz, uint16_t *p_ciel, uint16_t index, uint8_t pocet)
{
2716: fb 01 movw r30, r22
volatile LOCAL uint8_t beh_programu = FALSE;
uint16_t eep_number;
uint8_t *p_pointer_eep_eep, *p_pointer_eep_ram;

if (beh_programu == TRUE) return(FALSE);
2718:	90 91 c4 01 	lds	r25, 0x01C4	; 0x8001c4 <beh_programu.2465>
271c:	9f 3f       	cpi	r25, 0xFF	; 255
271e:	09 f4       	brne	.+2      	; 0x2722 <fn_int_rd_wr_eeprom+0xc>
2720:	5a c0       	rjmp	.+180    	; 0x27d6 <fn_int_rd_wr_eeprom+0xc0>
if (index >= DATA_EEP_MAX) return(FALSE);
2722:	40 34       	cpi	r20, 0x40	; 64
2724:	51 05       	cpc	r21, r1
2726:	08 f0       	brcs	.+2      	; 0x272a <fn_int_rd_wr_eeprom+0x14>
2728:	58 c0       	rjmp	.+176    	; 0x27da <fn_int_rd_wr_eeprom+0xc4>
beh_programu = TRUE;
272a:	9f ef       	ldi	r25, 0xFF	; 255
272c:	90 93 c4 01 	sts	0x01C4, r25	; 0x8001c4 <beh_programu.2465>

// ******************** SPRACOVANIE PAMATOVEJ OBLASTI **************************************
if ((index + pocet) >= DATA_EEP_MAX) {
2730: ba 01 movw r22, r20
2732: 62 0f add r22, r18
2734: 71 1d adc r23, r1
2736: 60 34 cpi r22, 0x40 ; 64
2738: 71 05 cpc r23, r1
273a: 10 f0 brcs .+4 ; 0x2740 <fn_int_rd_wr_eeprom+0x2a>
pocet = (uint8_t)(DATA_EEP_MAX - index);
273c: 20 e4 ldi r18, 0x40 ; 64
273e: 24 1b sub r18, r20
}
eep_number = 2 * pocet;
2740: 30 e0 ldi r19, 0x00 ; 0
2742: 22 0f add r18, r18
2744: 33 1f adc r19, r19
p_pointer_eep_eep = (uint8_t *)&data_eep[index];
2746: 44 0f add r20, r20
2748: 55 1f adc r21, r21
274a: 40 50 subi r20, 0x00 ; 0
274c: 50 40 sbci r21, 0x00 ; 0
p_pointer_eep_ram = (uint8_t *)p_ciel;

// ******************** SPRACOVANIE PRIKAZU **************************************
switch (prikaz) {
274e: 81 30 cpi r24, 0x01 ; 1
2750: 39 f0 breq .+14 ; 0x2760 <fn_int_rd_wr_eeprom+0x4a>
2752: 50 f0 brcs .+20 ; 0x2768 <fn_int_rd_wr_eeprom+0x52>
2754: 82 30 cpi r24, 0x02 ; 2
2756: d9 f5 brne .+118 ; 0x27ce <fn_int_rd_wr_eeprom+0xb8>
eep_number–;
}
break;
}
case DATA_COMPARE : {
while (eep_number) {
2758: 21 15 cp r18, r1
275a: 31 05 cpc r19, r1
275c: b9 f4 brne .+46 ; 0x278c <fn_int_rd_wr_eeprom+0x76>
275e: 37 c0 rjmp .+110 ; 0x27ce <fn_int_rd_wr_eeprom+0xb8>
eep_number–;
}
break;
}
case DATA_WRITE : {
while (eep_number) {
2760: 21 15 cp r18, r1
2762: 31 05 cpc r19, r1
2764: 29 f5 brne .+74 ; 0x27b0 <fn_int_rd_wr_eeprom+0x9a>
2766: 33 c0 rjmp .+102 ; 0x27ce <fn_int_rd_wr_eeprom+0xb8>

// ******************** SPRACOVANIE PRIKAZU **************************************
switch (prikaz) {

	case DATA_READ : {
		while (eep_number) {
2768:	21 15       	cp	r18, r1
276a:	31 05       	cpc	r19, r1
276c:	81 f1       	breq	.+96     	; 0x27ce <fn_int_rd_wr_eeprom+0xb8>
276e:	24 0f       	add	r18, r20
2770:	35 1f       	adc	r19, r21
			// cakaj na pripadne uvolnenie prace s EEPROM
			while(EEPROM_BUSY);
2772:	f9 99       	sbic	0x1f, 1	; 31
2774:	fe cf       	rjmp	.-4      	; 0x2772 <fn_int_rd_wr_eeprom+0x5c>
			EEPROM_ADR_REGISTER = (uint16_t)p_pointer_eep_eep;
2776:	52 bd       	out	0x22, r21	; 34
2778:	41 bd       	out	0x21, r20	; 33
			EEPROM_RD_BYTE;
277a:	f8 9a       	sbi	0x1f, 0	; 31
			*p_pointer_eep_ram = EEPROM_DATA_REGISTER;
277c:	80 b5       	in	r24, 0x20	; 32
277e:	81 93       	st	Z+, r24
			p_pointer_eep_eep++;
2780:	4f 5f       	subi	r20, 0xFF	; 255
2782:	5f 4f       	sbci	r21, 0xFF	; 255

// ******************** SPRACOVANIE PRIKAZU **************************************
switch (prikaz) {

	case DATA_READ : {
		while (eep_number) {
2784:	42 17       	cp	r20, r18
2786:	53 07       	cpc	r21, r19
2788:	a1 f7       	brne	.-24     	; 0x2772 <fn_int_rd_wr_eeprom+0x5c>
278a:	21 c0       	rjmp	.+66     	; 0x27ce <fn_int_rd_wr_eeprom+0xb8>
		break;
	}
	case DATA_COMPARE : {
		while (eep_number) {
			// cakaj na pripadne uvolnenie prace s EEPROM
			while(EEPROM_BUSY);
278c:	9f b3       	in	r25, 0x1f	; 31
278e:	89 2f       	mov	r24, r25
2790:	82 70       	andi	r24, 0x02	; 2
2792:	91 fd       	sbrc	r25, 1
2794:	fb cf       	rjmp	.-10     	; 0x278c <fn_int_rd_wr_eeprom+0x76>
			EEPROM_ADR_REGISTER = (uint16_t)p_pointer_eep_eep;
2796:	52 bd       	out	0x22, r21	; 34
2798:	41 bd       	out	0x21, r20	; 33
			EEPROM_RD_BYTE;
279a:	f8 9a       	sbi	0x1f, 0	; 31
			if (*p_pointer_eep_ram != EEPROM_DATA_REGISTER) return(FALSE);
279c:	61 91       	ld	r22, Z+
279e:	90 b5       	in	r25, 0x20	; 32
27a0:	69 13       	cpse	r22, r25
27a2:	1c c0       	rjmp	.+56     	; 0x27dc <fn_int_rd_wr_eeprom+0xc6>
			p_pointer_eep_eep++;
27a4:	4f 5f       	subi	r20, 0xFF	; 255
27a6:	5f 4f       	sbci	r21, 0xFF	; 255
			p_pointer_eep_ram++;
			eep_number--;
27a8:	21 50       	subi	r18, 0x01	; 1
27aa:	31 09       	sbc	r19, r1
			eep_number--;
		}
		break;
	}
	case DATA_COMPARE : {
		while (eep_number) {
27ac:	79 f7       	brne	.-34     	; 0x278c <fn_int_rd_wr_eeprom+0x76>
27ae:	0f c0       	rjmp	.+30     	; 0x27ce <fn_int_rd_wr_eeprom+0xb8>
27b0:	24 0f       	add	r18, r20
27b2:	35 1f       	adc	r19, r21
		}
		break;
	}
	case DATA_WRITE : {
		while (eep_number) {
			while(EEPROM_BUSY);
27b4:	f9 99       	sbic	0x1f, 1	; 31
27b6:	fe cf       	rjmp	.-4      	; 0x27b4 <fn_int_rd_wr_eeprom+0x9e>
			EEPROM_ADR_REGISTER = (uint16_t)p_pointer_eep_eep;
27b8:	52 bd       	out	0x22, r21	; 34
27ba:	41 bd       	out	0x21, r20	; 33
			EEPROM_DATA_REGISTER = (uint8_t)*p_pointer_eep_ram;
27bc:	81 91       	ld	r24, Z+
27be:	80 bd       	out	0x20, r24	; 32
			EEPROM_WR_BYTE;
27c0:	fa 9a       	sbi	0x1f, 2	; 31
27c2:	f9 9a       	sbi	0x1f, 1	; 31
			p_pointer_eep_ram++;
			p_pointer_eep_eep++;
27c4:	4f 5f       	subi	r20, 0xFF	; 255
27c6:	5f 4f       	sbci	r21, 0xFF	; 255
			eep_number--;
		}
		break;
	}
	case DATA_WRITE : {
		while (eep_number) {
27c8:	24 17       	cp	r18, r20
27ca:	35 07       	cpc	r19, r21
27cc:	99 f7       	brne	.-26     	; 0x27b4 <fn_int_rd_wr_eeprom+0x9e>
			eep_number--;
		}
		break;
	}
}	
beh_programu = FALSE;
27ce:	10 92 c4 01 	sts	0x01C4, r1	; 0x8001c4 <beh_programu.2465>
return(TRUE);
27d2:	8f ef       	ldi	r24, 0xFF	; 255
27d4:	08 95       	ret

Tak podle toho co jsi psal bych chápal, že mám ten kód co jsem poslal správně. EEARH nastavuji v prvním zápisu a čtení a pak již pracuji jen s EEARL. Pro první zápis a čtení je adresa 23 a pro druhé 24, takže to by mělo být také ok. Jenže když si kód otestuji v Atmel Studiu, tak mi vždy vypíše poslední zapsanou hodnotu - obě čtení mi vypíší poslední zapsanou hodnotu. Takže bych to chápal tak, že se první zadaná hodnota přepíše. Bohužel nemám přístup k HW a tak musím testovat pomocí debuggeru v Atmel Studiu.

Ak to testuješ v simulátore, tak potom je to možno chyba v ňom. Na to by som sa nespoliehal. Pravdepodobne to máš správne a iba strácaš čas nekvalitným sw prostredím. Možno :slight_smile:

To mě taky napadlo, ale sázel jsem spíš na to, že mám něco špatně.

Máš chybu ve čtecích rutinách. Na začátku máš SBIC EECR, EERE, ale v datasheetu je na začátku čtení SBIC EECR, EEWE. Bit pro start čtení se nuluje prakticky okamžitě, ale vždy (i před čtením) musíš kontrolovat, zda ještě neprobíhá zápis - tedy EEWE. Ve Tvém případě pořád ještě probíhá zápis, ale Ty už se snažíš číst.

Datasheet o čtení z EEPROM :
The user should poll the EEWE bit before starting the read operation. If a write operation is in
progress, it is neither possible to read the EEPROM, nor to change the EEAR Register.

Pro Martina (a nejenom pro něj) : Osobně nepoužívám čistě jenom zápis do EEPROM, ale vždy si před zápisem kontroluju, jestli je na dané adrese stejná hodnota a zapisuju až v případě neshody - v Cčku je to eeprom_update, v assembleru prostě vložím adresu do adresních registrů, přečtu a porovnám a zápis dávám až v případě neshody. Šetří se tím počet zápisů do EEPROM - a tedy EEPROMka, která má omezený počet zápisů, jako taková.

No a je to, chyba sa našla :slight_smile:

osobne funkciu

fn_int_rd_wr_eeprom()

volám, len keď nastala nejaká zmena a ešte v odôvodnených prípadoch kontrolujem, či tá zmena neprichádza príliš často. Napríklad zápis nameraných minimálnych a maximálnych hodnôt.

Ak sa po inicializácii regulátora teploty mení teplota relatívne husto, program sa neplaší, ale po zápise do EEPROM ďalší zápis nenastane skôr ako napríklad za 10 skúnd. Maximálne hodnoty ostávajú medzitým v RAM. To, že sa jedná o zmenu ktorú treba zapísať nemusím zisťovať načítavaním z EEPROM, ale nastavením bitu už pri prvej zmene údajov v RAM želaným smerom. Potom stačí iba odpočítať čas od posledného zápisu do EEPROM, spraviť zápis a vynulovať príznak.

Ale o to sa stará úplne iná časť programu.

Neskutečně díky. Já se zaměřil na EEARL/H a a hledal v tom chybu. Hlavně jsem našel kód, který měl být na 100%, protože už byl odsouhlasený vyučujícím ale bohužel v něm byla tato chyba. Ještě jednou díky