Trasmissione seriale asincrona con MCC

 

In questa pagina è mostrato come utilizzare MPLAB(c) Code Configurator per trasmettere e ricevere dati serialmente utilizzando il protocollo asincrono alla base degli standard RS-232 e RS485.

Alcune informazioni sulla struttura hardware sono disponibili alla pagina Trasmissione seriale asincrona.

Trasmissione senza interrupt

La configurazione è, al solito, particolarmente semplice; si limita:

Blocco dei due pin di trasmissione e ricezione

Configurazione dell'UART

Il codice da scrivere per trasmettere un byte è altrettanto semplice e si basa sull'uso della funzione EUSART_Write(uint8_t txData)  (nota 2), descritta nel file eusart.h automaticamente creato da MCC. Un esempio di semplice trasmissione di due byte:

EUSART_Write(0x08);
EUSART_Write(0x50);

Di seguito il segnale presente sul pin del PIC e la relativa decodifica:

Trasmissione seriale di due byte 

Un paio di osservazioni sul codice:

[Approfondimento] L'ultima osservazione ha riflessi importanti quando siamo interessati a conoscere il tempo richiesto per la trasmissione di una serie di byte.

Consideriamo il seguente codice, costituito da un ciclo infinito contenente un ritardo di 2 ms e la trasmissione di un singolo byte a 9600 baud:

while (1) {
 __delay_ms(2);
 EUSART_Write(0x50);
}

Si noti che il ritardo di 2 ms inserito è sufficiente per la trasmissione del byte, pari a 1 / 9600 · (1 + 8 +1) = 1,04 ms

Il diagramma temporale corrispondente mostra la trasmissione di un byte ogni 2 ms circa, coerentemente con il fatto che la funzione EUSART_Write(0x50) utilizza per la sua esecuzione un tempo trascurabile e quindi il tempo di esecuzione complessivo del ciclo dipende praticamente solo dalla macro __delay_ms(2);:

Trasmissione di un byte ogni 2 ms

Il seguente esempio apparentemente è simile: ritardo di 10 ms seguito dalla trasmissione di 8 byte, sempre a 9600 baud:

while (1) {
 __delay_ms(10);

 EUSART_Write(0x10);
 EUSART_Write(0x20);
 EUSART_Write(0x30);
 EUSART_Write(0x40);
 EUSART_Write(0x50);
 EUSART_Write(0x60);
 EUSART_Write(0x70);
 EUSART_Write(0x80);
}

Anche in questo caso il tempo richiesto per la trasmissione (8,32 ms) è inferiore al ritardo prodotto dalla macro  __delay_ms(10);

Il diagramma temporale mostra la trasmissione di 8 byte ogni 16 ms abbondanti. Vediamo di calcolare questo tempo:

Trasmissione di burst di 8 byte

Ricezione senza interrupt

La ricezione è altrettanto semplice ed utilizza una funzione ed una macro, dall'ovvio significato e documentate nel file eusart.h:

uint8_t EUSART_Read(void);

EUSART_DataReady

I byte ricevuti, fino ad un massimo di tre, sono accodati nel registro FIFO hardware di ricezione.

Il modo più semplice per verificare il funzionamento è la creazione di una connessione fisica tra il pin Tx ed il pin Rx del PIC (loopback): in questo modo quanto è trasmesso viene immediatamente ricevuto.

while (1) {
 __delay_ms(10);
 EUSART_Write(0xA0);  // Write a byte to Tx FIFO

 while (!EUSART_DataReady); // Wait until a byte is ready
 myData = EUSART_Read();    // Read the byte from Rx FIFO
 Nop();
 Nop();
}

Alcune osservazioni:

Uso delle interruzioni per trasmettere

L'uso delle interruzioni per la trasmissione seriale rende il codice non bloccante e permette la trasmissione e, soprattutto, la ricezione mentre il processore è in altre faccende affaccendato. In pratica vengono creati due buffer FIFO software, uno per la trasmissione ed uno per la ricezione, gestiti tramite interrupt.

Un tipico diagramma temporale è il seguente:

hello World!

La configurazione con MCC è, al solito rapida:

Il main() è piuttosto convenzionale:

#include <string.h>

