6 Replies - 1370 Views - Last Post: 29 May 2018 - 04:07 PM

#1 wolfrose   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 50
  • Joined: 20-January 16

Linear buffers vs circular buffers, which is best for uart

Posted 25 May 2018 - 05:39 PM

Hello,

I'm developing my uart libraries for AVR microcontroller, I read Peter Fleury's uart library which is a very good library which implement circular buffers to manage transmit/receive data with ISRs and get/put call functions.

My first try was to copy the library, then it didn't work, then I decided to develop my own library with direct buffering, in the following arrangement:
1. A counter to count the received ISR data and a pointer for get function.
2. A counter to count the data for put function and a pointer for transmit ISR.

Now, I'm trying to improve the code for read errors; like, full buffers.


My question, is that, is linear buffers very efficient like circular buffers for uart, or circular buffers are the best option and I should close my linear buffers based library and start a library based on circular buffers?

Is This A Good Question/Topic? 0
  • +

Replies To: Linear buffers vs circular buffers, which is best for uart

#2 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 6918
  • View blog
  • Posts: 23,525
  • Joined: 05-May 12

Re: Linear buffers vs circular buffers, which is best for uart

Posted 28 May 2018 - 07:32 AM

Circular buffers for the win! :)/>/>

The issue is when dealing with UARTs, you are typically responding to interrupts. Interrupt handlers must be fast because you are essentially getting a time slice of CPU time where everything else is practically halted for the entire machine. With a circular buffer, you only need to access the next available block. With a linear buffer, you will need to stop and allocate more memory, OR move things down to the front of the buffer to ensure that you always have room at the end of the buffer.

View Postwolfrose, on 25 May 2018 - 08:39 PM, said:

I should close my linear buffers based library and start a library based on circular buffers?

If you wrote your code with an eye towards modularity, you should be able to swap from linear to circular without affecting the rest of the code because the rest of your code would be simply calling PutInToBuffer() and GetNextFromBuffer() calls. The implementation behind those methods should not matter.
Was This Post Helpful? 2
  • +
  • -

#3 wolfrose   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 50
  • Joined: 20-January 16

Re: Linear buffers vs circular buffers, which is best for uart

Posted 28 May 2018 - 08:36 AM

View PostSkydiver, on 28 May 2018 - 07:32 AM, said:

Circular buffers for the win! :)/>/>/>

The issue is when dealing with UARTs, you are typically responding to interrupts. Interrupt handlers must be fast because you are essentially getting a time slice of CPU time where everything else is practically halted for the entire machine. With a circular buffer, you only need to access the next available block. With a linear buffer, you will need to stop and allocate more memory, OR move things down to the front of the buffer to ensure that you always have room at the end of the buffer.

Thank you very much for the reply, your answer is really interesting to me. I've done my library with interrupts, first I tried polling then moved to interrupts.

So, I've looked into Peter Fleury uart library based on circular buffers, it's very nice library it's done with circular buffers, and I tested the code which worked very well. Then I tried to copy the library with circular buffers but I didn't copy it accurately so it didn't work, so I decided to develop something different than circular buffers to my understanding which is linear buffers, it worked but the received text misses chars and maybe wrong words.

This is my implementation:

#include <avr/io.h>
#include <Arduino.h>
#include "uart_linear.h"

static volatile uint8_t rx_buf[buf_sz],tx_buf[buf_sz];
static volatile uint8_t rx_p,rx_cnt,tx_p,tx_cnt;
static volatile uint8_t rx_errors;

void uart_init_new(uint16_t ubrr)
{
    rx_p=0,tx_p=0,rx_cnt=0,tx_cnt=0;
    UBRR0H = (uint8_t)(ubrr>>8);
    UBRR0L = (uint8_t)ubrr;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0); //Enable Rx and Tx & Receive interrupt
    UCSR0C = (1<<USBS0)|(3<<UCSZ00);            //Set frame format: 8data, 2stop bit
}

uint16_t get_char(void)
{
    if (rx_cnt == 0)                        // when cnt is 0, it means
        return NO_DATA;                     // that rx ISR didn't receive anything
    else if (rx_p < rx_cnt)
        return rx_buf[rx_p++] + (rx_errors << 8);
        else
        {rx_cnt = 0;rx_p = 0;}              // when read everything reset counter and pointer    
}

ISR(USART_RX_vect)
{
    uint8_t status_mask,data_temp;
    data_temp = UDR0;
    status_mask = UCSR0A & ((1<<FE0)|(1<<DOR0)|(1<<UPE0));

    if (rx_cnt < buf_sz)
        rx_buf[rx_cnt++] = data_temp;       // just receive all with no limit
                                            // I didn't put else statement
                                            // because I didn't know what
                                            // else can be done if buffer
                                            // is full

    rx_errors |= status_mask;               // update error bits
}

