FreeRTOS任务切换导致SPI(FLASH)读写错误问题解决记录--转载

本文记录了解决FreeRTOS中因任务切换导致SPI Flash(W25Q128)读写错误的问题,通过在SPI操作时使用任务级临界区或调度锁避免调度,确保数据一致性。作者首先分析了问题,然后提供了两种关键代码修改策略:一是局部任务临界区,二是全局操作调度锁的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FreeRTOS任务切换导致SPI(FLASH)读写错误问题解决记录--转载

情景

在一个任务里面,使用 for 或者 while 循环调用 SPI 操作[读写] FLASH(W25Q128),但是该循环操作未超过看门狗复位时间。

问题

FreeRTOS系统重启(看门狗),调试发现在任务切换函数和SPI读写函数循环。

	{
		if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
		{
			/* Add the task to the suspended task list instead of a delayed task
			list to ensure it is not woken by a timing event.  It will block
			indefinitely. */
			vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
		}
		else
		{
			/* Calculate the time at which the task should be woken if the event
			does not occur.  This may overflow but this doesn't matter, the
			kernel will manage it correctly. */
			xTimeToWake = xConstTickCount + xTicksToWait;
 
			/* The list item will be inserted in wake time order. */
			listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
 
			if( xTimeToWake < xConstTickCount )
			{
				/* Wake time has overflowed.  Place this item in the overflow
				list. */
				vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
			}
			else
			{
				/* The wake time has not overflowed, so the current block list
				is used. */
				vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
 
				/* If the task entering the blocked state was placed at the
				head of the list of blocked tasks then xNextTaskUnblockTime
				needs to be updated too. */
				if( xTimeToWake < xNextTaskUnblockTime )
				{
					xNextTaskUnblockTime = xTimeToWake;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
	}
uint8_t SPI_ReadWriteByte(SPI_TypeDef* SPIx,uint8_t TxData)
{		
	uint8_t RxData = 0;
 
	while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);	//等待发送缓冲区空 
	SPI_I2S_SendData(SPIx, TxData);			//发送一个字节
 
    while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);	//等待数据接收完成
	RxData = SPI_I2S_ReceiveData(SPIx);		//返回接收到的数据
 
    return (uint8_t)RxData;			
}

原因

思考原因,可能是任务切换导致SPI读写错误。

解决

开关任务级临界段,在SPI操作时阻止任务调度。

uint8_t SPI_ReadWriteByte(SPI_TypeDef* SPIx,uint8_t TxData)
{		
	uint8_t RxData = 0;
	
    taskENTER_CRITICAL();	
	
	while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);	//等待发送缓冲区空 
	SPI_I2S_SendData(SPIx, TxData);			//发送一个字节
 
    while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);	//等待数据接收完成
	RxData = SPI_I2S_ReceiveData(SPIx);		//返回接收到的数据
	
    taskEXIT_CRITICAL();	
    return (uint8_t)RxData;		
}

后面发现使用以上方法后不会再卡死循环了,但是读取数据会出现全FF的情况,于是把临界段放在整体读写FLASH的函数。就没在出现读取错误的情况。

void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{	
	taskENTER_CRITICAL();		//进入临界区
	/*
    具体写入操作
    */
	taskEXIT_CRITICAL();
}
 
 
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
{
	taskENTER_CRITICAL();		//进入临界区
	/*
    具体读取操作
    */
	taskEXIT_CRITICAL();
}

或者使用调度锁:

void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{	
	vTaskSuspendAll(); /* 开启调度锁 */	
	/*
    具体写入操作
    */
	xTaskResumeAll (); /* 关闭调度锁 */
}
 
 
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
{
	vTaskSuspendAll(); /* 开启调度锁 */	
	/*
    具体读取操作
    */
	xTaskResumeAll (); /* 关闭调度锁 */
}

原文链接:
https://blog.csdn.net/p1279030826/article/details/109741257

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值