STM32F103基于标准库开发串口中断接收数据环形队列例程
Posted perseverance52
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103基于标准库开发串口中断接收数据环形队列例程相关的知识,希望对你有一定的参考价值。
STM32F103基于标准库开发串口中断接收数据环形队列例程
✨本示例源码来源于
野火-STM32库开发实战指南
,是一个值得学习借鉴的资源。
- 📑一个完整的串口数据包通讯协议一般包含:帧头、地址信息、数据类型、数据长度、数据块、校验码、帧尾。在一般开发过程当中,如果仅仅是作为调试信息使用,那么串口的使用就没有必要代码这么健壮。毕竟功能越完善,所需要提供的代码越复杂,处理的逻辑增加,以及资源的占用。
🔖有关环形列队的优点可以自行去搜索科普,这里不做介绍了。
🛠环形列队实现
🔖接收数据环形列队实现依托开启串口中断接收以及空闲中断。
rx_data_queue.h
#ifndef __ESP_DATA_QUEUE_H_
#define __ESP_DATA_QUEUE_H_
#include "stm32f10x.h"
#include <string.h>
#include <stdio.h>
//缓冲队列的个数需要为2的幂
#define QUEUE_NODE_NUM (2) //缓冲队列的大小(有多少个缓冲区)
#define QUEUE_NODE_DATA_LEN (2*1024 ) //单个接收缓冲区大小
//队列的主体数据类型接口
#define QUEUE_DATA_TYPE ESP_USART_FRAME
//队列的调试输出接口
#define DATA_QUEUE_LOG QUEUE_DEBUG
#define DATA_QUEUE_LOG_ARRAY QUEUE_DEBUG_ARRAY
//数据主体
typedef struct
char *head; //缓冲区头指针
uint16_t len; //接收到的数据长度
ESP_USART_FRAME;
//队列结构
typedef struct
int size; /* 缓冲区大小 */
int read; /* 读指针 */
int write; /* 写指针 */
int read_using; /*正在读取的缓冲区指针*/
int write_using; /*正在写入的缓冲区指针*/
QUEUE_DATA_TYPE *elems[QUEUE_NODE_NUM]; /* 缓冲区地址 */
QueueBuffer;
extern QueueBuffer rx_queue;
/*信息输出*/
#define QUEUE_DEBUG_ON 1
#define QUEUE_DEBUG_ARRAY_ON 0
#define QUEUE_INFO(fmt,arg...) printf("<<-QUEUE-INFO->> "fmt"\\n",##arg)
#define QUEUE_ERROR(fmt,arg...) printf("<<-QUEUE-ERROR->> "fmt"\\n",##arg)
#define QUEUE_DEBUG(fmt,arg...) do\\
if(QUEUE_DEBUG_ON)\\
printf("[%d]"fmt"\\n",__LINE__, ##arg);\\
while(0)
#define QUEUE_DEBUG_ARRAY(array, num) do\\
int32_t i;\\
uint8_t* a = array;\\
if(QUEUE_DEBUG_ARRAY_ON)\\
\\
printf("\\n<<-QUEUE-DEBUG-ARRAY->>\\n");\\
for (i = 0; i < (num); i++)\\
\\
printf("%02x ", (a)[i]);\\
if ((i + 1 ) %10 == 0)\\
\\
printf("\\n");\\
\\
\\
printf("\\n");\\
\\
while(0)
//输出队列的状态信息
#define cbPrint(cb) DATA_QUEUE_LOG("size=0x%x, read=%d, write=%d\\n", cb.size, cb.read, cb.write);\\
DATA_QUEUE_LOG("size=0x%x, read_using=%d, write_using=%d\\n", cb.size, cb.read_using, cb.write_using);
QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb);
QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb);
void cbReadFinish(QueueBuffer *cb);
void cbWriteFinish(QueueBuffer *cb);
//void cbPrint(QueueBuffer *cb);
QUEUE_DATA_TYPE* cbWriteUsing(QueueBuffer *cb) ;
int cbIsFull(QueueBuffer *cb) ;
int cbIsEmpty(QueueBuffer *cb) ;
void rx_queue_init(void);
void pull_data_from_queue(void);
void push_data_to_queue(char *src_dat,uint16_t src_len);
#endif
rx_data_queue.c
/**
******************************************************************************
* @file rx_data_queue.c
* @author fire
* @version V1.0
* @date 2015-01-xx
* @brief 环形缓冲区,适用于接收外部数据时用作缓冲
******************************************************************************
* @attention
*
* 实验平台:野火 IOT STM32 开发板
* 论坛 :http://www.firebbs.cn
* 淘宝 :https://fire-stm32.taobao.com
*
******************************************************************************
*/
#include "./usart/rx_data_queue.h"
//实例化节点数据类型
QUEUE_DATA_TYPE node_data[QUEUE_NODE_NUM];
//实例化队列类型
QueueBuffer rx_queue;
//队列缓冲区的内存池
__align(4) char node_buff[QUEUE_NODE_NUM][QUEUE_NODE_DATA_LEN] ;
/*环形缓冲队列*/
/**
* @brief 初始化缓冲队列
* @param cb:缓冲队列结构体
* @param size: 缓冲队列的元素个数
* @note 初始化时还需要给cb->elems指针赋值
*/
void cbInit(QueueBuffer *cb, int size)
cb->size = size; /* maximum number of elements */
cb->read = 0; /* index of oldest element */
cb->write = 0; /* index at which to write new element */
// cb->elems = (uint8_t *)calloc(cb->size, sizeof(uint8_t)); //elems 要额外初始化
//(此函数改成了宏,在头文件)
/**
* @brief 输出缓冲队列当前的状态信息
* @param cb:缓冲队列结构体
*/
//void cbPrint(QueueBuffer *cb)
//
// DATA_QUEUE_LOG("size=0x%x, read=%d, write=%d\\n", cb->size, cb->read, cb->write);
// DATA_QUEUE_LOG("size=0x%x, read_using=%d, write_using=%d\\n", cb->size, cb->read_using, cb->write_using);
//
/**
* @brief 判断缓冲队列是(1)否(0)已满
* @param cb:缓冲队列结构体
*/
int cbIsFull(QueueBuffer *cb)
return cb->write == (cb->read ^ cb->size); /* This inverts the most significant bit of read before comparison */
/**
* @brief 判断缓冲队列是(1)否(0)全空
* @param cb:缓冲队列结构体
*/
int cbIsEmpty(QueueBuffer *cb)
return cb->write == cb->read;
/**
* @brief 对缓冲队列的指针加1
* @param cb:缓冲队列结构体
* @param p:要加1的指针
* @return 返回加1的结果
*/
int cbIncr(QueueBuffer *cb, int p)
return (p + 1)&(2*cb->size-1); /* read and write pointers incrementation is done modulo 2*size */
/**
* @brief 获取可写入的缓冲区指针
* @param cb:缓冲队列结构体
* @return 可进行写入的缓冲区指针
* @note 得到指针后可进入写入操作,但写指针不会立即加1,
写完数据时,应调用cbWriteFinish对写指针加1
*/
QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb)
if (cbIsFull(cb)) /* full, overwrite moves read pointer */
return NULL;
else
//当wriet和write_using相等时,表示上一个缓冲区已写入完毕,需要对写指针加1
if(cb->write == cb->write_using)
cb->write_using = cbIncr(cb, cb->write); //未满,则增加1
return cb->elems[cb->write_using&(cb->size-1)];
/**
* @brief 数据写入完毕,更新写指针到缓冲结构体
* @param cb:缓冲队列结构体
*/
void cbWriteFinish(QueueBuffer *cb)
cb->write = cb->write_using;
/**
* @brief 获取可读取的缓冲区指针
* @param cb:缓冲队列结构体
* @return 可进行读取的缓冲区指针
* @note 得到指针后可进入读取操作,但读指针不会立即加1,
读取完数据时,应调用cbReadFinish对读指针加1
*/
QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb)
if(cbIsEmpty(cb))
return NULL;
//当read和read_using相等时,表示上一个缓冲区已读取完毕(即已调用cbReadFinish),
//需要对写指针加1
if(cb->read == cb->read_using)
cb->read_using = cbIncr(cb, cb->read);
return cb->elems[cb->read_using&(cb->size-1)];
/**
* @brief 数据读取完毕,更新读指针到缓冲结构体
* @param cb:缓冲队列结构体
*/
void cbReadFinish(QueueBuffer *cb)
//重置当前读完的数据节点的长度
cb->elems[cb->read_using&(cb->size-1)]->len = 0;
cb->read = cb->read_using;
//队列的指针指向的缓冲区全部销毁
void camera_queue_free(void)
uint32_t i = 0;
for(i = 0; i < QUEUE_NODE_NUM; i ++)
if(node_data[i].head != NULL)
//若是动态申请的空间才要free
// free(node_data[i].head);
node_data[i].head = NULL;
return;
/**
* @brief 缓冲队列初始化,分配内存,使用缓冲队列时,
* @param 无
* @retval 无
*/
void rx_queue_init(void)
uint32_t i = 0;
memset(node_data, 0, sizeof(node_data));
/*初始化缓冲队列*/
cbInit(&rx_queue,QUEUE_NODE_NUM);
for(i = 0; i < QUEUE_NODE_NUM; i ++)
node_data[i].head = node_buff[i];
/*初始化队列缓冲指针,指向实际的内存*/
rx_queue.elems[i] = &node_data[i];
DATA_QUEUE_LOG("node_data[i].head=0x%x,\\r\\nrx_queue.elems[i] =0x%x", (uint32_t)node_data[i].head,(uint32_t)rx_queue.elems[i]->head);
memset(node_data[i].head, 0, QUEUE_NODE_DATA_LEN);
// cbPrint(rx_queue);
/**
* @brief 往队列中写入数据的样例
*/
void push_data_to_queue(char *src_dat,uint16_t src_len)
QUEUE_DATA_TYPE *data_p;
uint8_t i;
for(i=0;i<src_len;i++)
/*获取写缓冲区指针,准备写入新数据*/
data_p = cbWrite(&rx_queue);
if (data_p != NULL) //若缓冲队列未满,开始传输
//往缓冲区写入数据,如使用串口接收、dma写入等方式
*(data_p->head + i) = src_dat[i];
data_p->len++;
printf("\\r\\ndata_p->len =%d",data_p->len);
else return;
cbPrint(rx_queue);
/*写入缓冲区完毕*/
cbWriteFinish(&rx_queue);
cbPrint(rx_queue);
/**
* @brief 从队列中取数据的样例
*/
void pull_data_from_queue(void)
QUEUE_DATA_TYPE *rx_data;
/*从缓冲区读取数据,进行处理,*/
rx_data = cbRead(&rx_queue);
if(rx_data != NULL)//缓冲队列非空
//加上字符串结束符,方便直接输出字符串
*(rx_data->head+rx_data->len) = '\\0';
QUEUE_DEBUG("接收到的数据:%s",rx_data->head);
QUEUE_DEBUG_ARRAY((uint8_t*)rx_data->head,rx_data->len);
//使用完数据必须调用cbReadFinish更新读指针
cbReadFinish(&rx_queue);
- 串口配置函数
/**
* @brief USART GPIO 配置,工作参数配置
* @param 无
* @retval 无
*/
void USART_Config(void)
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
USART_ITConfig ( DEBUG_USARTx, USART_IT_IDLE, ENABLE ); //使能串口总线空闲中断
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
- printf重定向代码
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
/* 等待串口输入数据 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_USARTx);
- main.c
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "./usart/rx_data_queue.h"
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
USART_Config();
rx_queue_init();
/* 发送一个字符串 */
Usart_SendString( DEBUG_USARTx,"这是一个串口中断接收回显实验\\n");
printf("STM32F103VCT6\\r\\n");
while(1)
//获取数据并输出
//实际应用中可参考pull data的方式获取数据进行处理
pull_data_from_queue();
- 📜串口打印信息
📚程序源码
⚡基于Keil5,ARMcompiler:
5.06
链接:https://pan.baidu.com/s/1zDyGGNJTdWg6jI9RSsqvxw
提取码:wdr0
以上是关于STM32F103基于标准库开发串口中断接收数据环形队列例程的主要内容,如果未能解决你的问题,请参考以下文章
STM32F103VET6基于STM32CubeMX创建串口中断+ DMA 不定长数据接收
STM32学习笔记 二基于STM32F103C8T6和STM32CubeMX实现UART串口通信数据收发
STM32F103VE基于标准库下DHT11数据串口打印输出