Jump to content
PLC Forum


Sign in to follow this  
ic910

Comunicazione Seriale Tra 2 Pic Invio Di 4 Byte

Recommended Posts

ic910

Salve a tutti

Ho effettuato una comunicazione seriale asincrona con protocollo usart tra 2 pic uno un 16f877 l'altro un 16f870.

Nel 16f877 ho effettuato la comunicazione tramite modulo hardware interno,mentre sull' 16f870 ho effettuato la comunicazione tramite software.

Mi serve questa comunicazione per trasferire la rilevazione di 2 temperature dal 16f870 al 16f877.

Sul 16f870 ho impostato una risoluzione di conversione A/D a 8 bit inviando 2 byte dati verso 16f877.

Fin qui tutto bene , il problema si presenta quando volendo aumentare la risoluzione A/D a 10 bit sul 16f870, invede di inviare 2 byte ne devo inviare 4 perchè utilizzando la 10 bit ho per le 2 temperature rilevate 2 variabili word.

Aumentando la risoluzione A/D a 10 bit non ho più una corretta visualizzazione delle temperature sul diplay gestito dal 877, infatti girando lentamente i trimmer multigiri che mi servono per simulare i sensori lm35 su scheda 870, invece di avere degli step di temperatura da 0,5 gradi ho degli step di 30 gradi all incirca.

Il listato è stato fatto tramite Proton Basic

Parte di listato16f877 inerente alla comunicazione seriale :

Declare Hserial_Baud 9600

Declare Hserial_TXSTA = %00100100

Declare Hserial_RCSTA = %10010000

Declare Hserial_SPBRG 25

Declare Hserial_Clear = On

HSerOut ["**",$0,"*"] 'invio i ** per sincronizzare la trasmissione l'ultimo * indica al 870 la funzione di leggere le 2 temperature e di inviare successivamente

HSerIn 50,ciclo_1,[let_sensor0.HighByte.,let_sensor0.LowByte] 'ricevo i 2 byte per la prima temperatura

HSerIn 50,ciclo_1,[let_sensor1.HighByte.,let_sensor1.LowByte] 'ricevo i 2 byte per la seconda temperatura

Parte di Listato inerente comunicazione seriale 16f870:

SerIn RX_PIC,84,[Wait("**"),dato,funzione]

SerOut TX_PIC,84,[let_sensor0.HighByte.,let_sensor0.LowByte]

SerOut TX_PIC,84,[let_sensor1.HighByte,let_sensor1.LowByte]

Spero che mi potete aiutare a risolvere la problematica grazie mille in anticipo.

Share this post


Link to post
Share on other sites

Livio Orsini

Non so niente di proton basic, io per i PIC uso solo "C" e asm.

Se usi il convertitore A/D in modalità 10 bits il risultato te lo da ovviamnete in 2 bytes, quindi se trasmetti due valori in contemporanea devi usare 4 bytes.

Il difetto che accusi sembra proprio un disallineamento della parola. infatti il rapporto tra 0xFFFF e 0x03FF è proprio 0x40 ovvero 64 decimali, da cui 0.5°C diventan 32°C.

Controlla che la variabile letta e trasmessa sia allineata a destra.

Share this post


Link to post
Share on other sites
ic910

Grazie Livio di avermi risposto .

Intendi ADFM giustificata verso destra ? Bit 7 del registro ADCON1 su 16f870.

Share this post


Link to post
Share on other sites
Livio Orsini

A memoria non ricordo la configurazione, però la giustificazione è quella.


A memoria non ricordo la configurazione, però la giustificazione è quella.


A memoria non ricordo la configurazione, però la giustificazione è quella.


A memoria non ricordo la configurazione, però la giustificazione è quella.

Share this post


Link to post
Share on other sites
ic910

ok adesso provo vediamo come và

Grazie per il momento

Share this post


Link to post
Share on other sites
ic910

Grande Livio ora funziona tutto il problema era proprio quello.

Ho anche suddiviso la trasmissione e ricezione in 2 step prima invio dati sensore 1 poi invio dati sensore 2

Ho ora una risoluzione di 0,5 gradi

Grazie ancora e alla prossima

Share this post


Link to post
Share on other sites
ic910

Salve a tutti .

Oltre ai 4 byte mi serve di inviare un quinto byte .

Nella trasmissione i 4 byte come detto sopra li trasmetto in 2 step e tutto và bene.

Successivamente se trasmetto il quinto byte tramite un 3 step di trasmissione ho dei problemi .

Non ricevo bene il quinto byte e addirittura mi sballa la ricezione del quarto

Come posso risolvere il problema ?

