当前位置:网站首页>STM32使用静态队列保存数据
STM32使用静态队列保存数据
2022-08-09 10:56:00 【zhaodong_jack】
最近在调试CAN总线接口的BootLoader。由于应用程序的代码比较大,升级过程比较慢。所以在不断的优化BootLoader程序,让升级时间尽可能缩短。其中最重要的就是使用了队列,而且使用的是静态队列。本文着重讨论静态队列
静态队列是基于数组的。
动态队列是基于链表的。
为什么使用静态队列而不使用动态队列?
我们先来对比一下静态队列和动态队列的优缺点(这个对比是基于单片机开发对比的):
动态队列的优点:没有长度的限制,只要堆内存充足,数据就可以入队;
动态队列的缺点:需要使用动态内存申请函数申请内存,申请内存耗费时间;不断的调用malloc和free可能会造成内存碎片化。
静态队列的优点:不需要动态内存申请,不需要耗费动态内存申请时间,不需要担心内存碎片
静态队列的缺点:初始化队列的时候就指定了队列的长度,最多只能存放定义队列时指定的长度;静态队列存在队列满的情况,当队列满时就会入队失败就会丢数据。
权衡了利弊之后此项目我选择了静态队列。
最开始没有使用队列,在ISR中将接收到的数据解析,这样的代码只适应低频率的数据接收,当数据帧与数据帧之间时间较短时,可能还没有处理完本次接收的数据而下一个数据帧又到来时,就会丢数据。
优化以后的程序使用队列,在ISR中将接收到的数据入队,在主循环中只要队列不为空就出队解析数据。这样的代码可以适应高频率的数据接收,因为在ISR中只是入队(将数据拷贝到SRAM),解析工作留在主循环中。此时可能有大兄弟担心了,如果数据还没有拷贝完成,又有一帧数据到来不就丢数据了吗?大兄弟啊,这个担心完全没必要,因为MCU主频速度通常比外设速度高的多。向SRAM拷贝数据是非常的快,也就是说在下一帧数据到来之前肯定是可以拷贝完成的(传输一帧数据的时间远远大于拷贝一帧数据的时间)。
下面开始介绍静态队列代码
队列用一个结构体组织起来
#define CAN_RING_BUFFER_MAX 100
typedef struct
{
uint8_t phead;//队列头下标
uint8_t ptail;//队列尾下标
CanRxMsg buffer[CAN_RING_BUFFER_MAX];//队列缓存
}CAN_Ring_Queue;
队列缓存区buffer是一个数组。物理上是线性的,逻辑上是环形的,也就是首尾相连的。当存储到数组末尾时就开始从头继续存储,如果不是环形的那岂不是存到数组末尾时队列就再无法存储数据了?所以静态队列在使用中必须把它看做环形的。
注意phead和ptail不是指针,而是保存队列头和队列尾的下标,
队列初始化函数
/** * @brief 队列初始化函数 * @param 队列的指针 * @retval 无 */
void queue_init(CAN_Ring_Queue *pqueue)
{
memset(pqueue->buffer,0,sizeof(pqueue->buffer));//队列缓存区清空
pqueue->phead = 0;//队列头指向缓存区起始地址
pqueue->ptail = 0;//队列尾指向缓存区起始地址
}
判断队列为空函数
/** * @brief 返回队列是否为空的函数 * @param 队列的指针 * @retval 1表示队列空 0表示队列非空 */
static uint8_t queue_is_empty(CAN_Ring_Queue *pqueue)
{
if (pqueue->phead == pqueue->ptail)//头和尾重合的时候链表为空
{
return 1;
}
else
{
return 0;
}
}
判断队列为满函数
因为是静态队列所以有满的时候,动态队列就没有判断满这个函数了
/** * @brief 返回队列是否为满的函数 * @param 队列的指针 * @retval 1表示队列满 0表示队列未满 */
static uint8_t queue_is_full(CAN_Ring_Queue *pqueue)
{
//队列尾加1等于队列头的时候就是队列满的时候
if (((pqueue->ptail)+1)%CAN_RING_BUFFER_MAX == pqueue->phead)
{
return 1;
}
else
{
return 0;
}
}
入队函数
/** * @brief 队列入队函数 * @param 队列的指针,要插入的变量 * @retval 1表示入队成功 0表示入队失败 */
uint8_t queue_insert_end(CAN_Ring_Queue *pqueue,CanRxMsg value)
{
if (queue_is_full(pqueue) == 1)
{
return 0;//队列已满插入失败
}
else
{
pqueue->buffer[pqueue->ptail] = value;//入队是入到队尾
pqueue->ptail += 1;//队列尾向后偏移一个单位
//尾指针指向缓存末尾时再从头开始向后指,实现环形效果
pqueue->ptail = (pqueue->ptail)%CAN_RING_BUFFER_MAX;
return 1;
}
}
出队函数
/** * @brief 队列出队函数 * @param 队列的指针,保存出队变量的指针 * @retval 1表示出队成功 0表示出队失败 */
/********/
uint8_t queue_delete(CAN_Ring_Queue *pqueue,CanRxMsg *pdata)
{
if (queue_is_empty(pqueue) == 1)//队列为空出队失败
{
return 0;
}
else
{
*pdata = pqueue->buffer[pqueue->phead];//取出队列头所指向的元素
pqueue->phead += 1;//队列头向后偏移一个单位
//头指针指向缓存末尾时再从头开始向后指,实现环形效果
pqueue->phead = (pqueue->phead) % CAN_RING_BUFFER_MAX;
return 1;
}
}
到此静态队列的API就写完了。
定义队列
CAN_Ring_Queue can_receive; //CAN缓存结构体变量
初始化队列
queue_init(&can_receive);
在中断函数中入队
/** * @brief CAN邮箱0接收中断函数 * @param 无 * @retval 无 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);
queue_insert_end(&can_receive,RxMessage);//此处并没有判断入队是否成功,如果有需要可以判断
}
主函数中出队
/** * @brief 处理CAN总线数据 * @param 无 * @retval 无 */
void processing_can_data(void)
{
CanRxMsg RxMessage;
if(queue_delete(&can_receive,&RxMessage) == 1)//出队成功
{
//解析RxMessage就好了
}
}
边栏推荐
猜你喜欢

