ESTE: UART - Interrupts and Circular Buffer

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar

This experiment is part of this project.

Here we are going to do an experiment to aprimorate the UART and Interrupts concepts and implement a solution for problems with interrupts. In our last UART application we used the interrupts generated by the income and transmission of data on serial ports. Now we will have two types of interrupts, one for the income of data and one for the transmission of data. For that reason, in each interrupts handler we would need to deal with the income/transmission of bytes and treatment of them among other activities. But these other treatments and routines happen much more slowly when compared to data sending and incoming. Therefore we will handle all these another routines on the main program and we will use the interrupts only for the transmission/reception of bytes. We also need to store all the data we are receiving or sending by the interrupts temporarily until we handle them on the main program and thus we will use circular buffers to store the data that are going to be sent and received. But why a circular buffer? Circular buffer is extremely useful in buffering serial communication data for some reasons: it makes possible avoid data loss when processing the received data; permits memory to be reusable; is an array that wraps around – where it overwrites old data in the buffer if the microcontroller processing speed is unable to momentarily keep up with the incoming data rate; and is a FIFO (first-in-first-out) buffer. So the exercise here proposed is to send and receive bytes, from the serial port, with the interrupts and handle the data stored on buffers at the main program. To complete this experiment it is recommended to see at first the UART - Basic Interrupts script.

Pseudo code

Let's begin with a solution. The pseudo code (actual coding is up to you) is below:

/*it really doesn’t matter wich buffer_size will we use since we have a circular buffer that will reuse the memory space to store any data*/
buffer_size;
       
/*We need 2 buffers, one to store data received from serial, and another to store data that will be sent to the serial*/
rx_buf[buffer_size] and tx_buf[buffer_size];

/*All the variables and information that may be changed during the interrupts handler should be declared as “volatile”, as those data are constantly instable
volatile rx_index_top, rx_index_down, tx_index_top, tx_index_down = 0;

int main(void) {
    // Set frame format to 8 data bits, no parity, 1 stop bit and baud_rate of 9600
    baud_rate = 9600;

/*at the main program we will only store the data/bytes on the respectively buffers, the transmition and income of data will be done by the interrupts handler*/
    while(1) {  
   
        if(rx_buffer_has_data())  
          /*if there is any data in rx_buffer already received from serial, put that data on the tx_buffer*/
            stored_transmit(stored_receive());
        }
    }
    return 0;    
}

stored_transmit(data) {
    led_on();                   //led is on for longer time to identify the call of the transmit routine
    delay(300);               //delay in ms

    aux_position_data_available;         //temporarily auxiliar index;
   aux_position_data_available =      /*make a logic operation to check the next position with data available to send and store on the auxiliar index*/

 while (tx_buffer_is_empty);               //wait when the tx_buffer is empty
 /* Here:
store new data in tx buffer on the position of auxiliar index
update the correct tx_index who informs the data we want to send with the auxiliar index 
*/
}

stored_receive() {
    
    blink_led();        //led blinks for shorter time to identify the call of the receive routine

    aux_index;         //temporarily auxiliar index;
  while(rx_buffer_is_empty);   /*just wait, i.e do nothing, when the rx_buf is empty (index_top is always equal to index_down when the buffer is empty)*/

   aux_index =     /*make a logic operation to check the next position with data available to fetch/return and store on the auxiliar index*/


   /* Here:
update the correct rx_index - who informs the data we want to return-  with the auxiliar index 
return the data -returned_value- on the position aux_index of the rx_buffer
*/

    return returned_value;
}

tx_interrupt_handler(){
    aux_index;        //temporarily auxiliar index;

/*check if ther is still data on the tx_buffer to transmit */
    if(data_available_tx_buffer){             
          aux_index = /*make a logic operation to check the next position with data available to send and store on the auxiliar index. Therefore you may use the tx_index_down and the buffer_size informations*/

         correct_tx_index = aux_index; /*update the correct tx_index who informs the data we want to send with the auxiliar index */
           println(data_on_position_aux_index);
    }
}

rx_interrupt_handler(){
    aux_index;
    data = read_serial_port();
    
  aux_index = /*make a logic operation to check the next position of the rx_buffer available to add data and store it on the auxiliar index. Therefore you may use the rx_index_top and the buffer_size informations*/

  correct_rx_index = aux_index;   /*update the correct rx_index - who informs the position we want to add new receiving data-  with the auxiliar index*/
       
    /* store “data” in rx_buffer at the aux_index position */
}

Schematic

Fig. 1 Schematic of the circuit

Part List

  • 1 LED
  • 1 220 ohm resistor for the led
  • 1 Protoboard
  • 2 copper wires (tinned) or jumpers


Assembly

Fig. 2 Assembly of the circuit

Solutions

Tools

References