[原创] 【瑞萨电子200MHz Cortex-M33 RA6E2评估板】基于循环缓冲区接收的串口驱动

qinyunti   2023-10-18 18:05 楼主

前言

前面使用Demo进行了串口的收发测试,为了后面方便的使用串口,我们需要进行一些封装,实现串口的收发接口。

 

设计思路

核心思路是设计环形缓冲区的FIFO,串口接收中断中将数据写入FIFO,满则丢弃

用户接收API查询FIFO有数据就读出无数据就等待。

中断和用户API都会操作FIFO所以需要对FIFO进行临界段操作保护。

对于发送也可以类似,不过这里为了简单,发送就使用查询方式。

 

image-20231018180050-1.png  

设计过程

数据结构

#include <stdint.h>

环形缓冲区数据结构如下

typedef struct

{

        uint32_t datalen_u32;

        uint32_t maxlen_u32;

        uint32_t in_u32;

        uint32_t out_u32;

        uint8_t* buffer_pu8;

}ring_buffer_t;

 定时缓冲区存接收数据

uint8_t uart_ring_buffer[128];

 

实例化一个缓冲区

ring_buffer_t s_ring_buffer_t=

{

.datalen_u32 = 0,

.maxlen_u32 = sizeof(uart_ring_buffer),

  .in_u32 = 0,

  .out_u32 = 0,

  .buffer_pu8 = uart_ring_buffer,

};

临界段处理

Rx API中操作FIFO时临界段通过关中断实现

#include "bsp_api.h"



#define Alloc_Critical() FSP_CRITICAL_SECTION_DEFINE

#define Enter_Critical() FSP_CRITICAL_SECTION_ENTER

#define Exit_Critical()   FSP_CRITICAL_SECTION_EXIT

接收中断处理

void uart_rx_handler(const uint8_t *buffer, uint32_t length)

{

    uint32_t i;

    for(i=0;i<length; i++)

    {

        if(s_ring_buffer_t.datalen_u32 < s_ring_buffer_t.maxlen_u32)

        {

            s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.in_u32] = buffer[i];

            s_ring_buffer_t.datalen_u32++;

            s_ring_buffer_t.in_u32++;

            s_ring_buffer_t.in_u32 %= s_ring_buffer_t.maxlen_u32;

        }

        else

        {

            /* full */

            break;

        }

    }

}

 

Uart_ep.c中

#include "uart.h"

void user_uart_callback(uart_callback_args_t *p_args)

{

    /* Logged the event in global variable */

g_uart_event = (uint8_t)p_args->event;

if(UART_EVENT_RX_CHAR == p_args->event)

    {

uint8_t tmp = (uint8_t ) p_args->data;

uart_rx_handler(&tmp, 1);

}

}

接收API

uint32_t uart_read(uint8_t *buff, uint32_t len)

{

    uint32_t readlen = 0;

    //uint32_t mask;

    if(s_ring_buffer_t.datalen_u32 == 0)

    {

        return 0;

}

Alloc_Critical();

    Enter_Critical();

    uint32_t i;

    for(i=0;i<len;i++)

    {

if(s_ring_buffer_t.datalen_u32 > 0)

      {

buff[i] = s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.out_u32];

s_ring_buffer_t.datalen_u32--;

s_ring_buffer_t.out_u32++;

s_ring_buffer_t.out_u32 %= s_ring_buffer_t.maxlen_u32;

readlen++;

}

else

{

break;

}

    }

    Exit_Critical();

    return readlen;

}

 

发送API

int uart_write(uint8_t *buff, uint32_t len)

{

uint32_t i;

for(i=0; i<len ;i++)

{

uart_sendbyte(buff[i]);

}

return 0;

}

 

uart_sendbyte在uart_ep.c中实现

void uart_sendbyte(uint8_t ch)

