IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 队列与中断导致指针冲突问题解决 -> 正文阅读

[嵌入式]队列与中断导致指针冲突问题解决

遇到问题

最近在进行数据的接收时,单纯的使用数组接收,已经满足不了我的需求。所以在网上了解到可以使用队列进行对数据的存储,使用也很简单,代码量也不大,并且都贴有运行代码,我也尝试着执行了几个粘贴来的代码,确实是符合要求的,但有个问题,并没有被考虑进去,那就是在单片机中使用队列,出队或者入队是是会被中断打断的,此时若是使用指针进行数据的操作,效果可想而知,直接卡死,你若是能看到我这篇文章,很显然你也遇到了同样的问题,所以在想,不行就用数组对付接收,然后清除buf。简单而实用,但遇到问题怎可轻言放弃,接下来说一下我的思路,速度有点快,快上车

如何解决

在网上你可能会看到无冲突的案例,是使用了关闭中断的方式,使入队、出队很安全,毫无风险,但同时,也可能出现丢包的情况出现,但为了入队、出队的安全,毕竟道路千万条,安全第一条,做出点牺牲是应该的嘛。 那有没有更加好的办法呢,有,我这里提供一种解决方法,不一定是最好的,但一定符合你的要求,既不用关中断,也保证了入队、出队的安全性

队列入队、出队思路

在一般的场合下,都是在中断中进行入队的操作,而在主循环中出队操作,所以问题就在入队的时候,不去干扰到出队,换句话说,入队与出队是两件事,各干各的,互不打扰,但又互相影响,可以想象成量子纠缠,在量子力学中,对入列与出列在彼此的相互作用后,系统总体容量依然不变。(别听我瞎说)

既然是互不打扰,那就各干各的,入队一个入队buf,出队一个出队buf,需要出队就将出队buf内的数据读出,需要入队就将数据写入入队buf,很好,思路是没问题的,出队时会将数据读空的,入队时会将数据塞满的,那怎么办,转换一下呗,那什么时候转换呢?
出队时:出队buf为空,入队buf不为空时,把出队buf改为入队buf,入队buf改为出队buf,这样下次读时可以读出数据,写入时也非溢出的写入进去
入队时:入队buf已满,出队buf不管是满是空,将把出队buf改为入队buf,入队buf改为出队buf,为什么要这么做,根据队列的性质,先进先出,当一个buf满时,说明出现了栈溢出,也就是队列溢出,需要将最先进入的数据清除,所以在一个队列溢出时,另一个队列全部都会被清除
入队的情况:当出队数据一个没有被读出,出队队列一直为满队列,入队队列在入队一个数据时,队列的长度会增加1,并不满足队列的性质,所以在入队队列加入数据时,要将出队队列手动取出一个数据,以保证队列的长度小于等于队列的大小。
基于以上思路,做一下总结,定义两个数组,一个为入队数组,一个为出队数组,入队数组会控制出队数组,出队数组会控制入队数组,控制的权限就是保证队列性质,保证量子纠缠态。

代码实战

首先在这里我先贴出我写的代码

#include "queue.h"


void Message_QueueMode(Queue4 *Queue,unsigned char mode)
{
		Queue->mode= mode;	
}


void Message_QueueEmptyOne(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned char (*Buff)[4],unsigned char lock)
{
		*(Head +lock) = Buff[lock];
		*(Tail +lock) = Buff[lock];
		Queue->full[lock] = 0;	
}

//清空队列
void Message_QueueEmpty(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned char (*Buff)[4])
{
	for(int i = 0; i < QUEUE_BUFFER_SUM; i++)
	{
		*(Head +i) = Buff[i];
		*(Tail +i) = Buff[i];
		Queue->full[i] = 0;	
	}
	Queue->lock 	  = 0;
	Queue->queue_idle = 0;
}


//队列长度计算
void Message_QueueLenUpdate(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned short Len)
{
	for(int i = 0; i < QUEUE_BUFFER_SUM; i++)
	{
		if(*(Tail+i) > *(Head+i)) 								{ Queue->length[i] =  *(Tail+i)-*(Head+i); }
		if(*(Tail+i) < *(Head+i)) 								{ Queue->length[i] =  *(Tail+i)+Len-*(Head+i); }
		if(*(Tail+i) == *(Head+i) && Queue->full[i])   			{ Queue->length[i] =  Len;}
		if(*(Tail+i) == *(Head+i) && !Queue->full[i])			{ Queue->length[i] =  0;  }	
	}	
}


