前言
上一篇我们进行了串口的收发测试,要方便使用,需要给应用层提供好用的串口收发接口。这里通过环形缓冲区的方式实现串口的接收。
过程
临界段处理
由于缓冲区的基本数据流是串口接收中断中写缓冲区,读接口函数读缓冲区,存在多方使用缓冲区资源,所以对缓冲区资源访问时必须做临界段处理。这里简单的使用开关中断处理。
__disable_irq();
__enable_irq();
由于写缓冲区只在串口接收中断中进行,所以uart_rx_handler写缓冲区就不需要再做临界段处理了,只有uart_read读缓冲区时需要进行临界段处理。
‘
环形缓冲区结构
通过以下结构体实现
分别设计了读写指针,当前有效数据个数,缓冲区大小。
写缓冲区时in_u32写指针递增,到末尾后绕到最开始,如果缓冲区满则丢失。
读缓冲区时out_u32读指针递增,到末尾后绕到最开始。
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;
代码
添加文件LPUART0_TXRXIRQ\Src\uart.c,内容如下
#include <stdint.h>
#include "fm33lg0xx_fl.h"
#include "core_cmFunc.h"
uint8_t uart_buffer[64];
uint8_t uart_ring_buffer[128];
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;
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)
{
for(uint32_t 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
{
break;
}
}
}
int 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;
}
__disable_irq();
for(uint32_t 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;
}
}
__enable_irq();
return readlen;
}
int uart_write(uint8_t *buff, uint32_t len)
{
for(uint32_t i=0; i<len; i++)
{
FL_LPUART_WriteTXBuff(LPUART0, buff[i]);
while(FL_LPUART_IsActiveFlag_TXBuffEmpty(LPUART0)==0);
}
return 0;
}
LPUART0_TXRXIRQ\Src\uart.h中提供三个接口
#ifndef UART_H
#define UART_H
#include <stdint.h>
void uart_rx_handler(const uint8_t *buffer, uint32_t length);
int uart_read(uint8_t *buff, uint32_t len);
int uart_write(uint8_t *buff, uint32_t len);
#endif
串口中断函数中调用uart_rx_handler
LPUART0_TXRXIRQ\Src\demo_lpuart.c中
#include "uart.h"
void LPUARTx_IRQHandler(void)
{
if((FL_ENABLE == FL_LPUART_IsEnabledIT_RXBuffFull(LPUART0))
&& (FL_SET == FL_LPUART_IsActiveFlag_RXBuffFull(LPUART0)))
{
temp = FL_LPUART_ReadRXBuff(LPUART0);
uart_rx_handler(&temp,1);
}
而int uart_read(uint8_t *buff, uint32_t len);
int uart_write(uint8_t *buff, uint32_t len);
供应用层调用。
测试
LPUART0_TXRXIRQ\Src\main.c中
#include "uart.h"
uint8_t buffer[128];
while(1)
{
int len=0;
if((len = uart_read(buffer, sizeof(buffer))) >0)
{
uart_write(buffer, len);
}
}
上位机持续发送,测试发送的内容是否和接收到的内容完全一样,测试稳定性。
同时也可以测试发送效率。
总结
- 通过实现环形缓冲区,实现串口收发的接口给应用使用。当然以上实现还可以优化,比如发送现在是查询方式,效率低,可以类似改为环形缓冲区中断发送。实际根据接收和处理速度调整缓冲区的大小,来满足生产和消费平均速率协调。
- 实际临界段处理不需要完全关中断,可以只关指定优先级中断,处理完再恢复到之前的优先级中断。