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
(Criou página com 'This experiment is part of this project. Here we are going to do an experiment to implement a real ...')
 
 
(23 revisões intermediárias por 2 usuários não estão sendo mostradas)
Linha 1: Linha 1:
 
This experiment is part of [[Embedded_Systems_Experiments_for_the_Telecommunication_Engineering_Course|this project]].
 
This experiment is part of [[Embedded_Systems_Experiments_for_the_Telecommunication_Engineering_Course|this project]].
  
Here we are going to do an experiment to implement a real aplication with UART. The exercise proposed here is to implement a communication between two boards, namelly two Arduinos Uno. A led on the second arduino(slave board) will blink according to the data received from the first arduino(master board). Therefore we need to programm to types of code: one for the master(sender) board and one for the slave board(receiver). In order to understand all the concepts used in this experiment it is recommended to see at first the basic UART script.  
+
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 [http://wiki.sj.ifsc.edu.br/index.php/ESTE:_UART_-_Basic_Interrupts UART - Basic Interrupts] script.  
  
 
==Pseudo code==
 
==Pseudo code==
  
Let's begin with a solution. The pseudo code of the master an slave boards, respectively, is below:
+
Let's begin with a solution. The pseudo code (actual coding is up to you) is below:
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
//master  - sender
+
 
 +
// We need 2 buffers, one to store data received from serial, and another to store data that will be sent through the serial
 +
byte rx_buf[BUFFER_SIZE];
 +
byte tx_buf[BUFFER_SIZE];
 +
 
 
int main(void) {
 
int main(void) {
  int num;
+
    //Configure serial to 9600 8N1
  baud_rate = 9600;
+
    baud_rate = 9600;
  while(1) {
+
    data_len = 8;
        num = 0;
+
    parity = 0;
        println(num);
+
    stop_bits = 1;
         delay_2000_ms();
+
 
        num = 1;
+
    //Implement loopback application - just remove older byte in the receive queue and enqueue it on the output buffer
        println(num);
+
    while(1)
         delay_2000_ms();
+
         if(rx_buffer_has_data()){
 +
            byte aux = pop(rx_buf);
 +
            push(tx_buf, aux);
 +
         }
 
     }
 
     }
     return 0 ;
+
     return 0;
 +
}
 +
 
 +
// Finished sending one byte
 +
tx_interrupt_handler(){
 +
    byte aux = pop(tx_buf);
 +
    send(aux);
 +
}
 +
 
 +
// Finished receiving one byte
 +
rx_interrupt_handler(){
 +
    byte aux = receive();
 +
    push(rx_buf, aux);
 
}
 
}
//slave - receiver
 
