I’m always hesitant to post code directly, because there is a lot of stuff in the code which is not relevant to the issue. After removing the delays the issue still happen, so I do not think it is related to vTaskDelay anymore, but rather some error when the context is saved and reloaded. Somehow, if the debugger can be trusted, when the context is saved before jumping into interrupt code, it sometimes overwrites data that is still needed by another task.
I did make an interesting finding though: when I place a breakpoint in my interrupt, I see the variable ulTaskHasFPUContext is always 1 when my task has a priority of 1. When I lower the priority of my task to 0, the variable ulTaskHasFPUContext alternates between 0 and 1.
But let me try anyway to post the [simplified] code here:
Typedefs:
typedef struct
{
void * tx_buffer;
void * rx_buffer;
uint32_t tx_buffer_size;
uint32_t rx_buffer_size;
bool done;
} spi_xfer_t;
typedef struct
{
volatile uint32_t * IECxCLR;
volatile uint32_t * IECxSET;
volatile uint32_t * IFSxCLR;
const uint32_t FAULT_INT_MASK;
const uint32_t RX_INT_MASK;
const uint32_t TX_INT_MASK;
volatile uint32_t * SPIxCON;
volatile uint32_t * SPIxBUF;
volatile uint32_t * SPIxBRG;
volatile uint32_t * SPIxSTAT;
const uint32_t _SPIxSTAT_SPITBF_MASK;
const uint32_t _SPIxSTAT_SPIRBE_MASK;
const uint32_t _SPIxSTAT_SPITBE_MASK;
volatile uint32_t * SPIxSTATCLR;
const uint32_t _SPIxSTAT_SPIROV_MASK;
volatile uint32_t * SPIxCONSET;
volatile uint32_t * SPIxCONCLR;
const uint32_t _SPIxCON_ON_MASK;
spi_xfer_t *tx_xfer;
spi_xfer_t *rx_xfer;
StaticQueue_t xSpiMasterxQueueBuffer;
uint8_t ucSpiMasterxQueueStorage[ SPIMASTERx_QUEUE_LENGTH *
sizeof( spi_xfer_t* ) ];
QueueHandle_t xSpiMasterxQueue;
} spi_master_common_ctx_t;
Task code:
int set(unsigned int regAddr, unsigned int regMask, unsigned int regValue)
{
uint8_t newValue;
if (regMask >= 0xFF)
newValue = regValue&0xFF;
else
{
/* perform read-modify-write */
//read
unsigned int oldValue;
get(devId, regAddr, &oldValue);
//modify
newValue = (regValue & regMask) | (oldValue & ~regMask);
}
//write
uint8_t spibuffer[3];
spibuffer[0] = (regAddr&0x7F00)>>8;
spibuffer[1] = regAddr&0xFF;
spibuffer[2] = newValue;
spi_xfer_t xfer;
xfer.tx_buffer = spibuffer;
xfer.rx_buffer = NULL;
xfer.tx_buffer_size = 3;
xfer.rx_buffer_size = 0;
SPIMasterx_Blocking_Xfer( &ctx, &xfer );
return 0;
}
int SPIMasterx_Blocking_Xfer( spi_master_common_ctx_t *ctx, spi_xfer_t *xfer )
{
xfer->done = false;
xQueueSendToBack( ctx->xSpiMasterxQueue, &xfer, portMAX_DELAY );
/* Wait for the transfer to be done */
while ( !xfer->done ) taskYIELD();
return 0;
}
Interrupts code:
void SPIMasterx_TX_InterruptHandler( spi_master_common_ctx_t *ctx )
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if (ctx->tx_xfer != NULL)
{
if ( ctx->tx_xfer->tx_buffer_size == 0 )
{
ctx->tx_xfer = NULL;
}
}
if (ctx->tx_xfer == NULL)
{
//fetches the next transfer
if ( xQueueIsQueueEmptyFromISR( ctx->xSpiMasterxQueue ) )
{
/* No pending transactions, turn off the interrupt */
*ctx->IECxCLR = ctx->TX_INT_MASK;
/* Clear the transmit interrupt flag */
*ctx->IFSxCLR = ctx->TX_INT_MASK;
return;
} else
{
xQueueReceiveFromISR( ctx->xSpiMasterxQueue,
&ctx->tx_xfer,
&xHigherPriorityTaskWoken);
}
}
/* Here, tx_xfer is not NULL. If there are more words to be
* transmitted, then transmit them here and keep track of the count. Push
* bytes into the fifo while it is not full
*/
while ( (bool)( *ctx->SPIxSTAT & ctx->_SPIxSTAT_SPITBF_MASK) == false &&
ctx->tx_xfer->tx_buffer_size > 0 )
{
*ctx->SPIxBUF = *((uint8_t *)ctx->tx_xfer->tx_buffer++);
ctx->tx_xfer->tx_buffer_size--;
}
/* Clear the transmit interrupt flag */
*ctx->IFSxCLR = ctx->TX_INT_MASK;
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
void SPIMasterx_RX_InterruptHandler( spi_master_common_ctx_t *ctx )
{
uint8_t rx_data;
if ( ctx->rx_xfer == NULL )
{
ctx->rx_xfer = ctx->tx_xfer;
}
if ( ctx->rx_xfer != NULL )
{
/* Check if the receive buffer is empty or not */
while ( (bool)(*ctx->SPIxSTAT & ctx->_SPIxSTAT_SPIRBE_MASK) == false )
{
/* Receive buffer is not empty. Read the received data. */
rx_data = *ctx->SPIxBUF;
if ( ctx->rx_xfer->rx_buffer_size > 0 )
{
if ( ctx->rx_xfer->rx_buffer != NULL )
*((uint8_t *)ctx->rx_xfer->rx_buffer++) = rx_data;
ctx->rx_xfer->rx_buffer_size--;
}
if ( ctx->rx_xfer->tx_buffer_size == 0 &&
ctx->rx_xfer->rx_buffer_size == 0 )
{
/* Transfer complete */
ctx->rx_xfer->done = true;
ctx->rx_xfer = NULL;
/* Enable the tx interrupt to fetch the next transfer */
*ctx->IECxSET = ctx->TX_INT_MASK;
break;
}
}
}
else
{
while ( (bool)(*ctx->SPIxSTAT & ctx->_SPIxSTAT_SPIRBE_MASK) == false )
{
/* Discard data when no RX is required */
rx_data = *ctx->SPIxBUF;
(void)rx_data;
}
}
/* Clear SPIx RX Interrupt flag */
/* This flag should cleared only after reading buffer */
*ctx->IFSxCLR = ctx->RX_INT_MASK;
}
In the above code, you can assument that ctx is a static variable containing all the required register addresses for that specific SPI peripheral. I use the same SPI driver on 5 different SPI interfaces, but only one (which happens to need an FPU context) is making the system crash.