说明&感谢
- 互联网是个开放的世界, 感谢无数开源和分享者, 本次学习主要参考了杰杰的分享, 经了解杰杰现在大学尚未毕业, 而本人工作8年了, 闻道有先后术业有专攻,再次感谢!
- 参考链接: 杰杰CSDN
环形缓冲区作用
串口数据接收, 如果数据量过大, 很可能来不及处理这些数据, 因此需要把接收的数据存放到一个位置缓存, 待空闲时间处理, 防止接收的数据丢失; 环形缓冲区越大, 那么可以缓存的数据就越多, 这样就是以空间换时间的做法.
编程步骤
- UART初始化, 打开串口发送和接收;
- UART中断优先级配置NVIC;
- 环形缓冲区定义
- 环形缓冲区数据接收: 在串口中断中读取收到的数据到环形缓冲;
- 环形缓冲区数据发送: 在主程序中把环形缓冲区的数据发送给串口;
代码&分析
#define RINGBUFF_LEN 200 //定义最大接收字节数
#define FLASE 1
#define TRUE 0
- 结构体定义, 定义了头位置, 尾位置, 和头尾之间间隔数(lenght)
typedef struct
{
uint16_t heard;
uint16_t tail;
uint16_t lenght; //这个长度指头到尾的长度
uint8_t ring_buffer[RINGBUFF_LEN]; //环形缓冲区数组
}RingBufferStruct;
- 每写一个数据到缓冲区, 尾的位置+1, 即第一次写数据到数组ring_buffer[0], 那么下一次数据写到ring_buffer[1]; 且和头的间隔lenght增加1;
uint8_t write_ring_buffer(uint8_t data)
{
if(RingBuffer.lenght >= RINGBUFF_LEN) //判断缓冲区是否已满
{
return FLASE;
}
RingBuffer.ring_buffer[RingBuffer.tail] = data;
RingBuffer.tail = (RingBuffer.tail + 1) % RINGBUFF_LEN; //防止越界非法访问
RingBuffer.lenght++;
return TRUE;
}
- 每读一个数据, 则头+1; 即第一次读ring_buffer[0], 下一次读数据就是ring_buffer[1]; 且和尾的间隔lenght减1;
uint8_t read_ring_buffer(uint8_t *rData)
{
if(RingBuffer.lenght == 0)//判断非空
{
return FLASE;
}
*rData = RingBuffer.ring_buffer[RingBuffer.heard];//先进先出FIFO,从缓冲区头出
RingBuffer.heard = (RingBuffer.heard + 1) % RINGBUFF_LEN; //防止越界非法访问
RingBuffer.lenght--;
return TRUE;
}
- 串口接收中断中, 把接收的数据写到环形缓冲区;
void USART1_IRQHandler(void)
{
if(USART_GetFlagStatus(DEBUG_UART, USART_FLAG_RXNE))
{
USART_ClearFlag(DEBUG_UART,USART_FLAG_RXNE);//清除中断标志
write_ring_buffer(USART_ReceiveData(DEBUG_UART));//读取接收到的数据
}
}
- 主程序中, 把环形缓冲区的数据读出来, 发送到串口, 打印出来
uint8_t data;
if(0==read_ring_buffer((uint8_t*) &data))
{
USART_SendData(DEBUG_UART,data);
}
实验现象
如果单次发送的数据大于环形缓冲区的大小, 还是会有丢数据的可能;
源代码
MCU型号: STM32F103VET6
开发板: 野火指南者
平台: keil V5.27.0.0
源代码