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
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
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
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
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