Share this post


Link to post
Share on other sites
Livio Orsini

Fai la ricezione ad interrupt?

Quanto è lungo il buffer di ricezione?

Share this post


Link to post
Share on other sites
ic910

No non utilizzo interrupt

Prima invio un comando di trasmissione dalla scheda di controllo(16f877) verso la scheda di attuazione inviando due * per sincronizzare il tutto HSerOut ["**",$0,"*"] .

Il 4 byte come si può vedere nella riga di programmazione e un * infatti sulla scheda di attuazione ho messo questa riga per la ricezione SerIn RX_PIC,84,[Wait("**"),dato,funzione]

Come funzione se riceve * trasmesso dalla scheda di controllo lo faccio andare a leggere i 2 ingressi analogici ed inoltre lo stato dell intera portac del micro di attuazione.

Poi successivamente vado a una routine di trasmissione dove trasmetto le word divise in 2 byte (le 2 word sono le 2 temperature) ed in più trasmetto il byte di stato portac

SerIn RX_PIC,84,[Wait("**"),dato,funzione] 'comando di ricezione dalla porta PORTA.2 = RX_PIC

If funzione = "*" Then
let_sensor0 = ADIn 0
DelayUS 50 'ritardo per tempo di acquisizione
let_sensor1 = ADIn 1
DelayUS 50

ingressi = PORTC

GoSub trasmissione 'và alla routine di trasmissione per inviare le 2 letture dei sensori di temperatura + stato portc 870

trasmissione: 'subroutin solo per trasmettere i dati tramite seriale al 16f877

SerOut TX_PIC,84,[let_sensor0.HighByte,let_sensor0.LowByte] '

SerOut TX_PIC,84,[let_sensor1.HighByte,let_sensor1.LowByte]

SerOut TX_PIC,84,[ingressi]

Return

Ricezione su 16f877

HSerIn 50,ciclo_1,[let_sensor0.HighByte,let_sensor0.LowByte]

HSerIn 50,ciclo_1,[let_sensor1.HighByte,let_sensor1.LowByte]


'HSerIn 50,ciclo_1,[inputc_870]

Edited by ic910

Share this post


Link to post
Share on other sites
Livio Orsini

Divresti usare l'interrupt di ricezione. Ad ogni interrupt scatta la routine di servizio, in questo modo sei sicuro di non perder caratteri.

Share this post


Link to post
Share on other sites
ic910

Prima di utilizzare l'interrupt stavo pensando di provare in trasmissione una array di 5 byte.

4 byte contenenti le informazioni delle 2 temperature e l'ultimo contenente lo stato della porta C del 16f870.

Poi stò pensando di provare la trasmissione inviando il comando da un terminal per vedere se mi escono in esa i 5 byte. Anche se utilizzo un array si devono vedere i 5 byte ?

Ho pensato anche che il problema del quinto byte possa dipendere dal fatto che io ho impostato la portaC con il 3 bit come input quello che utilizzo per rilevare ,mentre gli altri 8 gli ho configurati come output perchè non essendo utilizzati li ho cosi per evitare disturbi in ingresso .

Share this post


Link to post
Share on other sites
Livio Orsini

Come ti ho detto non conosco cosa fa il compilatore basic che stai usando.

So però cosa fa il micro.

Ogni volta che il buffer di trasmissione dello UART contiene un dato si attiva la serializzazione e questo dato viene messo in seriale sulla linea TX.

Dualmente ogni volta che compare un carattere sulla linea di ricezione questo viene convertito in parallelo e messo nel buffer di ricezione.

Se il buffer di ricezione non viene svuotato prima dell'arrivo del secondo carattere questo viene svrascritto.

Ecco perchè è sempre consigliabile l'uso degli intterrpts.

Share this post


Link to post
Share on other sites
ic910

Grazie Livio di avermi risposto .Si l'interrupt è la soluzione migliore. Infatti penso che il problema sia proprio del carattere sovrascritto. Ho provato a mettere un ritardo dopo ogni trasmissione e ora ricevo tutti e 5 i byte. Però l'aggiornamento delle temperature ha un pò di ritardo.

Inoltre delle volte l'unita di controllo invia delle instruzioni di attuazione all unità attuatrice che non esegue correttamente

Edited by ic910

Share this post


Link to post
Share on other sites
Livio Orsini
Però l'aggiornamento delle temperature ha un pò di ritardo.

Scusa ma la variazione di temperatura, se non si tratta di casi particolari, è sempre dell'ordine dei secondi almeno.

