Hi.
We are using the LPC4370 SSP0 as an SPI 8-bit Master to drive an MLX90393 hall sensor. The bitrate is 8 MHz, CPOL=1 and CPHA=1. The problem is that, after several hours interacting with the sensor at intervals of about 500 ms, with no problem, we eventually get stuck at the while in the following function:
/* SSP Polling Read in blocking mode */
uint32_t Chip_SSP_ReadFrames_Blocking(LPC_SSP_T *pSSP, uint8_t *buffer, uint32_t buffer_len)
{
uint32_t rx_cnt = 0, tx_cnt = 0;
/* Clear all remaining frames in RX FIFO */
while (IP_SSP_GetStatus(pSSP, SSP_STAT_RNE)) {
IP_SSP_ReceiveFrame(pSSP);
}
/* Clear status */
IP_SSP_ClearIntPending( pSSP, SSP_INT_CLEAR_BITMASK );
if (IP_SSP_GetDataSize( pSSP ) > SSP_BITS_8) {
// Omitted because we are using 8-bit frames.
}
else {
uint8_t *rdata8;
rdata8 = buffer;
while (tx_cnt < buffer_len || rx_cnt < buffer_len) {
/* write data to buffer */
if ((IP_SSP_GetStatus(pSSP, SSP_STAT_TNF) == SET) && (tx_cnt < buffer_len)) {
IP_SSP_SendFrame(pSSP, 0xFF); /* just send dummy data */
tx_cnt++;
}
/* Check overrun error */
if (IP_SSP_GetRawIntStatus(pSSP, SSP_RORRIS) == SET) {
return ERROR;
}
/* Check for any data available in RX FIFO */
while (IP_SSP_GetStatus(pSSP, SSP_STAT_RNE) == SET && rx_cnt < buffer_len) { // It seems RNE is never set for the last byte.
*rdata8 = IP_SSP_ReceiveFrame(pSSP);
rdata8++;
rx_cnt++;
}
}
return rx_cnt;
}
}
While we are stuck, tx_cnt = buffer_len, but rx_cnt = buffer_len - 1. In other words, all bytes have been transmitted, but we have received one byte less than expected.
I find this puzzling—if tx_cnt = buffer_len, that means all dummy bytes were sent, providing the clock signal. Regardless of the state of the other end, MISO should be sampled, and a new frame should be added to the RX FIFO. So how is it possible that rx_cnt is lower than tx_cnt?
It’s worth noting that the code in question runs within a FreeRTOS task, which can be preempted. Given that, the only plausible explanation I can think of is that some other code—either another task or an interrupt—might be "stealing" the received frame by reading the DR register before we do. However, after thoroughly investigating this possibility in the code, I haven’t found any evidence to support it, though I may have missed it.
So, what other scenarios could lead to this behavior?
Note that I am ruling out faulty hardware for a few reasons: 1) In my experience, software issues are far more common than hardware failures 2) the chip’s errata does not mention any known issues with SSP and 3) I haven’t found any reports of others encountering this issue during my research.
Thanks!
Hi @perencia-wc
Yes, i think your suspicion about another task or interrupt "stealing" the received frame is definitely valid under FreeRTOS.
So you can try:
Disable interrupts during transaction: To rule out task preemption, consider using taskENTER_CRITICAL() and taskEXIT_CRITICAL() around the SPI transaction code.
Or
Add a timeout: Implement a timeout in your while loop. If the RX FIFO never fills as expected, log the error and retry.
BR
Harry
Hi @Harry_Zhang
I can confirm that no access is done to the DR register in any other task. I've set a breakpoint to trigger each time the DR address is accessed and, outside our code, no one is reading the DR.
Add a timeout: Implement a timeout in your while loop. If the RX FIFO never fills as expected, log the error and retry.
I'd like to know the cause before trying possible solutions, as the bug takes a lot, ~3 or 4 hours to reproduce.
Hi @perencia-wc
If that no access is done to the DR register in any other task.
SSP has separate TX and RX FIFOs. When SPI is running at high speed (8 MHz), the following can happen:
The RX FIFO isn’t serviced in time, leading to a missed byte.
The TX side could send dummy bytes faster than the RX side processes incoming data, especially if the ISR or RTOS context switching adds slight delays.
So would this problem occur if you slow down the speed?
BR
Harry