{

uint8_t tmp = ch;

fsp_err_t err   = FSP_SUCCESS;

    uint32_t local_timeout = (DATA_LENGTH * UINT16_MAX);

    /* Reset callback capture variable */

    g_uart_event = RESET_VALUE;



    /* Writing to terminal */

    err = R_SCI_UART_Write (&g_uart_ctrl, &tmp, 1);

    if (FSP_SUCCESS != err)

    {

        return;

    }



    /* Check for event transfer complete */

    while ((UART_EVENT_TX_COMPLETE != g_uart_event) && (--local_timeout))

    {

        /* Check if any error event occurred */

        if (UART_ERROR_EVENTS == g_uart_event)

        {

            return;

        }

    }

    return;

}

串口初始化

使用原来的uart_initialize

测试

uint8_t buffer[128];

for(;;)

{

uint32_t len=0;

if((len = uart_read(buffer, sizeof(buffer))) >0)

  {

     uart_write(buffer, len);

  }

  }

上位机发送,开发板原样返回,收发数据一致说明功能OK

image.png  

代码

uart.c

#include <stdint.h>
#include "uart.h"
#include "bsp_api.h"

#define Alloc_Critical() FSP_CRITICAL_SECTION_DEFINE
#define Enter_Critical() FSP_CRITICAL_SECTION_ENTER
#define Exit_Critical()   FSP_CRITICAL_SECTION_EXIT
			
typedef struct
{
        uint32_t datalen_u32;
        uint32_t maxlen_u32;
        uint32_t in_u32;
        uint32_t out_u32;
        uint8_t* buffer_pu8;
}ring_buffer_t;

 
uint8_t uart_ring_buffer[128];

ring_buffer_t s_ring_buffer_t=
{
	.datalen_u32 = 0,
	.maxlen_u32 = sizeof(uart_ring_buffer),
  .in_u32 = 0,
  .out_u32 = 0,
  .buffer_pu8 = uart_ring_buffer,
};

void uart_rx_handler(const uint8_t *buffer, uint32_t length)
{
    uint32_t i;
    for(i=0;i<length; i++)
    {
        if(s_ring_buffer_t.datalen_u32 < s_ring_buffer_t.maxlen_u32)
        {
            s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.in_u32] = buffer;
            s_ring_buffer_t.datalen_u32++;
            s_ring_buffer_t.in_u32++;
            s_ring_buffer_t.in_u32 %= s_ring_buffer_t.maxlen_u32;
        }
        else
        {
            /* full */
            break;
        }
    }
}

uint32_t uart_read(uint8_t *buff, uint32_t len)
{
    uint32_t readlen = 0;
    //uint32_t mask;
    if(s_ring_buffer_t.datalen_u32 == 0)
    {
        return 0;
    }
		Alloc_Critical();
    Enter_Critical();
    uint32_t i;
    for(i=0;i<len;i++)
    {
			if(s_ring_buffer_t.datalen_u32 > 0)
      {
				buff = s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.out_u32];
				s_ring_buffer_t.datalen_u32--;
				s_ring_buffer_t.out_u32++;
				s_ring_buffer_t.out_u32 %= s_ring_buffer_t.maxlen_u32;
				readlen++;
			}
			else
			{
				break;
			}
    }
    Exit_Critical();
    return readlen;
}

int uart_write(uint8_t *buff, uint32_t len)
{
	uint32_t i;
	for(i=0; i<len ;i++)
	{
		uart_sendbyte(buff);
	}
	return 0;
}

uart.h

#ifndef UART_H
#define UART_H

#include <stdint.h>

void uart_rx_handler(const uint8_t *buffer, uint32_t length);
uint32_t uart_read(uint8_t *buff, uint32_t len);
int uart_write(uint8_t *buff, uint32_t len);

extern void uart_sendbyte(uint8_t ch);

#endif

总结

依赖底层的串口驱动,实现了基于循环缓冲区FIFO的串口驱动接口,方便后面应用层使用。

得益于官方完善的驱动和Demo可以很快速的实现。

回复评论 (1)

这个点用内存空间大吗?其实可以上RTT,他带的shell更强大。

点赞  2023-10-18 19:46
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复