//入队
void Message_QueueDataIn(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned char (*Buff)[4], unsigned short Len, unsigned char *Data,  unsigned short DataLen)
{	
if(Queue->mode){
	if(Queue->queue_idle == 0){ MessageQueueEmptyOne( (*Queue),!Queue->lock); }//空闲清除读队列
	MessageQueueEmptyOne( (*Queue),Queue->lock); //非空闲清除写队列
}

	for(int num = 0; num < DataLen; num++, Data++)
	{
if(!Queue->mode){
	MessageQueueLenUpdate( (*Queue) );
	if( (Queue->length[0] + Queue->length[1]) >= 4 && !Queue->queue_idle)
	{
		if(Queue->length[!Queue->lock] == 0) {
			Queue->lock = !Queue->lock;				
			Queue->full[!Queue->lock] = 0;
		}

		for(int i = 0; i <= ((Queue->length[Queue->lock] + Queue->length[!Queue->lock]) - 4);i++)
		{
			if(++(*(Head+!Queue->lock)) == Buff[!Queue->lock]+Len) { *(Head+!Queue->lock) = Buff[!Queue->lock]; }
			if(*(Tail+!Queue->lock) == *(Head+!Queue->lock) && Queue->full[!Queue->lock] == 0){ break; }
		}
	}
}

	MessageQueueLenUpdate( (*Queue) );//队列长度更新
	
		**(Tail+Queue->lock) = *Data;	/*单纯的数据存储*/
		if(Queue->full[Queue->lock] == 1) //队列满
		{
			if(++(*(Head+Queue->lock)) == Buff[Queue->lock]+Len)   	{ *(Head+Queue->lock) = Buff[Queue->lock]; }
			if(++(*(Tail+Queue->lock)) == Buff[Queue->lock]+Len) 	{ *(Tail+Queue->lock) = Buff[Queue->lock]; }
		}
		else							  							//队列未满
		{
			if(++(*(Tail+Queue->lock)) == Buff[Queue->lock]+Len) 	{ *(Tail+Queue->lock) = Buff[Queue->lock]; }
			if(*(Tail+Queue->lock) == *(Head+Queue->lock) )			{ Queue->full[Queue->lock] = 1; 		   }
		}
	}
	MessageQueueLenUpdate( (*Queue) );//队列长度更新
}


//出队
void Message_QueueDataOut(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned char (*Buff)[4], unsigned short Len, unsigned char *Data)
{
	Queue->queue_idle = 1; Queue->ret = 0;
	if( !(*(Tail+!Queue->lock) == *(Head+!Queue->lock) && Queue->full[!Queue->lock] == 0 )) //非空队列
	{
		*Data = **(Head+!Queue->lock);
		if(++(*(Head+!Queue->lock)) == Buff[!Queue->lock]+Len) { *(Head+!Queue->lock) = Buff[!Queue->lock];}//指针指向溢出,指向数组首地址
		Queue->full[!Queue->lock] = 0; Queue->ret = 1;
	}
	MessageQueueLenUpdate( (*Queue) );
	if(Queue->length[Queue->lock] != 0 && Queue->length[!Queue->lock] == 0) //出队时判断当前队列是否为空队列,若为空队列且从队列不为空队列,则跳转,若前面没有获取到数据,重新到跳转队列获取数据
	{
		Queue->lock = !Queue->lock;
		if(Queue->ret == 0){ MessageQueueDataOut((*Queue),Data); }
	}
	Queue->queue_idle = 0;//这里结束 队列为空闲态
}

#ifndef __QUEUE_H
#define __QUEUE_H

typedef enum
{
	DATEQUEUE = 0,
	TASKQUEUE = 1,
}queuemode_e;

typedef struct
{
	#define QUEUE_BUFFER_SUM 2
	unsigned char full[QUEUE_BUFFER_SUM];
	unsigned char length[QUEUE_BUFFER_SUM];
	unsigned char ret,lock,queue_idle,mode;
	unsigned char *Head[QUEUE_BUFFER_SUM]; 
	unsigned char *Tail[QUEUE_BUFFER_SUM]; 
	unsigned char Buff[QUEUE_BUFFER_SUM][4];
}Queue4;