int main(void) {
 
  int byte_received = 0;
 
  int baud_rate = 9600;
 
  int led;
 
  while(1) {
 
      if (data_input_field > 0 ) {
 
          byte_received = read_data_on_input_field();
 
      }
 
      led = byte_received;
 
  }
 
  return 0;   
 
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The master pseudo-code always varies a value to be send via the serial port. The slave pseudo-code has a comparison statement to check if the data_input_field is already filled and, if so, we know a data has been sent from the serial port and then the receiver board stores the data on the memory board and turns the led on or off according to the byte_received.
+
<!--
 +
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==
 
==Schematic==
[[Arquivo:Uart_2arduinos_AVR_Esquemático_new.png|600px|thumb|center|Fig. 1 Schematic of the circuit for the AVR microcontroller]]
+
[[Arquivo:Uart_interrupt_basic_Esquemático.png|450px|thumb|center|Fig. 1 Schematic of the circuit]]
[[Arquivo:Uart_2arduinos_IDE_Esquemático_new.png|600px|thumb|center|Fig. 1.1 Schematic of the circuit for the Arduino IDE]]
 
  
 
==Part List==
 
==Part List==
 
*1 LED
 
*1 LED
*2 Arduinos UNO
+
*1 220 ohm resistor for the led
*1 220 ohm resistor
 
 
*1 Protoboard
 
*1 Protoboard
*1 9V Batery (just for the Arduino IDE circuit)
+
*2 copper wires (tinned) or jumpers
*6 copper wires (tinned) or jumpers
+
 
 
<br/>
 
<br/>
  
 
==Assembly==
 
==Assembly==
[[Arquivo:Uart_2arduinos_AVR_bb_new.png|600px|thumb|center|Fig. 2 Assembly of the circuit for the AVR microcontroller]]
+
[[Arquivo:Uart_interrupt_basic_bb.png|450px|thumb|center|Fig. 2 Assembly of the circuit]]
[[Arquivo:Uart_2arduinos_IDE_bb.png|600px|thumb|center|Fig. 2.1 Assembly of the circuit for the Arduino IDE]]
 
  
 
==Solutions==
 
==Solutions==
*[https://drive.google.com/open?id=0B_chZ-d1CkpCSjdjeTB5T2RDM1E C - AVR (master)]
+
*[https://drive.google.com/open?id=0B_chZ-d1CkpCa1lhaEczNjZvOVE C - AVR]
*[https://drive.google.com/open?id=0B_chZ-d1CkpCc0hhNU5HWUpIbUk Arduino UNO (master)]
+
*[https://drive.google.com/open?id=0B_chZ-d1CkpCSUdnN0VnNEJsb1E Arduino UNO]
*[https://drive.google.com/open?id=0B_chZ-d1CkpCaklFN2xwQXRUWlk C - AVR (slave)]
 
*[https://drive.google.com/open?id=0B_chZ-d1CkpCdlhmYkpNZHhDUXc Arduino UNO (slave)]
 
  
 
==Tools==
 
==Tools==
Linha 67: Linha 74:
  
 
==References==
 
==References==
*[http://lab.guilhermemartins.net/2008/12/24/serial-comunication-between-arduinos-with-wire-wireless/ Serial Comunication between Arduinos]
+
<!--
*[http://arduinizando.blogspot.com.br/2013/02/comunicacao-serial-entre-arduinos.html Arduinizando: Comunicação serial entre arduinos]
+
*[http://db.zmitac.aei.polsl.pl/Electronics_Firm_Docs/ATMEL/Atmel/acrobat/doc1451.pdf Atmel: Using the AVR UART in C]
*[http://harduino.me/2013/11/30/comunicacao-serial-entre-dois-arduinos/ Harduino: Comunicação Serial Entre Dois Arduinos]
+
-->
*[https://www.youtube.com/watch?v=U1kr9gYviMc Video Tutorial: Microcontroller - AVR ATmega32 - One Way UART Communication with Two Chips]
+
*[http://www.embarcados.com.br/arquitetura-de-desenvolvimento-de-software-ii/ Embarcados: Arquitetura de desenvolvimento de software]
 +
*[http://leap-embedded-system.com/?p=79 LEAP: An Interrupt Driven Circular Buffer for Serial Communication]
 +
*[http://www.simplyembedded.org/tutorials/interrupt-free-ring-buffer/ Simply Embedded: UART Receive Buffering]
 +
*[http://www.downtowndougbrown.com/2013/01/microcontrollers-interrupt-safe-ring-buffers/ Downtown Doug Brown: Microcontrollers: Interrupt-safe ring buffers]
 +
*[http://www.avrfreaks.net/forum/tutsoft-interrupt-driven-uart-receive-ring-fifo-buffer AVR Freaks: Interrupt Driven UART Receive Ring (FIFO) Buffer]
 +
*[https://code.google.com/p/arduino-buffered-serial/ Google Code: Arduino buffered serial]

Edição atual tal como às 08h48min de 3 de novembro 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:

// We need 2 buffers, one to store data received from serial, and another to store data that will be sent through the serial
byte rx_buf[BUFFER_SIZE];
byte tx_buf[BUFFER_SIZE];

int main(void) {
    //Configure serial to 9600 8N1
    baud_rate = 9600;
    data_len = 8;
    parity = 0;
    stop_bits = 1;

    //Implement loopback application - just remove older byte in the receive queue and enqueue it on the output buffer
    while(1) {  
        if(rx_buffer_has_data()){
            byte aux = pop(rx_buf);
            push(tx_buf, aux);
        }
    }
    return 0;
}

// Finished sending one byte
tx_interrupt_handler(){
    byte aux = pop(tx_buf);
    send(aux);
}

// Finished receiving one byte
rx_interrupt_handler(){
    byte aux = receive();
    push(rx_buf, aux);
}


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