Mudanças entre as edições de "ESTE: UART - Interrupts and Circular Buffer"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
Linha 17: Linha 17:
 
rx_buf[buffer_size] and tx_buf[buffer_size];
 
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
+
/*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;
 
volatile rx_index_top, rx_index_down, tx_index_top, tx_index_down = 0;
  

Edição das 12h48min de 16 de outubro de 2015

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 */
}

This pseudo-code has a main program with an infinite loop responsable of checking if there are bytes on the rx_buffer - already received from the serial - that may be copied from the rx_buffer to the tx_buffer and later send to the serial port. There ara also interrupt handlers - called by actions/events on the serial port- responsable for each functionality of sending and receiving data. We also have buffers to store bytes received and that are going to be sent and indexes that indicate positions to add new data and catch/fetch old data from the buffers. The functions we need to have are “stored_transmit” - take data received on rx_buffer and store on tx_buffer -, “stored_receive”- return data from the rx-buffer -, “tx_interrupt_handler” - send data from the tx_buffer to the serial port - and “rx_interrupt_handler”, who adds new data received from the serial port on the rx_buffer. The differences between the indexes we have are the following: The “rx_index_top” has the location where a next incoming byte will be stored. In other words this index always point to 1 location ahead of the last received byte. The “rx_index_down” has the location where the next available data can be fetched/collected and always follow behind “rx_index_top”. Similarly, the “tx_index_top” has the location where a new incoming byte will be stored and the “tx_index_down” has the location where the next available data can be collected and send to the serial port. Also “tx_index_down”, always follow behind “tx_index_top”. We still need to to clarify that when we have a logic operation as “((rx_index_down + 1) & (buffer_size -1))” it simply means that we will take the atual position of the last byte we want to catch. For example if we have rx_index_down = 0010, buffer_size = 1111, (rx_index_down + 1) = 0011 and then (buffer_size - 1) = 1110 we will do (0011 & 1110) = 0010 and store this value later on rx_index_down to return the correspondent byte in this 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