Description

100%
100%

Few months ago, I made a tutorial about circular buffer in STM32 using DMA and idle line detection. Although it was working pretty well, it was a little bit complex to work with and had few problems. Today in this tutorial, I am going to show you guys an alternative for that. Here, we are going to implement ring buffer using head and tail method in STM32.

This tutorial is written in STM32CUBEIDE. It can work with other IDEs also, you just have to use the library files. You can download the code at the end of the post. The library files are inside the project folders itself.

How the ring buffer works

Well, I would suggest that you google this query and take a look at the wikipedia page. There you will get a better explanation of the topic. But anyway, i will try to explain it, as much as i can.

Whenever UART receives data in the rx_buffer, the head gets incremented by 1. And when we read that data, tail gets incremented by 1. Now let’s assume that we sent a string “hello“. So the UART received 5 characters and they get stored in the rx_buffer. So head will now have a value of 5. The tail is still at 0, as we haven’t read the characters yet. If we read the first character, ‘h‘, tail will get incremented by 1. And once we read the entire string, head and tail will both be at 5.

Once the buffer is full, or the head is at the end of the buffer, on receiving new character, it will again go to the beginning of the buffer i.e at 0.

Reading the buffer, so that the tail can increment, is very important part of this process. The data will only be written in circular direction, if the data at that position (start of buffer) is already read. In other words, if the difference between head and tail is more than the buffer size, no data will be received in the buffer.

HOW TO

Setting up the ring buffer in STM32 is very easy, Easier than circular buffer at least. Below is my Cubemx setup. I am using baud rate of 115200 and interrupt is enabled. The rest is the usual setup.

CubeMx setup for uart

Next copy the uartringbuffer.h and uartringbuffer.c files in the code and also include it in the main file. These files are located under /src and /inc folders of the code attached here. You should also copy them to the same locations in your project.

Once copied, open the stm32….it.c file and copy extern void Uart_isr (UART_HandleTypeDef *huart); in the file. And at last we need to replace the default ISR with the one we have. So browse down the file to the void USART1_IRQHandler(void) and replace the default ISR just as shown in the picture below

ISR file edit

This completes the setup part Now let’s take a look inside some functions available.

Some Insight into the CODE

int IsDataAvailable(void);

checks whether the data is available to read. It returns 1 is there is some data in the rx_buffer, which is not read yet.

int Uart_read(void);

Reads the data from the rx_buffer and increment the tail by 1. The head in the rx_buffer get incremented when the data is received in the buffer via the interrupt. only reads 1 character and returns it.

/* Peek for the data in the Rx Bffer without incrementing the tail count 
* Returns the character
* USAGE: if (Uart_peek () == 'M') do something 
*/
int Uart_peek();

Uart_peek Peek for the data in the Rx Buffer without incrementing the tail count. This is useful, if you want to check a character or a string, without reading the data. It returns the character.

void Uart_write(int c); 

writes the data to the tx_buffer and increment the head counter in the tx_buffer by 1. From where, the data is sent to the uart using interrupt and tail get incremented. This function only writes 1 character at a time.

void Uart_sendstring(const char *s);

prints the entire string to the uart.

void Uart_printbase (long n, uint8_t base);

prints the numbers in different format to the uart.

/* Copy the data from the Rx buffer into the buffer, Upto and including the entered string 
* This copying will take place in the blocking mode, so you won't be able to perform any other operations
* Returns 1 on success and -1 otherwise
* USAGE: while (!(Copy_Upto ("some string", buffer)));
*/
int Copy_upto (char *string, char *buffertocopyinto);

Copy_upto Copies the data from the Rx buffer into the entered buffer. But it will copy until the entered string is reached.

/* Copies the entered number of characters (blocking mode) from the Rx buffer into the buffer, after some particular string is detected
* Returns 1 on success and -1 otherwise
* USAGE: while (!(Get_after ("some string", 6, buffer)));
*/
int Get_after (char *string, uint8_t numberofchars, char *buffertosave);

Get_after function is used to get the predefined number of characters after the entered string is detected in the incoming data. It will save the characters into the entered buffer.

/* Wait until a paricular string is detected in the Rx Buffer
* Return 1 on success and -1 otherwise
* USAGE: while (!(Wait_for("some string")));
*/
int Wait_for (char *string);

Wait_for will wait until a predefined string is received in the Rx buffer. If it does, than the function will return 1.

/* Look for a particular string in the given buffer
 * @return 1, if the string is found and -1 if not found
 * @USAGE:: if (Look_for ("some string", buffer)) do something
 */
int Look_for (char *str, char *buffertolookinto);

Look_for will look for a particular string inside the given buffer. If the string is found, it will return 1.

Let’s see an example to read and write the data

while (IsDataAvailable())
{
  int data = Uart_read();  // read the data in the rx_buffer
  Uart_write(data);  // send the data to the uart 
}

So, if the data is available in the rx_buffer, it will be read using Uart_read () function and than again written back to the uart. This way you can test whether the things are working properly or not. Below is the screenshot of the terminal

ring buffer working

Note that the pink color is the one that I am sending to the microcontroller and the black one is the received data back in the computer. As you can see above that either I sent 1 character, 2 characters or 5 characters, I always received the same data back from the microcontroller.

This way we can always receive the data of unknown length. Make sure that the size of the rx_buffer (in the uartringbuffer.h) is according to your need. If the data received at once, is higher than the size, the whole code will block there and you might have to reset the controller.

YOU CAN DOWNLOAD FULL CODE AT THE END OF THIS POST

In case you are using a microcontroller, which gave you error on Uart_isr function. Errors like DR and SR are not present, replace the Uart_isr code with the following file Uart_isr_single.c

100%
100%

Result

100%
100%
Check out the VIDEO Below
100%
100%

DOWNLOAD

You can buy me a coffee sensor ūüôā

download the CODE below

100%
100%
3 2 votes
Article Rating
Subscribe
Notify of
guest
9 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Menu
9
0
Would love your thoughts, please comment.x
()
x