Newsgroup: it.hobby.elettronica.digitale
Titolo: [Help PIC] RS232 routine di ricezione dinamica

Ciao a tutti,
sto lavorando su un PIC, il 18F4320 a 4MHz, gestisco parecchie periferiche
tra le quali una seriale RS232 asincrona, avrei bisogno di ricevere da un PC
N bytes che possono essere al massimo 35, ma anche 3 o 4 solo;
Ho realizzato una routine che riceve 1 byte, ma per ricevere piů bytes e
memorizzarli non posso ripeterla con una chiamata perchč alla fine della
ricezione di ogni byte perdo il tempo necessario a memorizzare il dato,
quindi perdo il byte successivo.
L'altro problema č che se il PC trasmette solo 4 bytes vorrei fermarmi
almeno al quinto, come posso fare?Non posso controllare ad ogni byte se
corrisponde ad un byte di stop, per un problema simile a quello che ho
appena spiegato.
Grazie a tutti per l'aiuto.
Ronnie

 appena spiegato.
Che vuol dire?


premesso che tutte condivido le considerazioni fatte da pozz, ma se vuoi una
soluzione più semplice ti suggerisco (se sei tu a decidere quali e quanti
bytes il pc deve trasmettere) di crearti un pacchetto di lunghezza costante
assegnando ad ogni byte un significato ben preciso. Se non ti servono tutti
i bytes, semplicemente quelli in più li ignori.
Io in un mio progetto ho fatto quanto segue:
 - pacchetto di lunghezza fissa di 11 bytes;
 - i primi 5 bytes individuano la stringa di sincronizzazione (io ho
condiviso lo stesso bus per più periferice, le individuo con questa stringa;
 - sesto byte è il codice che mi individua il comando;
 - i sei bytes rimanenti rappresentano dei valori opzionali di ogni comando;
 - non tutti i comandi sfruttano tutti i bytes dei parametri, li includo
comunque nel pacchetto ma durante l'elaborazione li ingnoro.
Risultato? Vita semplificata e una certa flessibilità nel creare nuovi
comandi e nel passare i parametri.
diversamente non mi viene altro in mente.

 comandi e nel passare i parametri.
Io con un 16f876 a 4MHz, ho implementato la routine di ricezione ad 
interrupt, in modo che ogni carattere ricevuto venga immagazzinato in un 
buffer circolare che poi verrà gestito con calma nel ciclo principale. 
L'idea del pacchetto era simile alla tua, ma mi sono scontrato con un 
problema: se trasmetto più di 2 byte si verifica un errore di overrun. 
Quindi suppongo che la mia routine di interrupt sia troppo lenta, ma non 
posso limarla ulteriormente. Ho provato anche ad abbassare la baudrate da 
19200 a 9600, ma non è cambiato nulla. Tu a che frequenza fai girare il 
programma?
Ciao.
-- 
Nik (RN) - IW4EL
http://www.moduliaggiuntivi.com





Sto usando una 16f628 con quarzo da 16 MHz e un baudrate di 38400.
Non uso gli interrupt perchè la sequenza degli eventi è arbitrata dalla pic
che la fa da padrone.
Diversamente eseguo una procedura di handshake che mi permette di prendere
il controllo della pic e farle eseguire i comandi che voglio (cambio di
configurazione, trasferimento del contenuto della eeprom, cancellazione
della stessa, impostazione dell'orologio, controllo dello stato di carica
della batteria tampone ecc.....).

Ciao Pozz,
che dire: GRAZIE!
In questi giorni ho pensato al problema, avevo valutato anch'io la
possibilitą di trasmettere come primo byte la lunghezza del pacchetto, ma ho
deciso invece di emettere alla fine del pacchetto un byte di STOP, per una
scelta anche di programma su PC.
Il mio timore era quello di non riuscire, alla fine di ogni byte ricevuto, a
fare un controllo per discriminare se il byte era l'ultimo (esco da
ricezione) o se ne devo aspettare altri (rimango in ascolto);
in effetti bastava fare 4 conti, cioč,correggimi se sbaglio:
- Con una velocitą di trasmissione di 9600 bit/s, ogni bit dura 1/9600=0,104
ms
- Lavorando con un comune quarzo da 4MHz ogni ciclo macchina dura
1/4000000=0,25us
- 0,104/0,00025=416 cicli macchina utili per riconoscere il byte ricevuto
durante la durata del bit di stop.
Quindi ora basta aggiustare un paio di routines ed il gioco mi sembra fatto
giusto?
Un'ultima domanda, sono un po' carente nel MPasm, posso fare un confronto di
questo tipo?
IF    RXByte == Stopbyte    ;RXByte=byte ricevuto, Stopbyte=byte dichiarato
da me come Stop
        <esci dal ciclo RX>