人物 | 从程序员到架构师,我是如何快速成长的?

electron 应用开发优秀实践

Dialogue with the DPO of a multinational consumer brand: How to start with data security compliance?See you on 8.11 Live!

情感分析SowNLP词库

Quartz的理解

相关系数计算,热力图绘制,代码实现

类与对象 (下)

Netscope: Online visualization tool for neural network structures

在webgis中显示矢量化后的风险防控信息

centos7.5 设置Mysql开机自启动
随机推荐
美的数字化平台 iBUILDING 背后的技术选型
乘积量化(PQ)
爬虫实例,获取豆瓣上某部电影的评论
Since I use the HiFlow scene connector, I don't have to worry about becoming a "dropper" anymore
1006 Sign In and Sign Out (25分)
最长回文子串
C语言统计不同单词数
golang runtime Caller、Callers、CallersFrames、FuncForPC、Stack作用
matlab图像分割,从基因芯片荧光图像中提取阴性点(弱)和阳性点(强)
How tall is the B+ tree of the MySQL index?
关于anaconda中conda下载包或者pip下载包很慢的原因,加速下载包的方法(无视anaconda版本和环境)
Unix Environment Programming Chapter 14 14.8 Memory Mapped I/O
Jmeter BeanShell post processor
jvm-类加载系统
性能测试(04)-表达式和业务关联-JDBC关联
RPN principle in faster-rcnn
15.8 the semaphore Unix environment programming chapter 15
linux mysql操作的相关命令
TensorFlow—计算梯度与控制梯度 : tf.gradients和compute_gradients和apply_gradients和clip_by_global_norm控制梯度
numpy的ndarray取数操作