Hello,
We are on a problem that the SPI module seems not to receive all data thus we never end waiting for it. This happens on heavy load (i.e. lot of data has to be send to different participants on SPI).
For this a MK66FN2M0VLQ18 (Mask 0N65N) is configured with RTX. Many other modules are running on the MCU (CAN0, CAN1, UART, ADC0, ADC1, Ports, etc.).
The code keeps track of how much data has been written to the PUSHR and how many bytes have been collected from the POPR. When the problem occurs the data we have pushed out is one more than the data we have popped. The TCR is always the same number as the push-counter.
Checking on the bus how much data actually have been transmitted it reveals sometimes the data was not on the bus, so there is really one byte missing. However sometimes it was there and in case it was one of the very latest bytes it is missing in the RX FIFO.
Many thanks in advance for any tips.
Regards Dani
Stripped code looks like this
#include <MK66F18.h>
...
void D_SPI_IRQHandler( T_D_SPI_IF * spi, const uint32_t fifoLength )
{
// links to common used data
SPI_Type* spiBase = spi->base_ptr; // SPI base pointer
volatile uint32_t* statRegPtr = &(spiBase->SR); // SPI status register
// handle errors
if( (*statRegPtr & SPI_SR_RFOF_MASK) ) { // receive FIFO overflow set?
*statRegPtr = SPI_SR_RFOF_MASK; // clear it
ERROR_MSG("SPI%d RX-Overflow. Is SPI FIFO length properly set?\n", spi->id);
}
// handle rx
D_SPI_irqRxHandler( spi, spiBase );
// end of communication
D_SPI_irqEoCHandler( spi, spiBase );
// handle tx
D_SPI_irqTxHandler( spi, spiBase, fifoLength );
}
void D_SPI_irqRxHandler( T_D_SPI_IF * spi, SPI_Type* spiBase) {
volatile uint32_t* statRegPtr = &(spiBase->SR);
uint32_t *rxBuf = &(spi->rxBuffer[spi->rxBufferLength]); // rx Buffer
while(*statRegPtr & SPI_SR_RFDF_MASK ) { // as long as there is data
uint16_t data = spi->base_ptr->POPR; // get data from buffer
*statRegPtr = SPI_SR_RFDF_MASK; // clear receive fifo drain flag
if( spi->rxBufferLength >= ELEMENTS_OF(spi->rxBuffer) ) { // if we have no space left in buffer
ERROR_MSG("SPI %d, RxBuf Overflow", spi->id ); // report it
} else {
*(rxBuf++) = data; // fill up buffer
}
spi->rxBufferLength++;
}
}
void D_SPI_irqTxHandler( T_D_SPI_IF * spi, SPI_Type* spiBase, const uint32_t fifoLength) {
volatile uint32_t* statRegPtr = &(spiBase->SR);
// new comm?
if(!spi->inCom) {
T_U_RingBuffer_32bit_Elem data;
spi->rxLen = 0;
// Read first element of ringbuffer containing the transfer length
if( E_U_RingBuffer_RS_OK == E_U_RingBuffer_32bit_takeOut( &spi->txBuf, &data ) ) {
spi->rxLen = data;
}
spi->txDone = 0;
spiBase->RSER &= ~SPI_RSER_TFFF_RE_MASK; // disable tx interrupt, only needed once to enter IRQ TX
}
T_U_RingBuffer_32bit_Elem txCommand;
while( *statRegPtr & SPI_SR_TFFF_MASK && // as long as tx fifo not full
spi->txDone < spi->rxLen && // and we still have something left to send in active communication
spi->txDone - spi->rxBufferLength < fifoLength // rx Fifo has enough space for on-the-way data
) {
spi->inCom = 1;
E_U_RingBuffer_32bit_takeOut( &spi->txBuf, &txCommand ); // take tx Data
spi->base_ptr->PUSHR = txCommand; // and write it to bus
*statRegPtr = SPI_SR_TFFF_MASK; // clear Tx FIFO fill flag
spi->txDone++;
}
}
void D_SPI_irqEoCHandler( T_D_SPI_IF * spi, SPI_Type* spiBase) {
if( spi->inCom ) {
if ( spi->rxLen == spi->rxBufferLength ) {
T_D_SPI_Callback* func;
void* ctx;
E_U_RingBuffer_RS rsF = E_U_RingBuffer_32bit_takeOut( &spi->txBuf, (uint32_t*) &func );
E_U_RingBuffer_RS rsC = E_U_RingBuffer_32bit_takeOut( &spi->txBuf, (uint32_t*) &ctx );
if( E_U_RingBuffer_RS_OK == rsF && func != NULL && E_U_RingBuffer_RS_OK == rsC ) {
func(ctx, spi->id, spi->rxBuffer, spi->rxBufferLength );
}
spi->base_ptr->SR |= SPI_SR_EOQF_MASK; // clear end of queue flag
spi->rxBufferLength=0;
spi->inCom = 0; // remember that spi is free
int len = E_U_RingBuffer_32bit_length(&spi->txBuf);
if( len >= 4 ) { // new data in queue
spiBase->MCR |= SPI_MCR_HALT_MASK; // make module configuration register writable
spiBase->MCR |= SPI_MCR_CLR_TXF_MASK | // clear tx fifo
SPI_MCR_CLR_RXF_MASK; // clear rx fifo
T_U_RingBuffer_32bit_Elem elem;
E_U_RingBuffer_32bit_at( &spi->txBuf, 1, &elem );
uint32_t pcs = count_trailing_zeros((SPI_PUSHR_PCS_MASK & elem) >> SPI_PUSHR_PCS_SHIFT);
spiBase->CTAR[0] = spi->devices[pcs].CTARn.reg32; // setup timings and configuration for this channel
spiBase->MCR &= (uint32_t)~(uint32_t)(SPI_MCR_HALT_MASK); // lock configuration again
} else if ( len > 0 ) {
ERROR_MSG("Invalid TX Buffer size %d",len);
}
}
}
}
E_D_SPI_Err D_SPI_Init_LL( uint8_t interface,
E_D_SPI_SM masterSlave, uint8_t priority)
{
// create an alias
T_D_SPI_IF *spi = &D_SPI_interfaces[interface];
spi->id = interface;
spi->rxBufferLength = 0;
// enable clock to SPI module
uint8_t nvicP;
switch( interface )
{
case 0 :
SIM->SCGC6 |= SIM_SCGC6_SPI0_MASK;
nvicP = SPI0_IRQn;
break;
case 1 :
SIM->SCGC6 |= SIM_SCGC6_SPI1_MASK;
nvicP = SPI1_IRQn;
break;
case 2 :
SIM->SCGC3 |= SIM_SCGC3_SPI2_MASK;
nvicP = SPI2_IRQn;
break;
default :
ERROR_MSG("Unknown SPI %d", interface);
return E_D_SPI_Err_UkwnIf;
}
spi->base_ptr = SPI_BASE_PTRS_AR[interface];
nvicP += NVIC_OFFSET;
uint32_t nvic_idx = nvicP/32;
uint32_t nvic_bit = nvicP%32;
NVIC->ICPR[nvic_idx] |= (1<< nvic_bit); // clear any pending interrupts
NVIC->ISER[nvic_idx] |= (1<< nvic_bit); // enable interrupt for GPIO-modul
NVIC->IP[nvicP] = (priority & 0xFF) << 4; // set priority (higher nibble only)
spi->base_ptr->MCR = SPI_MCR_MSTR_MASK | // enable master mode
SPI_MCR_PCSIS(0x00) | // set inactive state to PCS to 0=low/1=high
SPI_MCR_ROOE_MASK | // enable retrieving incoming data
SPI_MCR_SMPL_PT(0x00) | // controls sample point (only valid on CPHA = 0)
SPI_MCR_CLR_TXF_MASK | // clear tx fifo
SPI_MCR_CLR_RXF_MASK | // clear rx fifo
SPI_MCR_HALT_MASK; // put transfers on hold
spi->base_ptr->SR = SPI_SR_TCF_MASK | // clear transfer complete flag
SPI_SR_EOQF_MASK | // clear end of tx queue underflow flag
SPI_SR_TFUF_MASK | // clear tx fifo underflow flag
SPI_SR_TFFF_MASK | // clear tx fifo full flag
SPI_SR_RFOF_MASK | // clear rx fifo overflow flag
SPI_SR_RFDF_MASK | // clear rx fifo not empty flag
SPI_SR_TXCTR(0x00) | // clear tx fifo counter
SPI_SR_TXNXTPTR(0x00) | // clear tx next pointer
SPI_SR_RXCTR(0x00) | // clear rx fifo counter
SPI_SR_POPNXTPTR(0x00) | // clear rx next pointer
0x00200000U;
spi->base_ptr->RSER = SPI_RSER_RFDF_RE_MASK | // enable IRQ that reports RX fifo not empty
SPI_RSER_RFOF_RE_MASK; // enable IRQ that reports RX overflow
spi->base_ptr->MCR &= (uint32_t)~(uint32_t)(SPI_MCR_HALT_MASK);
spi->inCom = 0;
return E_D_SPI_Err_Ok;
}
E_D_SPI_Err D_SPI_transfer( uint8_t interface, E_D_SPI_PCS pcs, uint16_t * data, uint16_t length, T_D_SPI_Callback cbFunc, void * cbCtx)
{
if( length == 0 )
return E_D_SPI_Err_Length;
// create an alias
T_D_SPI_IF *spi = &D_SPI_interfaces[interface];
EnterCritical();
if( E_U_RingBuffer_32bit_spaceLeft( &spi->txBuf ) < (length+3) )
{
ExitCritical();
return E_D_SPI_Err_Busy;
}
uint32_t spiInUse = E_U_RingBuffer_32bit_length( &spi->txBuf );
// set basic register for all transfers
uint32_t dataFlags = 0U; // keep PCS signal asserted
// find out which Chip-Selects have to be used
uint8_t pcsNum = getPcsNumber(pcs);
if( ! spiInUse ) {
// config SPI periphery for CPOL and CPHA
spi->base_ptr->MCR |= SPI_MCR_HALT_MASK;
spi->base_ptr->MCR |= SPI_MCR_CLR_TXF_MASK | // clear tx fifo
SPI_MCR_CLR_RXF_MASK; // clear rx fifo
spi->base_ptr->CTAR[0] = spi->devices[pcsNum].CTARn.reg32;
spi->base_ptr->MCR &= (uint32_t)~(uint32_t)(SPI_MCR_HALT_MASK);
// trigger an interrupt for pushing our data to the SPI module
spi->base_ptr->SR |= SPI_SR_TXRXS_MASK;
spi->base_ptr->RSER |= SPI_RSER_TFFF_RE_MASK;
}
// fillup buffer with transfer data
E_U_RingBuffer_32bit_insert( &spi->txBuf, (T_U_RingBuffer_32bit_Elem) length );
for(int i=0;i<length;i++)
{
// push prepared register data to buffer that includes
if( E_U_RingBuffer_RS_Full == E_U_RingBuffer_32bit_insert( &spi->txBuf,
(T_U_RingBuffer_32bit_Elem) (
data[i] | // actual bus data
dataFlags | // PCS configuration
((i==(length-1))?SPI_PUSHR_EOQ_MASK:SPI_PUSHR_CONT_MASK) | // end of transfer OR PCS signal continuous asserted
((i==0)?SPI_PUSHR_CTCNT_MASK:0) // clear transfer counter (for first bit only)
) )
)
{
ERROR_MSG("FATAL: SPI queue overflow!"); // should never happen due to interrupt EnterCritical
ExitCritical();
return E_D_SPI_Err_Length;
}
}
E_U_RingBuffer_32bit_insert( &spi->txBuf, (T_U_RingBuffer_32bit_Elem) cbFunc);
E_U_RingBuffer_32bit_insert( &spi->txBuf, (T_U_RingBuffer_32bit_Elem) cbCtx);
ExitCritical();
return E_D_SPI_Err_Ok;
}
Hi Ma Hui,
Many thanks for your response. In the mean time further attempts have been done to find a solution. We figured out that disabling ROOE in MCR register (Receive FIFO Overflow Overwrite Enable) seems to solve the problem.
However this looks bizarre because this seems to have nothing to do with our TX problem. Furthermore the overflow was never set in SR and the data we receive has a checksum that would allow us to detect if some data was missing.
Has anyone had similar problems with the ROOE bit set?
Regards
Dani
Hi,
I would recommend customer to refer MCUXpresso SDK for FRDM-K66F board DSPI driver example, which located at below path:
..\SDK_2.2_FRDM-K66F\boards\frdmk66f\driver_examples\dspi\interrupt
The DSPI interrupt service routine is below for your reference:
void EXAMPLE_DSPI_MASTER_IRQHandler(void)
{
if (masterRxCount < TRANSFER_SIZE)
{
while (DSPI_GetStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR) & kDSPI_RxFifoDrainRequestFlag)
{
masterRxData[masterRxCount] = DSPI_ReadData(EXAMPLE_DSPI_MASTER_BASEADDR);
++masterRxCount;
DSPI_ClearStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR, kDSPI_RxFifoDrainRequestFlag);
if (masterRxCount == TRANSFER_SIZE)
{
break;
}
}
}
if (masterTxCount < TRANSFER_SIZE)
{
while ((DSPI_GetStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR) & kDSPI_TxFifoFillRequestFlag) &&
((masterTxCount - masterRxCount) < masterFifoSize))
{
if (masterTxCount < TRANSFER_SIZE)
{
EXAMPLE_DSPI_MASTER_BASEADDR->PUSHR = masterCommand | masterTxData[masterTxCount];
++masterTxCount;
}
else
{
break;
}
/* Try to clear the TFFF; if the TX FIFO is full this will clear */
DSPI_ClearStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR, kDSPI_TxFifoFillRequestFlag);
}
}
/* Check if we're done with this transfer.*/
if ((masterTxCount == TRANSFER_SIZE) && (masterRxCount == TRANSFER_SIZE))
{
/* Complete the transfer and disable the interrupts */
DSPI_DisableInterrupts(EXAMPLE_DSPI_MASTER_BASEADDR,
kDSPI_RxFifoDrainRequestInterruptEnable | kDSPI_TxFifoFillRequestInterruptEnable);
}
}
Have a great day,
Ma Hui
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------