Else
        <continua RX>
ENDIF
Grazie molte per l'aiuto
Ronnie

Devi fare la sottrazione, o il compare tra i due valori.
Il confronto lo posso fare con lo Xor, controllando poi il carry bit z che
va ad uno quando i byte corrispondono,
ma le direttive con cosa funzionano? non posso verificare con un IF il
valore di un registro? Cosa posso verificare con le direttive?
Ciao e grazie ancora.
Ronnie

 byte e quello successivo.
Beh, a questo punto bisognerebbe aggiungere anche un bit di start. Quindi
10 tempi di bit per fare un byte. Siamo a 10/9600=1.041ms.
Effettivamente per i PIC bisogna dividere per 4 e abbiamo 1us ogni
istruzione.
Hai più di mille istruzioni assembler tra un byte ed un altro... credo
siano abbastanza (tranne se non fai dei grossi loop a vuoto).

 ENDIF
Come ti fa notare giustamente TheDancer, quelle sono direttive assembler
e non istruzioni per il microcontrollore. Mi sa che parti proprio da zero.
Dovresti leggerti qualche tutorial di programmazione dei PIC.
A proposito, che PIC stai usando? Se è "grandicello" (ma anche il 16F84
può andare bene in alcuni casi) lo puoi programmare direttamente in C.
Magari ti è più semplice.

 valore di un registro? Cosa posso verificare con le direttive?
Le direttive sono solo delle "istruzioni" all'assemblatore ma non
rappresentano
delle istruzioni del microcontrollore, cioé non fanno parte del suo set di
istruzioni. Servono per gestire meglio il tuo sorgente.
Immagina per esempio di voler creare una versione del tuo programma in
funzione di un simbolo (per esempio, se DEBUG è definito nel sorgente,
vuoi che vengano aggiunte delle istruzioni al tuo programma).
Lo puoi fare con delle direttive all'assemblatore.

 programma?
Impossibile, sbagli sicuramente qualcosa. La ricezione ad interrupt ti
garantisce proprio di non perdere dei byte, anche se ravvicinati,
poiché non appena un byte viene ricevuto il programma viente "interrotto"
(interrupt...) per servire la routine che si preoccuperà di leggere
un byte. Solo se la tua ISR è troppo lunga può succedere qualcosa del
genere... ma a 9600 la tua routine dovrebbe durare almeno 1ms, cioé
circa 1000 istruzioni assembler. Che cavolo hai scritto? Normalmente
nella ISR ti basta controllare se ci sono stati errori di ricezione
(Frame Error o Parity Error), prelevare il byte dal registro di ricezione
spostandolo in RAM, eventualmente incrementare il puntatore della tua
zona di memoria circolare, resettare il flag di interrupt ed uscire.
Penso che con una quindicina di istruzioni dovresti farcela?
Per caso utilizzi qualche loop?


Sto usando un 18F4320, ho cominciato con un 16F84A, ho migrato un programma
su un 16F818 ed ora sto provando a fare una consolle che gestisca una
seriale di comunicazione con PC, una con un lettore barcode, un display ed
una tastiera.
Sono già riuscito a gestire tutto, mi manca solo di realizzare una
comunicazione "stabile" ma dinamica con il PC, trasmettendo pacchetti di
lunghezza variabile terminati da un byte di stop.
Vorrei imparare bene l'assembler, poi il C dovrebbe venire da sè...
Grazie ancora per l'aiuto
Ronnie


[CUT]
Io problemi di questo tipo li ho risolti creando routine che lavora sotto
interrupt ed utilizzando un carattere per identificare l'inizio del
pacchetto ed uno per la fine.
In sostanza le operazioni sono le seguenti, sotto interrupt (RCIF) scrivi un
routine che ad ogni carattere ricevuto fa le seguenti operazioni:
1- verifica se il carattere corrisponde all'inizio trasmissione o alla fine
trasmissione
2- nel caso in cui sia l'inizio della trasmissione azzera il contatore dei
caratteri ricevuti e memorizza il byte nel buffer di ricezione (area di
memoria di dimensioni adeguate alla dimensione del pacchetto massimo che
devi ricevere)
3- incremento il contatore dei caratteri ricevuti
4 - verifico che il contatore non superi il valore massimo possibile, in tal
caso lo azzero
(equivale a scartare il pacchetto)
Se il carattere ricevuto era quello della fine trasmissione e se il numero
di caratteri ricevuti è
inferiore al massimo allora imposto un flag ad 1, questo flag verrà
analizzato nel main che
deciderà se il caomando è valido ed in tal caso la relativa esecuzione.
Sempre nel main, una
volta eseguito il comando imposto a zero il flag. Di seguito un esempio
molto semplice codificato
in C.
if (RCIF)
     {
       SerRx = RCREG;    // Leggo il dato ricevuto dalla seriale e lo copio
in SerRx
        if (SerRx == 'STX')   // Se ricevuto carattere di inizio
trasmissione
            RxCtr = 0;            // Azzero il contatore dei caratteri
ricevuti
        if (SerRx != 'ETX')   // Se il carattere ricevuto è diverso dal
carattere di fine trasmissione
            {
              SerialBufferRx[RxCtr]=SerRx;  // Copia il carattere ricevuto
nel buffer seriale di RX
              RxCtr = RxCtr + 1;   // Aggiorna il puntatore del buffer
              if (RxCtr >= MAXSERBUF)
                  {
                    RxCtr = 0;   // Ricevuto troppi caratteri, azzero il
contatore dei caratteri ricevuti in ingresso
                    RSFlag = 0;   // Non segnalo al main di processare il
comando
                   }
             }
       else
             {
               RSFlag = 1;    // Segnala al main che il dato è stato
ricevuto e che deve essere processato
               SerRx = RCREG;   // Svuoto il buffer di ricezione
               SerRx = RCREG;   // Svuoto il buffer di ricezione
              }
     }       // Fine gestione interrupt RCIF
In questo modo non ti preoccupi di nulla e nel main fai quello che devi
fare, l'interrupt si
occupa di memorizzare i caratteri in ingresso e di darti il consenso al
processo solo se
hai ricevuto la fine del pacchetto e se la lunghezza del pachetto è
inferiore a quella massima
ammessa. Nel mai devi solo verificare se fra il carattere di STX e di ETX
trasmissione hai
un parametro oppure un comando valido.
Nel caso di comandi differenti con numero di parametri fisso il controllo lo
puoi fare abbastanza facilmente utilizzando il costrutto switch.
Se ipotizziamo che un comando sia del tipo  STXAnnnnETX, quindi con
lunghezza massima di 7 caratteri ed in cui
nnnn sono quattro parametri allora, una volta che hai RSFlag=1
dall'interrupt, scriverai come controllo:
if (RSFlag)
{
 switch (SerialBufferRx[1])
    {
      case 'A' : if (RxCtr != (MAXCHARCMD1))
        {
          // Il numero di parametri per il comando A
          // risulta errato poichè differente da MAXXHARXMD1, in tal caso
          // puoi trasmettere un carattere di not acknowledge. Azzeri il
contatore
          // dei caratteri ricevuti e non fai nulla ritornando al main una
volta uscito dallo
          // switch.
        }
      else
        {
         // Il numero di parametri per il comando A è corretto,
         // puoi ad esempio trasmettere un carattere che identifica
         // la corretta ricezione ed interpretazione del comando, un
         // acknowledge. In seguito processi il comando
        }
      break;
  ......
 } // Fine dello switch
} // Fine dell'if(RSFLag)
Fra le inizializzazioni del programma dovrai scrivere:
#define MAXCHARCMD1     7   // Numero massimo di caratteri per il comando A
mentre nel main avrai:
.........................
while(TRUE)
{
  // Ciclo di instruzioni principale
  if (RSFlag)
    {
      // Analisi ed esecuzione del comando
      // ......
      // Fine analisi ed esecuzione del comando
      RSFlag=0;
    }
}
Potrebbe essere un suggerimento.
Ciao
Pow

 dovresti farcela? Per caso utilizzi qualche loop?
Che sbaglio qualcosa l'ho sempre sospettato, infatti facendo il conto 
della durata della trasmissione di un carattere come dici tu ho il tempo 
per eseguire l'interrupt ed altro. Tra l'altro, per velocizzare la cosa 
prima di uscire dall'interrupt controllo sempre che nel frattempo non sia 
arrivato un altro pacchetto, nel qual caso lo gentisco.
L'unica cosa che posso pensare provochi un errore è l'oscillatore che è 
un risonatore ceramico e quindi non è preciso come un quarzo, ma questa 
ipotesi mi sento di scartarla perchè l'errore dovrebbe essere un frame 
error e all'inizio di ogni pacchetto il conteggio asincrono dovrebbe 
ricominciare. Spero di trovare il problema al più presto, è abbastanza 
disarmante.
Grazie, Ciao.


 > N bytes che possono essere al massimo 35, ma anche 3 o 4 solo;
Se voui utilizzare un protocollo a pacchetto semplice e robusto, dai
un'occhiata alla application note 851
(
http://www.microchip.com/1010/suppdoc/appnote/all/an851/index.htm
)
. Non c'è niente da inventare: da pag 24 in poi c'è il codice per
implementare la ricezione a pacchetto dalla seriale. Leggi la
spiegazione, poi copia le routine necessarie!
A presto,
Eugenio.