STM32 中断缓存队列的竞态争用问题



利用STM32 中断进行串口数据读写,由于引入中断机制,串口的读是放在中断回调函数中,由于中断的高速性,很容易引起指定的缓存区满,因此增加串口读缓冲队列,是保证数据高效可靠的保证。

STM32中,没有高级操作系统中的线程调度这一概念,也就无锁的概念,但是中断的优先级高,会不停的打断主程序的执行,当主程序重回中断向量位置时,由于中断对一些数据状态的改变,主程序很容易读到脏数据。因为缓冲队列是作为公共缓冲区的中转场合,势必会造成中断部分与主程序部分的争用问题,为了解决这种竞态争用队列的问题,采用传统的递归扫描队列中的环形数组状态的方法,就不可行了,递归的过程中很容易读到脏数据了。

分析中发现,由于缓冲区较大,读、写部分,是对缓冲区中的不同部分来分开处理的,因此可以把公共竞态的部分,缩小,缩小到状态位的处理,这样争用就少了,但是单独靠状态位,无方向性,因此引入了计数器,只需要比较读、写状态的计数差值,就知道了当前读、写的区间段,为了保证环形数组构造的队列的可循环性,还需要引入游标机制,来确定读、写部分的当前位,游标读、写独立游标,二者唯一需要交互的就是计数器了,利用此方法解决竞态问题。这种机制由于不是高级操作系统的锁那种在CPU上强制做排他处理,因此队列部分无法满足多个输入中断同时进入,多个中断之间的计数会引起竞态。

ReceiveBufferData receiveBufferQueue[20];//½ÓÊÕ¶ÓÁÐ
int writeBufferQueueCursor=0;//½ÓÊÕ¶ÓÁеÄѹÈëÓαê
int writeBufferQueueCount=0;//×ܼÆд¶ÓÁеĴÎÊý
int readBufferQueueCursor=0;//½ÓÊÕ¶ÓÁеĶÁÈ¡Óαê
int readBufferQueueCount=0;//×ܼƳɹ¦¶Á¶ÓÁеĴÎÊý
int receiveBufferQueueLen=20;//½ÓÊÕ¶ÓÁ㤶È
ReceiveBufferData emptyReceiveBufferData;//¿ÕµÄÊý¾Ý£¬ÓÃÓÚ¿ÕÅųöʹÓÃ
//ѹÈë½ÓÊÕÊý¾Ý°üµ½¶ÓÁÐ
void PushReceiveBufferData(u8 *tempReceiveBuffer,u8 receiveLen)
{
 	 int i=0;
   int tempWriteBufferQueueCursor=writeBufferQueueCursor;
	 ReceiveBufferData receiveItem;
	 writeBufferQueueCursor+=1;
	 if(writeBufferQueueCursor==receiveBufferQueueLen)
	 {
      writeBufferQueueCursor=0;
   }
	 receiveBufferQueue[writeBufferQueueCursor].ReceiveStatus=0x00;
	 receiveItem=receiveBufferQueue[tempWriteBufferQueueCursor];
	 receiveItem.ReceiveLength=receiveLen;
	 for(i=0;i<receiveLen;i++)
	 {
		 *(receiveItem.ReceiveBuffer+i)=*(tempReceiveBuffer+i);
   }
	 receiveItem.ReceiveStatus=0x01;
	 receiveBufferQueue[tempWriteBufferQueueCursor]=receiveItem;
	 writeBufferQueueCount+=1;
}
//µ¯³öÐèÒª´¦ÀíµÄ½ÓÊÕÊý¾Ý°ü
ReceiveBufferData PopReceiveBufferData()
{
	int tempReadBufferQueueCursor=readBufferQueueCursor;
	ReceiveBufferData receiveItem;
	if(readBufferQueueCount>=writeBufferQueueCount)
	{
     emptyReceiveBufferData.ReceiveStatus=0x00;
	   return emptyReceiveBufferData;
  }
	readBufferQueueCursor+=1;
	if(readBufferQueueCursor==receiveBufferQueueLen)
	{
     readBufferQueueCursor=0;
  }
	receiveItem=receiveBufferQueue[tempReadBufferQueueCursor];
	if(receiveItem.ReceiveStatus==0x01)
	{
	   receiveItem.ReceiveStatus=0x02;
	}
	receiveBufferQueue[tempReadBufferQueueCursor]=receiveItem;
	readBufferQueueCount+=1;
	return receiveItem;
}




STM32 SPI 环形队列是一种用于管理 STM32 微控制器上 SPI (Serial Peripheral Interface) 总线通信的数据结构。它主要用于在发送数据流到外部设备时提供缓冲功能,防止数据丢失并提高系统稳定性。 ### 环形队列原理 环形队列本质上是一个循环数组,其特点是最后一个元素的下一个位置直接指向队列的第一个元素,形成一个闭环。这种设计使得队列的操作非常高效,在插入或删除元素时仅需简单的指针操作就能完成,而不需要频繁地复制数组或处理边界条件。 ### STM32 SPI环形队列的应用场景 当使用 STM32 的 SPI 接口与外部设备(如传感器、显示器等)通信时,数据传输可能会受到多种因素的影响,比如微处理器主频、总线速度以及外部设备的速度。如果数据发送速度过快,可能导致 STM32 内存来不及处理接收到的所有数据包,导致丢包或数据溢出。此时,引入环形队列可以有效解决这个问题: 1. **缓存数据**:通过在硬件层面上设置一个环形队列,接收来自 SPI 接口的数据并将它们存储于队列中。这样,微处理器可以在需要时从队列中按顺序取出数据进行处理,而不是实时同步数据流。 2. **减少延迟**:使用环形队列可以减少数据传输过程中的等待时间,提高系统的整体效率。 3. **优化资源利用**:通过合理配置环形队列的大小,可以在保证数据不丢失的同时,最小化内存占用,优化资源利用率。 ### 实现步骤 要在 STM32 中实现基于 SPI 的环形队列,通常包括以下几个关键步骤: 1. **初始化**:首先定义一个结构体来表示环形队列,包含队头指针、队尾指针以及队列大小。然后根据实际需求分配相应大小的内存空间作为队列数组。 2. **入队操作**:将新数据放入队尾,并更新队尾指针,同时检查是否超出队列容量限制。 3. **出队操作**:从队头取出数据,并更新队头指针,同样需要考虑队列为空的情况。 4. **循环使用**:循环执行入队和出队操作,直到队列满或不再有可用数据为止。 5. **错误检测**:在关键操作之前和之后都应检查队列的状态(例如是否已满或为空),避免非法访问队列。 6. **中断处理**:通常在中断服务程序中负责处理环形队列的相关操作,尤其是数据的读取和发送。 ### 相关问题: 1. 在设计 STM32 SPI 环形队列时需要注意哪些关键点? 2. 如何在实际项目中测试 STM32 SPI 环形队列的性能? 3. 当系统负载过高时,如何调整 STM32 SPI 环形队列的大小以适应变化的需求? 通过理解并实施上述步骤,您可以有效地利用 STM32 的 SPI 接口,增强系统的稳定性和响应能力,特别是在高数据流量下。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值