void Message_QueueEmpty		(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned char (*Buff)[4]);
void Message_QueueDataLen	(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned short Len);
void Message_QueueDataIn	(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned char (*Buff)[4], unsigned short Len, unsigned char *Data,  unsigned short DataLen);
void Message_QueueDataOut	(Queue4 *Queue,unsigned char **Head, unsigned char **Tail, unsigned char (*Buff)[4], unsigned short Len, unsigned char *Data);
void Message_QueueMode		(Queue4 *Queue,unsigned char mode);

#define MessageQueueLenUpdate(x)  	Message_QueueLenUpdate(&x,x.Head,x.Tail, sizeof((x).Buff[0]))
#define MessageQueueEmpty(x)	   	Message_QueueEmpty  (&x,x.Head,x.Tail, x.Buff)
#define MessageQueueEmptyOne(x,y)	Message_QueueEmptyOne  (&x,x.Head,x.Tail, x.Buff,(y))
#define MessageQueueDataIn(x,y,z) 	Message_QueueDataIn (&x,x.Head,x.Tail, x.Buff,sizeof((x).Buff[0]),(y),(z))
#define MessageQueueDataOut(x,y)  	Message_QueueDataOut(&x,x.Head,x.Tail, x.Buff,sizeof((x).Buff[0]),(y)) 
#define MessageQueueMode(x,y)		Message_QueueMode(&x,(y));


#endif


一共有100行代码左右,相比较,确实比普通的队列更加繁琐,但确实实现了该有的效果,成功总要是付出点代码的嘛

这里我觉得口头叙述效果可能并不好,说到后面前面又忘记了,所以我画了一张图,入队、出队的逻辑思维图,在结合代码看,一定会有意想不到的惊喜

这就是数据结构可视化的结构,循环队列
在这里插入图片描述
下图代表入队和出队出现的情况与处理机制,如果有不明白的地方可以在下方留言
请添加图片描述

难以信服,实战演示

我使用的单片机是stm32F103C8T6

这里不做 先入列在出列,或者 先出列在入列 的基本操作,演示成功也没有什么特别的,直接演示最头疼的部分

使用过单片机就会知道,单片机有个优先级很低,但很好用的定时器,滴答定时器,HAL用来封装了延时函数,这里我使用标准库来演示,在滴答定时器进行入队操作,入队个数随意,100,1000都可以,因为机制不允许越界,只会存储最后存入的,删除最先存入的。

static unsigned char varible = 0;
unsigned char send[4];

void SysTick_Handler(void)
{
	for(int i = 0; i <3; i++)
	{	
		send[i] = varible++;
	}
	MessageQueueDataIn(KeyMsg,send,3);
}
unsigned char echo;
Queue4 KeyMsg;

int main(void)
{	
	
	MessageQueueEmpty(KeyMsg); //提前初始化,否则由于指针问题,HardFault_Handler()
	MessageQueueMode(KeyMsg,TASKQUEUE);
	SysTick_Init();
	LED_GPIO_Config();
	USART1_Init(115200);
	CMD_LIST_Init();
	
	while(1)
	{
		MessageQueueDataOut(KeyMsg,&echo);
		if(KeyMsg.ret == 1) //返回值为1代表读取成功
		{
			printf("varible = %d\r\n",echo);
		}
	}
}

中断中入队,循环中出队,下面是串口直观打印图,当前为 任务队列模式
在这里插入图片描述

下面打印结果是 消息队列模式 打印情况,若出现丢包,是因为入队快而出队慢
在这里插入图片描述
下面我贴出硬件连接图,过于简单请添加图片描述

代码总结

最后的代码是符合我的预期的,而且长时间的测试并没有出现问题,逻辑上的框图也可以体现出,避免指针越界或数据位错乱问题。总体上满足了使用要求,如果有需要测试平台的源码及完整使用教程,可以评论或私信@我。

最后贴出代码

链接:可中断式非阻塞循环队列代码
如有任何问题,都可私信或评论解答疑问

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:58:47  更:2022-04-18 18:02:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:21:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码