Come abilitare la ricezione degli interrupt da USART

Gli AVR e la gran parte dei microcontrollori, dispongono di una caratteristica conosciuta come Interrupt. Come il nome stesso implica, l’interrupt permette agli eventi esterni (come un input da utente o dall’unità periferica dell’AVR) di bloccare il programma principale, di eseguire l’ISR (Interrupt Service Routine) e solo dopo riprendere  il programma dove si era interrotto. Gli interrupt sono estremamente utili per gestire input irregolari (come il cambio di un pin) o per eseguire “background tasks” come il lampeggio di un LED nel caso di un timer overflow. Per questo esempio stiamo usando un MEGA16. Partiamo da un semplice programma per l’utilizzo dell’interfaccia per la comunicazione seriale USART (listato 1).

#include <avr/io.h>
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU /
        (USART_BAUDRATE * 16UL))) - 1)
int main (void)
{

    char ReceivedByte;
    UCSRB |= (1 << RXEN) | (1 << TXEN);
 // Turn on the transmission and
 //reception circuitry
    UCSRC |= (1 << URSEL) | (1 << UCSZ0) |
 (1 << UCSZ1); // Use 8-bit character sizes
    UBRRL = BAUD_PRESCALE; // Load lower
 //8-bits of the baud rate value into the
 //low byte of the UBRR register
    UBRRH = (BAUD_PRESCALE >> 8); // Load
 //upper 8-bits of the baud rate value into
 //the high byte of the UBRR register
    for (;;) // Loop forever
    {

       while ((UCSRA & (1 << RXC)) == 0)
 {}; // Do nothing until data have been
 //received and is ready to be read from UDR
       ReceivedByte = UDR; // Fetch the
 //received byte value into the variable
 //“ByteReceived”
        while ((UCSRA & (1 << UDRE)) == 0)
 {}; // Do nothing until UDR is ready for
 //more data to be written to it
        UDR = ReceivedByte; // Echo back
//the received byte back to the computer
        }
        }
Listato 1

Il nostro obiettivo è di estendere tale codice in modo tale che il dato seriale sia rinviato come echo quando si riceve un interrupt. Per fare questo abbiamo bisogno di includere l’AVRLIBC standard library header, avr/interrupt.h. Questo file contiene librerie di funzioni e macros relative alle funzionalità di interrupt dell’AVR. Ora dobbiamo posizionare nel programma una funzione che permetta di eseguire l’ISR e posizionarla nel codice come una normale funzione.

ISR({Vector Name})

{//Code to be executed when ISR fires}

La funzione in questione richiede un vector name che dovrà essere cercato nel datasheet dell’AVR; nel nostro caso usando un MEGA16 il nome dell’interrupt in caso di ricezione di Byte è “USART_RXC_vect”. La libreria standard avr/io.h include tutte le funzioni di I/O dell’AVR definendone  i relativi vector name.

ISR(USART_RXC_vect)

{//Code to be executed when the USART receives a byte here}

Ora che abbiamo definito  il tipo di sintassi dobbiamo popolare la funzione ISR ossia scrivere il  codice che verrà compilato quando la funzione viene invocata. Quando l’ISR si abilita, sappiamo che si è ricevuto un bit nel buffer di ingresso USART mentre non vi è nulla nel buffer di uscita. Seguendo questo ragionamento possiamo arrivare a questo codice molto semplice (listato 2).

ISR(USART_RXC_vect)
{
       char ReceivedByte;
       ReceivedByte = UDR; // Fetch the recieved byte value into
the variable “ByteReceived”
       UDR = ReceivedByte; // Echo back the received byte back to
the computer
}
Listato 2

Quando si ricevono dei dati, la procedura completa di ricezione prevede la lettura di tutti i dati ricevuti dall’UDR per poi liberare l’RXC flag.

Ora vediamo come abilitare l’USART per la ricezione di interrupt, visto che fino ad ora le aggiunte di codice hanno permesso di definire la funzione ISR ma non di abilitarla. Per fare questo è necessario fare due cose:

1- Abilitare il Global interrupt enable flag.

2- Abilitare l’USART per la ricezione degli interrupt.

Il primo obiettivo è relativamente facile quindi cominceremo da quello. I microcontrollori AVR contengono una global flag grazie alla quale settandola o cancellandola possiamo abilitare o meno il controllo manuale degli interrupt. Se il Global interrupt enable flag è disabilitato tutti gli interrupt verranno ignorati, è importante capire che l’abilitazione della global flag permette di ricevere gli interrupt ma non li esegue. Ora che abbiamo definito  il global interrupt flag possiamo abilitarlo usando la macro sei(), funzione già definita nella libreria avr/interrupt.h che permette di generare un’istruzione  SEI in assembler, che il nostro AVR interpreterà come ordine di abilitazione del global interrupt flag. Posizioneremo questa macro appena prima del for(;;). Anche se non useremo questa macro è utile conoscere il complementare della funzione sei() ossia cli() che come possiamo aspettarci disabiliterà  il flag. Ora dobbiamo occuparci dell’obiettivo numero 2, che però deve essere impostato prima che nel programma principale venga abilitato il  global flag interrupt. Nel nostro caso usando un MEGA16 useremo RXCIE (Receive Complete Interrupt Enable) che fa parte dell’UCSRB. Settando questo bit possiamo abilitare la funzione ISR precedentemente dichiarata. Inseriremo questa parte di codice prima della macro “sei()”. Nel listato 3 possiamo quindi vedere il nostro codice completo, e pronto a ricevere interrupt.

#include <avr/io.h>
#include <avr/interrupt.h>
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
int main (void)
{
        UCSRB |= (1 << RXEN) | (1 << TXEN); // Turn on the transmission
and reception circuitry
        UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use
8-bit character sizes
        UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate
value into the low byte of the UBRR register
        UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the
baud rate value into the high byte of the UBRR register
        UCSRB |= (1 << RCXIE); // Enable the USART RECEIVE Complete
interrupt (USART_RXC)
        sei(); // Enable the Global Interrupt Enable flag so that
interrupts can be processed
        for (;;) // Loop forever
        {
                // Do nothing - echoing is handled by the ISR in-
        stead of in the main loop
        }
}
ISR(USART_RXC_vect)
{
        char ReceivedByte;
        ReceivedByte = UDR; // Fetch the recieved byte value into
the variable “ByteReceived”
        UDR = ReceivedByte; // Echo back the received byte back to
the computer
}
Listato 3

 

 

Una risposta

  1. Maurizio Di Paolo Emilio Maurizio 21 novembre 2016

Scrivi un commento