Comunque il metodo più corretto è l'interrupt silla ricezione o, incaso di trasmissione di pochi dati e non molto frequneti, il polling sul bit che segnala che il buffer di ricezione non è vuoto.

Share this post


Link to post
Share on other sites
ic910

Ciao Livio l'interrupt non l ho mai utilizzato questa potrebbe essere la volta buona.

Ti spiego brevemente quello che devo realizzare.

Ho una scheda di attuazione che oltre ad attuare rileva anche 2 temperature con conversione a 10 bit, inoltre rileva anche lo stato dell' intera porta C .

Ho anche una scheda di controllo che dialoga tramite seriale con la scheda di attuazione .

La scheda di controllo attualmente invia una trasmissione di 4 byte nella quale richiede alla scheda di attuazione i valori delle 2 temperature e lo stato della porta C (scheda attuazione)

La scheda di attuazione dopo aver ricevuto la prima trasmissione dei 4 byte và a leggere le 2 temperature e lo stato della sua porta c

La scheda di attuazione invia le 2 temperature tramite 2 trasmissioni contenenti 2 byte alla volta (perchè essendo 10 bit di AD ho bisogno di una word) ad infine invia anche in un ultima trasmissione lo stato della port c.

Dopo ogni singola trasmissione della scheda di attuazione ho messo un ritardo di 500 mS(sulla routine della scheda di attuazione) per dare il tempo alla scheda di controllo di ricevere i dati.

Sulla scheda di controllo ci sono 3 distinte ricezioni rispettando l'ordine di trasmissione.

Inoltre la scheda di controllo quando analizza le 2 temperature e la condizione della porta c della scheda di attuazione se si verificano determinate condizione deve inviare un comando di controllo verso la scheda di attuazione per attivare dei relè.

Ora aggiungendo questi ritardi lo scopo è stato raggiunto. Delle volte raramente ho notato che la seriale si perde per circa 2/ 3 secondi per poi ritornare a funzionare automaticamente. Questo mi è capitato una solo volta nell' arco di 2 ore di funzionamento.

Inizialmente avevo anche un altro problema che poi ho risolto semplicemente invertendo una serie di instruzioni. Praticamente se inviavo il comando di attivazione relè e successivamente il messaggio su diplay dell' avvenuta attivazione ,mi visualizzava il messaggio ma non mi attivava i relè(il comando di attivazione relè và tramite seriale). Ho dovuto far visualizzare prima il messaggio e successivamente fare attivare i relè per non aver problemi . Ancora non sono riuscito a comprendere il perchè .

Livio ti ho spiegato brevemente il sistema, utilizando l'interrupt per migliorare il tutto e per rendere più affidabile il sistema puoi illustrarmi la logica come a cercato di fare io sopra ?

Da notare che le temperature e lo stato della porta c vengono trasmessi in continuazione e in continuazione la scheda di controllo li deve ricevere e inviare un comando di attivazione se si verificano eventuali condizioni.

Grazie Mille alla prossima

P.S.Ti ho inviato anche una richiesta di contatto su skype se ti fà piacere puoi accettare.

Edited by ic910

Share this post


Link to post
Share on other sites
Livio Orsini

E' semplice.

Uno dei modi è creare un buffer di ricezione di n byati quantinsono quelli che dovrai ricevere in totale.

Ad ogni interrupt la routine di servizio prende il contenuto del buffer di ricezione dello USART e lo trasferisce nel primo carattere libero del buffer di ricezone che hai creato.

Ovviamnete sarebbe necessario associare al tutto un carattere che specifica l'inizio di trasmissione. Riconosciuto quel carattere punti al primo elemento del buffar di ricezione, così al successivo arrivo trasferisci il byte e incrementi il puntatore. Alla fine della trasmizzione invii il carattere di fine trasmissione, solitamente si manda un CR.

A questo punto abiliti la routine che dovrà trattare i dati ricevuti.

Io solitamente queste cise le scrivo in "C"; se vuoi posso vedere di recuperare un pezzo di programma e postarlo come esempio.

Share this post


Link to post
Share on other sites
ic910

Livio grazie ancora di avermi risposto. Ora ho un pò chiara la logica .Cerco di fare una prova in basic vediamo che esce fuori.

Per quanto riguarda il C non lo conosco, se vuoi puoi postare il listato vediamo se riesco a capirlo. .

Share this post


Link to post
Share on other sites
ic910

Eccomi ancora .

Alla fine sono riuscito ad implementare una ricezione dati tramite interrupt.

Ho creato una variabile Array definita buffer con 6 indici disponibili ad ogni interrupt di ricezione vado a leggere il contenuto del registro RCREG e associo il dato ricevuto all indice del buffer.