void put_char(uint8_t data)
{
    if (tx_cnt < buf_sz)                    // start count the input data
        tx_buf[tx_cnt++] = data;            // 1st input location tx_cnt
    else
        while (tx_cnt == buf_sz);           // wait for free space

    UCSR0B |= (1<<UDRIE0);
}

ISR(USART_UDRE_vect)
{
    if (tx_p < tx_cnt)                      // check if pointer < counted data
        UDR0 = tx_buf[tx_p++];
        else
        {
            tx_p = 0;                       // when the pointer reach max value, reset it
            tx_cnt= 0;                      // also reset counter for new transmission
            UCSR0B &= ~(1<<UDRIE0);         // disable empty data register interrupt
        }
}

void put_s(uint8_t *str)
{
    while (*str)
        put_char(*str++);
}




Quote

If you wrote your code with an eye towards modularity, you should be able to swap from linear to circular without affecting the rest of the code because the rest of your code would be simply calling PutInToBuffer() and GetNextFromBuffer() calls. The implementation behind those methods should not matter.

You absolutely right! That's my goal, I wrote libraries, now I want to modify and tweak my work :)
Was This Post Helpful? 0
  • +
  • -

#4 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 6918
  • View blog
  • Posts: 23,525
  • Joined: 05-May 12

Re: Linear buffers vs circular buffers, which is best for uart

Posted 28 May 2018 - 07:13 PM

Sorry, I don't know enough about Arduino to understand the cryptic register or port names you are using. Two things did catch my eye:
  • You are not always running returning a value in your functions with return values.
  • Your put_char() waits for more room in the buffer and then does nothing once the is enough room.


If you want help with this code, I suggest opening a new thread in the C/C++ Programing Help forum (the parent of this forum). This forum is primarily for discussing the C/C++ languages and features.
Was This Post Helpful? 1
  • +
  • -

#5 wolfrose   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 50
  • Joined: 20-January 16

Re: Linear buffers vs circular buffers, which is best for uart

Posted 29 May 2018 - 01:25 AM

View PostSkydiver, on 28 May 2018 - 07:13 PM, said:

Sorry, I don't know enough about Arduino to understand the cryptic register or port names you are using. Two things did catch my eye:
  • You are not always running returning a value in your functions with return values.
  • Your put_char() waits for more room in the buffer and then does nothing once the is enough room.


1. It's OK, I know this forum is not for microcontrollers. I'm just want to search here about skills and solutions in dealing with incoming and outgoing data.

2. put_char() well, I tell you I'm not sure about the while loop I put there but, my idea is as follows:

- If the tx_cnt didn't reach the full buffer size, then put data into the buffer.
if (tx_cnt < buf_sz)                        // start count the input data
        tx_buf[tx_cnt++] = data;            // 1st input location tx_cnt


- Here, if the counter reached the full buffer size, it should loops until the transmission ISR sends one or more bytes. But I'm not sure if this while loop is the optimized way as it locks the program waiting for free room.
    else
        while (tx_cnt == buf_sz);           // wait for free space



Quote

If you want help with this code, I suggest opening a new thread in the C/C++ Programing Help forum (the parent of this forum). This forum is primarily for discussing the C/C++ languages and features.


OK, thank you very much for the advice and your help too. OK, I should start a new thread for God's will :)
Was This Post Helpful? 0
  • +
  • -

#6 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 6918
  • View blog
  • Posts: 23,525
  • Joined: 05-May 12

Re: Linear buffers vs circular buffers, which is best for uart

Posted 29 May 2018 - 10:43 AM

It will actually keep looping until all the bytes are sent. The issue is what happens next after line 61 of the ISR is executed? Your while loop will break out, and just go on to line 51. data is never put into the transmission buffer which is now empty.
Was This Post Helpful? 1
  • +
  • -

#7 wolfrose   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 50
  • Joined: 20-January 16

Re: Linear buffers vs circular buffers, which is best for uart

Posted 29 May 2018 - 04:07 PM

View PostSkydiver, on 29 May 2018 - 10:43 AM, said:

It will actually keep looping until all the bytes are sent.


OK, the way I'm understanding the while (tx_cnt == buf_sz); // wait for free space


Is that buf_sz is defined as 32, and when tx_cnt reaches this value, it should loop, and when the transmission ISR sends at least one byte ... but you know I realized!

OMG you're right, the while loop won't terminate until tx_cnt is reset. I should've put a decrement routine in the ISR. How about this idea? each time a byte is sent, it follows it a decrement for the counter.

Quote

The issue is what happens next after line 61 of the ISR is executed? Your while loop will break out, and just go on to line 51.data is never put into the transmission buffer which is now empty.


You know what?! I think linear buffers won't work for FIFO system as the buffer should be written/read all. I tried to think of a solution but couldn't! Circular buffers are the best solution, now I know that circular buffers are the only best solution available.

Here's the company's Application Note in the attachment for this specific application, I thought I can do it with linear buffers, it work but with definite errors. I must modify this library to circular buffers.

Attached File(s)


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1