Newsgroup: it.hobby.elettronica.digitale
Titolo: Gestire trasmissione UART via interrupt

Per una particolare applicazione, devo ridurre al minimo il tempo della risposta 
ad un comando proveniente dalla UART di un microcontrollore.
Giusto per semplificare, immaginate che il micro con ADC interno faccia solo 
l'acquisizione di 8 misure analogiche ed abbia internamente un array di interi:
   int segnale[8];
Nel loop principale, vengono acquisite le 8 misure ciclicamente e l'elemento 
corrispondente dell'array viene aggiornato.
Un PC collegato via seriale, invia una richiesta di lettura dell'array e il 
micro deve rispondere non appena riceve questa richiesta, minimizzando il tempo 
di risposta.
La cosa più ovvia è ricevere e trasmettere ad interrupt sull'UART, cosa che ho 
fatto senza grossi problemi. In effetti il tempo di risposta è trascurabile.
Il dubbio che mi viene, però, è sulla gestione nel loop principale dell'array di 
interi. Il micro è ad 8 bit e gli interi sono a 16 bit, quindi l'aggiornamento 
di un elemento dell'array non è fatto mediante una sola istruzione assembler, ma 
con più istruzioni assembler.
Supponiamo che
   segnale[0] = 0x0100
e supponiamo che alla successiva acquisizione dello stesso segnale si abbia il 
valore 0x00FF. Supponiamo inoltre che l'aggiornamento dell'intero avviene così:
   1. copia parte bassa del risultato dell'AC nell'accumulatore
   2. copia accumulatore nella parte bassa di segnale[0]
   3. copia parte alta del risultato ADC nell'accumulatore
   4. copia accumulatore nella parte alta di segnale[0]
Se scatta l'interrupt di ricezione tra la fase 2. e la fase 3., nella stessa 
routine di interrupt abilito la trasmissione del primo byte, per esempio la 
parte alta di segnale[0]. In effetti trasmetto il byte 0x01.
Durante la trasmissione del byte 0x01, che avviene ad interrupt, l'esecuzione 
del programma continua con la fase 3. e 4.
Quando il byte 0x01 è stato completamente trasmesso, scatta di nuovo l'interrupt 
di trasmissione e viene inviata la parte bassa di segnale[0] che ora vale 0xFF.
Alla fine dei conti quello che viene trasmesso è il valore 0x01FF che non 
c'entra nulla nè con 0x0100 nè con 0x00FF.
Naturalmente il problema deriva dal fatto che l'aggiornamento di un elemento 
dell'array segnale[] non è "atomico", cioè non avviene in una istruzione 
assembler e quindi può essere interrotta a metà, ottenendo un valore non coerente.
Ho pensato di risolvere il problema disabilitando gli interrupt subito prima 
dell'aggiornamento dell'array e riattivando gli interrupt subito dopo. Però è 
una soluzione che non mi piace poichè rischio di perdere dei byte in ricezione 
se l'operazione di aggiornamento dell'array dovesse durare molto (se gli 
elementi anzichè essere 8 sono 200 o anzichè essere interi sono stringhe di 
caratteri molto lunghe).
Ci ho pensato un po' e, alla fine, penso che non si possa fare altro che avere 
un doppio buffer di dati, uno pronto per la trasmissione con uno stato coerente 
e l'altro temporaneo usato per l'aggiornamento. Quando l'aggiornamento è 
completo, con una operazione atomica (per esempio, l'impostazione di un flag) 
faccio uno switch da un buffer all'altro.
L'esempio che ho fatto è solo indicativo e non è quello reale. Volevo solo 
sapere, sperando di essermi spiegato, come vengono risolti questi tipi di 
problemi...

problemi...
Se hai ram sufficiente, l'idea migliore è senz'altro quella di utilizzare un
doppio buffer, uno per l'elaborazione e uno per la trasmissione.
La cosa ti viene utile se ad esempio vuoi anche fare un controllo della validità
del dato letto o una media sulle ultime 2^n letture.
--
ciao
  Stefano

 tipi di problemi...
Le soluzioni da te proposte sono quelle più diffuse ed in genere sono 
molto buone. Se non hai problemi di coerenza dei dati dell'array (cioè 
non necessiti che i dati dell'array debbano essere per forza tutti 
aggiornati atomicamente in un colpo solo), puoi implementare una 
soluzione che è a metà tra le 2.
Dovresti fare così:
- Acquisisci dall'ADC il valore e lo metti in una variabile temporanea
- Disabiliti gli interrupt
- Copi la varibile temporanea nel posto giusto sull'array
- Riabiliti gli interrupt
La copia nella variabile temporanea serve ad evitare di dover 
disabilitare gli IRQ per il tempo necessario alla conversione AD.
Ciao