Ora mi si è creato un problema.

Quando ricevevo tramite 3 comandi ben distinti i 5 byte se entro gli 800 mS non ricevevo nulla , il programma era indirizzato verso una routine di segnalazione seriale ko.

Ora cosa devo prendere in considerazione per indirizzare il programma a quella routine di seriale ko ? Perchè ora la ricezione è gestita dall interrupt.

Grazie Mille in Anticipo

Share this post


Link to post
Share on other sites
Livio Orsini

Fai lastessa cosa. magari conta gli interrutp del timer 1 quello che usi per l'antirimbalzo.

Share this post


Link to post
Share on other sites
ic910

Il timer1 non lo utilizzo per l'antirimbalzo

Avevo pensato di mettere una condizione sulla variabile array buffer . In poche parole se il buffer [0] rimaneva a 0 per più di 800mS mi doveva dare seriale ko.

Da notare che una volta ricevuti tutti e 6 i byte porto il buffer[0] = 0 , in modo tale che tutto è pronto per ricevere un altra trasmissione dati.

Però questa soluzione non è stata risolutiva mi porta sempre alla routine di seriale ko.

Share this post


Link to post
Share on other sites
Livio Orsini

Come fai a fare il filtro antirimbalzo' usi i delay? se è così è un modo molto brutto, perchè sono bloccanti, ovvero il processore "looppa" nella routine di ritardo e basta.

Prima o poi dovrai decuderti a fare temporizzazioni corrette.

Questa potrebbe essere l'occasione per iniziare perchè non puoi tenere fermo il processore per 800 ms.

#int_timer1
void Timer10ms()
    {
            set_timer1(0xB1DF);   // Caricato 15535 ==>
                  // 20.000 * 4 * 125nsec = 10msec
                  // 65535 - 20000 = 45535(0xB1DF)
         rd_ad ();
           if ( time_data >0)
            {
                time_data--;
            }
            else
                {
                    time_data = 5;
                }
   }
#int_ext
void rb0_int()
{
   #asm
      btfss   port_b,0
      goto   end_int
   #endasm
      rx_temp ();      //lettura temperatura
      memo_temp();
   #asm
   end_int:
      nop
    #endasm
}

#INT_RDA
Rx_232()
{
      ibuff_Rx[iRxpnt] = getc();
      iRxpnt++;
      enable_interrupts (INT_RDA);
      if (iRxpnt>=16){
         iRxpnt = 0;
      }
}

Nell'esempio che ho messo sopra uso l'interrupt per 10ms, poi c'è un contatore per avere un temporizzatore a 50 ms; se carichi la variabile con 80 invece che con 5 hai i tuoi 800ms. Abiliti il timer a bufferrx vuoto e se allo scadere è ancora vuoto dai l'allarme.

Share this post


Link to post
Share on other sites
ic910

Ciao Livio grazie di avermi risposto.

Ora il micro non è più fermo per 800 mS ma la ricezione e affidata all interrupt anche in basic l'ultima parte dell listato che hai scritto e molto simile alla mia ti invio la mia routine di interrupt

Context Save 'salva i registri e i vari parametri per rimetterli come erano prima del interrupt .Questo solo per la serie 16F '

If PIR1.5 = 1 Then 'PIR1.5 è il bit 5 del registro PIR1 corrisponde al flag di
'avvenuto interrupt per ricezione datascheet 877 pag.22. Nel momento in cui ricevo il PIR1.5 si porta a 1
Clear PIR1.5 'con il comando clear lo risetto a 0 il PIR1.5 cosi successivamente potranno avvenire altri interrupt

buffer[buft] = RCREG 'RCREG dove arrivano i dati in ricezione del Usart
Inc buft 'Con il comando Inc incremento di una unità buft

If buft > 5 Then 'buft è una variabile che rappresenta l'idice della variabile Array. Siccome devo ricevere 6 dati (byte)
buft = 0 'e il conteggio và da 0 a 5 (sono 6) quando supera i 5 riporto l'indice a 0
End If

EndIf


Context Restore 'ripristina i registri utilizzati per il salto verso la routine di interrupt ai valori iniziali prima

If buffer[0] = "*" Then 'buffer[0] è il 1 byte che ricevo mi serve come byte di controllo inizio ricezione dal 870

let_sensor0.HighByte = buffer[1]
let_sensor0.LowByte = buffer[2] '
let_sensor1.HighByte = buffer[3]
let_sensor1.LowByte = buffer[4]
inputc_870 = buffer[5]
EndIf
buffer[0] = 0

Edited by ic910

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...