void main(void) {
 char myData[]="hello World!";
 int len;

 SYSTEM_Initialize();
 INTERRUPT_GlobalInterruptHighEnable();
 INTERRUPT_GlobalInterruptLowEnable();

 while (1) {
  len = strlen(myData);
  for (uint8_t i = 0; i < len; i++) // Send string
   EUSART_Write (myData[i]);

  for(int j=0; j<2000; j++) // Blink LED
   LED_TEST_Toggle();
 }
}

Si noti che in questo caso la funzione EUSART_Write() non scrive direttamente nel registro hardware di trasmissione, ma accoda il byte nel buffer FIFO software di trasmissione. Se i byte il buffer FIFO è  sufficientemente grande quindi questa funzione ha un tempo di esecuzione molto breve.

Se viene attivato il flag Redirect STDIO to USART  nella finestra di configurazione di MCC è possibile l'uso delle funzioni standard di I/O del C, in genere usate nelle applicazioni console. Per esempio  è possibile sostituire il ciclo for() del precedente esempio con:

printf("%s", myData );

Uso delle interruzioni per ricevere

Se sono attivate le interruzioni, tutti i byte ricevuti sono accodati nel buffer FIFO software di ricezione

Il codice per leggere il buffer con i dati ricevuti richiede l'uso della macro EUSART_DataReady  e della funzione EUSART_Read(). Per esempio, il codice seguente permette di verificare il funzionamento della lettura con l'uso di un semplice filo che collega il pin Tx con il pin Rx del PIC (loop-back):

len = strlen(dataWrite);
for (uint8_t i = 0; i <= len; i++) // Write a string to UART Tx FIFO
    EUSART_Write(dataWrite[i]);

k=0;
while EUSART_DataReady {         // Any data in Rx FIFO?
   dataRead[k] = EUSART_Read();  // Read back the string from UART Rx FIFO
   k++;
}

Un esempio

Questo codice di esempio è solo presentato per sommi capi e senza commenti nel codice in quanto oggetto degli esercizi.

L'obbiettivo è quello di scambiare dati tra un PIC18 ed un Raspberry Pi attraverso una comunicazione seriale:

Il collegamento è piuttosto ovvio:

Ovviamente la frequenza di clock deve essere la stessa (nell'esempio 115 200 baud)

Un altro esempio

Spesso i microcontrollori comunicano attraverso un segnale RS485.

Il diagramma temporale seguente mostra dall'alto:

L'aspetto delicato è legato alla disattivazione del trasmettitore RS485, da fare appena terminata la trasmissione del frame. In genere si preferisce non basarsi sul tempo di trasmissione, che dipende dall'eventuale presenza nel buffer di altri pacchetti, ma ascoltare la propria trasmissione e riconosce la fine.

Lo spezzone di codice seguente è quello che ha generato il diagramma temporale:

 DE_SetHigh(); // Enable RS485 trasmitter
EUSART_Write(MAGIC); // Write 4 byte
EUSART_Write(0x33);
EUSART_Write(0x44);
EUSART_Write(0x55);

while (EUSART_DataReady < 4); // Wait until 4 byte are in Rx buffer

DE_SetLow(); // Disable transmitter

data[0] = EUSART_Read(); // Flush buffer
data[1] = EUSART_Read();
data[2] = EUSART_Read();
data[3] = EUSART_Read();

Quella mostrata non è una soluzione particolarmente robusta, ma è la base per costruire un rete RS485. Si veda a questo proposito il terzo esercizio

Codice

Esercizi

Osservazioni

  1. Non sempre è possibile ottenere esattamente il clock desiderato, soprattutto se il processore ha una frequenza di clock bassa e/o la velocità richiesta è elevata. Come linea guida è bene mantenere l'errore percentuale visualizzato limitato a poche unità.
  2. La funzione di inizializzazione EUSART_Initialize() è inserita automaticamente da MCC all'interno di SYSTEM_Initialize()
  3. Questa funzione è quindi poco adatta nei sistemi real-time o all'interno di interrupt in quanto di durata a priori imprevedibile: rapida se il buffer è vuoto, lenta se il buffer contiene ancora il byte precedente. Tecnicamente si dice che EUSART_Write() è una funzione bloccante
  4. Il codice è stato scritto per essere eseguito dal processore a 64 MHz. Frequenza del UART elevate richiedono frequenza elevata anche al processore, sebbene questa affermazione sia da prendere come regole di buon senso più che vincolo tecnico

 


Data di creazione di questa pagina: giugno 2016
Ultima modifica: 29 marzo 2017


Licenza Creative Commons Attribuzione 4.0 Internazionale


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima