情景
在一个任务里面,使用 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