La Scrittura e lettura byte in eeprom interna al 328 è atomica?

Domandona a cui al momento non ho trovato risposta, sto cercando la risposta nei file della avrlibc, ma credo di dovere leggere anche il datasheet del 328.

La domanda precisa è questa: Posso scrivere e leggere in eeprom tranquillamente un numero qualunque di byte usando le funzioni di avrlibc, anche se nel mio codice c'è un timer che scatta ogni millesimo di secondo?

Se già di suo la scrittura di un byte è non interrompibile da una richiesta di interrupt posso stare tranquillo altrimenti devo spegnere gli interrupt, scrivere e riabilitare gli interrupt per ogni byte da scrivere.

PS:il timer serve per l'RTC software.

Ciao.

E' una domandona, effettivamente... lo sai che non lo so? Il datasheet dice che puoi scegliere di fare la scrittura in un'unica operazione atomica oppure di scindere la cancellazione della memoria e la successiva scrittura del nuovo valore in 2 operazioni distinte, attivando un interrupt per segnalare quando l'operazione pendente è stata eseguita. Leggi pag. 23 del datasheet.

In avr-gcc esiste la funzione eeprom_is_ready() che controlla appunto se la memoria è pronta per l'operazione: http://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html

Cmq penso che se scindi l'operazione nei 2 step, il codice non venga interrotto e l'operazione sia eseguita in HW nei tempi richiesti dalla periferica di accesso alla EEPROM

Ciao leo, capisco che l’argomento è di poco interesse generale, e alla fine quello che mi risponde sei sempre tu ed Astro.

Tu nella stazione meteo prendi provvedimenti quando scrivi in eeprom? (so che usi l’arduino core)

void updateProgramInEEprom()
{
    uint16_t eep_address = (current_program * 29) + 1;

    uint16_t len = 29;
    uint8_t *buff = (uint8_t *)&program;    // trasforma la struttura in un array di uint8_t

    for (uint16_t i = 0; i < len; i++) {

        if (buff[i] != eeprom.busyRead(eep_address)) {
            while (eeprom.write(eep_address, buff[i]) == MHV_EEPROM_BUSY) {;}
        }
        eep_address++;
    }
}

Io sto usando questa funzione per scrivere in eeprom solo se il byte da scrivere e diverso da quello in eeprom.
“program” è una struttura.

Io potrei proteggere la scrittura disabilitando gli interrupt prima del “while(eeprom…” e riabilitandoli dopo, sempre se serve, il mio unico problema è il rischio di corruzione dati della eeprom a seguito del salto alla routine di interrupt che avviene ogni millesimo di secondo, solo questo. Fino ad ora non ho notato nulla di strano scrivendo senza prendere precauzioni, ma vorrei capirci al 100%.

Ciao.

per caso nella struttura hai qualche cosa che può variare a causa degli interrutp?

se si io consiglio questa soluzione: crei una struttura temporanea, fermi gli interrupt, fai la COPIA(niente riferimenti/puntatori, occhio) della struttura vera, riattivi gli interrupt, e scrivi la copia su disco come fai ora.

In questo modo mantieni il tempo di blocco dell'interupt al minimo possibile!

edit: non lo so per certo, ma sono abbastanza sicuro che la scrittura e lettura non sia atomica

per caso nella struttura hai qualche cosa che può variare a causa degli interrutp?

no, non è volatile cioè non modifico il contenuto della struttura dentro una ISR ecc.

se si io consiglio questa soluzione: crei una struttura temporanea, fermi gli interrupt, fai la COPIA(niente riferimenti/puntatori, occhio) della struttura vera, riattivi gli interrupt, e scrivi la copia su disco come fai ora.

Volevi dire: e scrivi la copia in EEprom :roll_eyes:

Ciao.

La mia stazioncina meteo è nata prima della swRTC per cui lì ho un PCF8563 esterno da cui prelevo l'orario esatto ogni tanto. La scrittura dei dati avviene su una EEPROM esterna per cui non mi pongo il problema.

Tornando a noi, hai letto le pagine che ti ho suggerito del datasheet? Da lì io capisco che l'operazione viene effettuata da una periferica per cui il codice carica nei registri dati ed indirizzi per la registrazione sulla EEPROM i valori dopodiché affida alla periferica il compito di effettuare la registrazione fisica. In questo modo il micro prosegue per i fatti suoi mentre la periferica esegue l'operazione in maniera indipendente.

Tornando a noi, hai letto le pagine che ti ho suggerito del datasheet? Da lì io capisco che l'operazione viene effettuata da una periferica per cui il codice carica nei registri dati ed indirizzi per la registrazione sulla EEPROM i valori dopodiché affida alla periferica il compito di effettuare la registrazione fisica. In questo modo il micro prosegue per i fatti suoi mentre la periferica esegue l'operazione in maniera indipendente.

Anche io ho capito così, ma quello che c'è scritto nella avrlibc mi ha confuso, quindi cercavo conferma.

Forse la divisione tra cancellazione e scrittura mediante ISR la si usa quando si hanno tempi stretti ed il codice non può aspettare di compiere l'intera operazione e allora la si spezza in due volte, tra le quali verra eseguito un numero x di cicli scelti dal programmatore.

Ok, farò finta che non serve prendere nessuna precauzione, o meglio sembra non sia necessario quindi non mi preoccupo.

Al massimo mi tocca rivedere la scrittura in eeprom.

Ciao.

MauroTec:

per caso nella struttura hai qualche cosa che può variare a causa degli interrutp?

no, non è volatile cioè non modifico il contenuto della struttura dentro una ISR ecc.

se si io consiglio questa soluzione: crei una struttura temporanea, fermi gli interrupt, fai la COPIA(niente riferimenti/puntatori, occhio) della struttura vera, riattivi gli interrupt, e scrivi la copia su disco come fai ora.

Volevi dire: e scrivi la copia in EEprom :roll_eyes:

Ciao.

sì esatto. Se la tua struttura non è modificata dagli interrupt, allora è inutile farsi gli sbattimenti. Le parti della libreria EEPROM che necessitano un blocco degli interrupt (se lo necessitano) sicuramente già possiedono le chiamate, e visti che il resto del codice arduino è sequenziale, è impossibile che modifichi la struttura mentre viene scritta.

Questi problemi si pongono quando i dati vengono modificati da interrupt, altri thread o processi (ma per i processi la storia si complica un po')

Per capire a fondo il problema dovresti studiarti l'architettura di un sistema, anche 8086, e leggere qualcosa sulla concorrenza. In particolare la concorrenza si applica agli interrupt

Tornando a noi, hai letto le pagine che ti ho suggerito del datasheet? Da lì io capisco che l'operazione viene effettuata da una periferica per cui il codice carica nei registri dati ed indirizzi per la registrazione sulla EEPROM i valori dopodiché affida alla periferica il compito di effettuare la registrazione fisica. In questo modo il micro prosegue per i fatti suoi mentre la periferica esegue l'operazione in maniera indipendente.

Per sicurezza potresti fare così: prosegui tranquillamente nel tuo codice, ma nel momento in cui devi modificare la struttura prima aspetti che la eeprom restituisca un segnale di ready, così sei sicuro al 100% che non disallinei i dati da scrivere

MauroTec:

Tornando a noi, hai letto le pagine che ti ho suggerito del datasheet? Da lì io capisco che l'operazione viene effettuata da una periferica per cui il codice carica nei registri dati ed indirizzi per la registrazione sulla EEPROM i valori dopodiché affida alla periferica il compito di effettuare la registrazione fisica. In questo modo il micro prosegue per i fatti suoi mentre la periferica esegue l'operazione in maniera indipendente.

Anche io ho capito così, ma quello che c'è scritto nella avrlibc mi ha confuso, quindi cercavo conferma.

Forse la divisione tra cancellazione e scrittura mediante ISR la si usa quando si hanno tempi stretti ed il codice non può aspettare di compiere l'intera operazione e allora la si spezza in due volte, tra le quali verra eseguito un numero x di cicli scelti dal programmatore.

Ok, farò finta che non serve prendere nessuna precauzione, o meglio sembra non sia necessario quindi non mi preoccupo.

Al massimo mi tocca rivedere la scrittura in eeprom.

Ciao.

Non intendevo questo ma l'esatto contrario :stuck_out_tongue_closed_eyes: Cioè, tu ti poni il problema relativamente alla swRTC. Allora la cosa è questa: se usi la scrittura atomica, io credo che tu vada a bloccare gli altri interrupt per 3,3ms per cui avresti una perdita non indifferente nella precisione della swRTC. Ti consigliavo perciò di separare la scrittura nelle 2 operazioni cancellazione e scrittura così da farle eseguire all'HW.

Non intendevo questo ma l'esatto contrario smiley-yell

Si in effetti e così, si rischia di perdere o ritardare l'esecuzione di un interrupt che deve accadere ogni millisecondo. A me ha confuso il datasheet e la avrlibc io pensavo che durante la scrittura in eeprom se arrivasse un'interrupt c'èra rischio di corrempere il valore scritto in eeprom, visto che questo rischio non c'è non c'è motivo di preoccuparsi più di tanto.

Ciao.

solo se ne avete voglia, mi spiegate che significa scrittura atomica ? credevo fosse una metafora :)

Per "atomico" si intende NON DIVISO. Ad esempio, un blocco di codice atomico significa che verrà eseguito tutto senza interruzioni dall'esterno. Le interruzioni sono appunto eventuali interrupt. Come sai, durante l'esecuzione del normale sketch, il flusso di esecuzione viene interrotto un sacco di volte da tutta una seria di interrupt, che possono andare dal cambio di stato di un pin all'overflow di un contatore. La swRTC lavora in "background" con un timer che va in overflow ogni 1 ms, generando un interrupt che interrompe l'esecuzione dello sketch. Ci sono però delle operazioni che NON vanno interrotte. Per permettere ciò si disattivano gli interrupt prima di esse, e si riattivano dopo. Quel blocco di codice diventa atomico, non divisibile.

Ogni ISR (Interrupt Service Routine) è per default atomica: significa che non può essere interrotta da un altro interrupt

Ogni ISR (Interrupt Service Routine) è per default atomica: significa che non può essere interrotta da un altro interrupt

Si, anche se in teoria c'è il modo per avere interrupt attivi durante l'esecuzione della routine di interrupt, ma questa cosa come si gestisce e l'utilità che ha io non lo so, ma c'è la possibilità per cui ci sarà anche un modo di usarla.

Ciao.

Se un codice non puó essere interrotto durante la sua esecuzione puoi disattivare gli interrupt per quel tempo con CLI / SEI La scrittura di un EEPROm avviene tramite ISP o I2C. In tutti due i casi la trasmissione avviene via HW dedicata ma i comandi che la pilotano sono diversi e percui un numero discreto diverso da 1.

Non vedo grossi problemi nella scrittura del EEPROM. Qual é in particolare il Tuo problema? Hai un problema oppure sono considerazioni teoriche perché vuoi capire come funziona?

Ciao Uwe

Ciao uwe, io mi riferisco alla EEprom interna al 328.

Non vedo grossi problemi nella scrittura del EEPROM. Qual é in particolare il Tuo problema? Hai un problema oppure sono considerazioni teoriche perché vuoi capire come funziona?

Nessun problema, il perchè me lo sono chiesto è scritto qualche post fa, dove ho scritto: A me ha confuso il datasheet e la avrlibc io pensavo che durante la scrittura in eeprom se arrivasse un'interrupt c'èra rischio di corrempere il valore scritto in eeprom, visto che questo rischio non c'è non c'è motivo di preoccuparsi più di tanto.

Ciao.

scusami, allora dimostra che non ho letto bene tutta la discussione. Ciao Uwe

Grazie leo della spiegazione, chiarissima

Dove?

La risposta alla domanda è molto semplice, se la scrittura della EEPROM, ed è solo questa ad essere critica, la gestisci tramite la libreria eeprom.h è una operazione atomica eseguita con alcune righe in assembly come chiaramente descritto nel sorgente della libreria.

"/* START EEPROM WRITE CRITICAL SECTION */\n\t"
        "in r0, %[__sreg]       \n\t"
        "cli                \n\t"
        "sbi    %[__eecr], %[__eemwe]   \n\t"
        "sbi    %[__eecr], %[__eewe]    \n\t"
        "out    %[__sreg], r0       \n\t"
        "/* END EEPROM WRITE CRITICAL SECTION */"

Da notare che la porzione assembly è inserita in un file oggetto che viene linkato durante la compilazione.

Da notare che la porzione assembly è inserita in un file oggetto che viene linkato durante la compilazione.

Astro, ma come ci sei arrivato, io mi sono andato a spulciare la avrlibc cominciando dal eeprom.h, e sono andato a finire a guardare i file “.s”, ma non ho terminato questa analisi che è molto lunga al momento per me che non sono al 100%.

Ok grazie, per aver postato questo pezzo di codice, ora però non ho capito se quel codice lo hai ricavato da un file oggetto disassemblandolo e se si quale questo file? Comunque se c’è quel contenuto in un file oggetto ci deve essere un asm con quel contenuto ora faccio una ricerca e vediamo.

Io vedo un “cli” prima della scrittura, ma cosa accade al timer se quando va in overflow, la richiesta di interrupt viene salvato ed eseguita dopo il “sei”, penso che sia qualcosa di questo tipo, quindi scrittura lunghe in EEprom portano con maggiore probabilità un errore nell’ora conteggiata.

PS: in superficie è tutto semplice, quando cominci a scavare le cose si complicano, chi lo sa per esperienza è cosciente che non esiste nulla di semplice.

@leo72
legacy mi ha risposto qui http://www.electroit.tk/index.php

Allora sono andato a vedere l’implementazione di una classe EEprom di una libreria ed ho visto che usano questa macro ATOMIC_BLOCK(ATOMIC_RESTORESTATE) prima e dopo la scrittura in EEprom. Sicuramente questa macro mantiene lo stato, quindi chiamata due volte blocca e diciamo rilascia.

Sono rinco forte, sono andato a guardare vecchio codice scritto da me e anche li faccio un cli prima di scrivere e un sei dopo, ma non c’è nessun commento nel codice, probabilmente nel dubbio lo inserito.