SPI0_IRQHandler triggered immediately after EnableIRQ(SPI0_IRQ0)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

SPI0_IRQHandler triggered immediately after EnableIRQ(SPI0_IRQ0)

Jump to solution
1,592 Views
jmueckl
Contributor IV

I modified the SDK Example frdmk64f_interrupt_b2b_master to implement a DSPI byte by byte transfer interrupt function.  In the SDK example SPI0_IRQ0 isn't triggered until after the function DSPI_StartTransfer(EXAMPLE_DSPI_MASTER_BASEADDR), and is prevented from triggering by the function DSPI_StopTransfer().  In my single byte revision, SPI0_IRQ0 is being triggered repeatedly immediately after the function  EnableIRQ(SPI0_IRQn).  The behavior is different even though I have implemented the same preset of default setting - Master and the interrupt configuration is the default IRQ setting.  

Can anyone identify what my problem is?

Labels (1)
Tags (2)
0 Kudos
1 Solution
1,454 Views
jmueckl
Contributor IV

After much trial and error I am still unable to find a resolution to my problem.  I considered changing to non-interrupt driven SPI functionality, but there is still the problem that the documentation is inadequate.  I did find some better documentation, but it is for older software versions that no longer pertain. 

Furthermore, no one in the community has been able to help me with several other issues I have posted.  I have determined that my best solution is to switch to an NXP processor because the documentation for NXP processors provides much more clarity.      

View solution in original post

0 Kudos
8 Replies
1,587 Views
mjbcswitzerland
Specialist V

Hi

Which interrupt source is causing the interrupt?
Don't forget that SPI0 in thw K64 has a 4-deep FIFO so if it is the Tx interrupt triggering due to space being available you need to fill the Fifo completely for it to stop.

Regards

Mark

 

0 Kudos
1,584 Views
jmueckl
Contributor IV

When I step through my code, immediately after the code line EnableIRQ(SPI0_IRQn) the ISR is triggered.  Doesn't that imply that the source of the interrupt is SPI0.

Perhaps you can point me to a document or app note that would help me understand how the 4-byte FIFO works with DSPI.  Because I want to write and read only one byte (or integer) at a time, I am attempting to remove the four byte FIFO operation.  Would the following IRQ handler code work?     

void SPI0_IRQHANDLER(void)
{
uint32_t intStatus;
uint32_t tmpmasterCommand = masterCmd;
/* Reading all interrupt flags of status register */
intStatus = DSPI_GetStatusFlags(SPI0_PERIPHERAL);
DSPI_ClearStatusFlags(SPI0_PERIPHERAL, intStatus);
// Read POPR reg
RxData = DSPI_ReadData(SPI0);
// write PUSHR reg
SPI0->PUSHR = tmpmasterCommand | TxData;
/* Try to clear the TFFF; if the TX FIFO is full this will clear */
DSPI_ClearStatusFlags(SPI0, kDSPI_TxFifoFillRequestFlag); // See if this is required by removing it

/* Complete the transfer and disable the interrupts */
DSPI_DisableInterrupts(SPI0,
kDSPI_RxFifoDrainRequestInterruptEnable | kDSPI_TxFifoFillRequestInterruptEnable);
spiTransferCompleted = true;

#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif
}

0 Kudos
1,571 Views
mjbcswitzerland
Specialist V

Hi

If you have difficulties with FIFOs you could change to SPI1 or SPI2 which only have a depth of one.

 

mjbcswitzerland_0-1609686694554.png

 

However the only change that would be needed is to disable the Tx interrupt once your routine has written the total amount of bytes that you want to send (this means that all data is in the SPI, but not necessarily transmitted yet). This is compatible with single-FIFO depth too whereby the difference would be that generally when the final byte is written there are 1..2 bytes still in transmission in the single depth FIFO case or 4..5 bytes in the 4 deep FIFO case. Firmware transmission is simply finished earlier in the multi-FIFO case.

If you need an interrupt after the transmission has been completed enable the receive buffer interrupt at this point instead of the tx interrupt.

Regards

Mark
[uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training, solutions to problems or rapid product development requirements

For professionals searching for faster, problem-free Kinetis and i.MX RT 10xx developments the uTasker project holds the key: https://www.utasker.com/kinetis/FRDM-K64F.html

0 Kudos
1,544 Views
jmueckl
Contributor IV

I switched to SPI2 in order to use a one byte Fifo rather than four bytes, and made other changes you recommended.  However, my original problem remains.  I am posting my calling function readRegister8() and my SPI2 Handler ISR, which is wrapped with an extern C that is not shown.

Let me clarify my problem.  The ISR fires the first time appropriately.  But then it keeps firing (looping) without breaking out.  If I set a breakpoint in the ISR, and then step over each remaining function in the ISR, execution returns back to my calling function and works as expected.  But if I remove the breakpoint, the ISR doesn’t return and the thread continues to run.  Please help me understand the cause of this problem.        

void SPI2_IRQHANDLER(void)
{
uint32_t tmpmasterCommand = masterCmd;
while (DSPI_GetStatusFlags(SPI2) & kDSPI_RxFifoDrainRequestFlag)
{
RxData = DSPI_ReadData(SPI2);
DSPI_ClearStatusFlags(SPI2, kDSPI_RxFifoDrainRequestFlag);
}

while ((DSPI_GetStatusFlags(SPI2) & kDSPI_TxFifoFillRequestFlag) && masterTxCount < TRANSFER_SIZE)
{
SPI2->PUSHR = tmpmasterCommand | TxData; // write PUSHR reg
++masterTxCount;
/* Try to clear the TFFF; if the TX FIFO is full this will clear */
DSPI_ClearStatusFlags(SPI2, kDSPI_TxFifoFillRequestFlag);
}

spiTransferCompleted = true;
/* Complete the transfer and disable the interrupts */
DSPI_DisableInterrupts(SPI2,
kDSPI_RxFifoDrainRequestInterruptEnable | kDSPI_TxFifoFillRequestInterruptEnable);

/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
Store immediate overlapping exception return operation might vector to incorrect interrupt. */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif
}

/**********************************************/

uint8_t MAX31865::readRegister8(uint8_t addr)  {
    uint32_t i;
    addr &= 0x7F; // make sure top bit is not set

    uint16_t TxAddr = addr; // address to write
    //TxAddr = 0x1234; // for testing only
    masterTxCount = 0;
    masterRxCount = 0;
    spiTransferCompleted = false;
    DSPI_StopTransfer(SPI2);
    DSPI_FlushFifo(SPI2, true, true);
    DSPI_ClearStatusFlags(SPI2, (uint32_t)kDSPI_AllStatusFlag);

    while (DSPI_GetStatusFlags(SPI2) & kDSPI_TxFifoFillRequestFlag) 
    {   // Write TxAddr to master fifo
        DSPI_MasterWriteData(SPI2, &m_commandData, TxAddr);
        // clear the TFFF; if the TX FIFO is full this will clear
        DSPI_ClearStatusFlags(SPI2, kDSPI_TxFifoFillRequestFlag);
    }

    /*Enable master RX interrupt*/
    DSPI_EnableInterrupts(SPI2, kDSPI_RxFifoDrainRequestInterruptEnable);
    DSPI_StartTransfer(SPI2);

    /* Wait for 1st SPI Handler transfer to write address data to slave */
    while (!spiTransferCompleted)
    {
    }
    spiTransferCompleted = false;

    /* Wait until slave is ready */
    for (i = 0; i < EXAMPLE_DSPI_DELAY_COUNT; i++)
    {
        __NOP();
    }
    /* Wait for 2nd SPI handler to write TX data to slave*/
    RxData = 0; // RxData is read in the IRQ_Handler; don't want the first value
    masterTxCount = 0;
    masterRxCount = 0;
    DSPI_StopTransfer(SPI2);

    DSPI_FlushFifo(SPI2, true, true);
    DSPI_ClearStatusFlags(SPI2, (uint32_t)kDSPI_AllStatusFlag);

    /*Enable master RX interrupt where RxData is read and stored */
    DSPI_EnableInterrupts(SPI2, kDSPI_RxFifoDrainRequestInterruptEnable);
    DSPI_StartTransfer(SPI2);

    /* Wait for 2nd SPI Handler transfer to read Rx data from slave */
    while (!spiTransferCompleted)
    {
    }
    spiTransferCompleted = false;
    return RxData; // RxData obtained in SPI_Handler
}

 

 

0 Kudos
1,539 Views
mjbcswitzerland
Specialist V

Hi

Here is the SPI2 interrupt handler from the uTasker project as reference:

 

// Interrupt when the output fifo is not full (space to accept further data)
//
static __interrupt void int_spi_2_master(void)
{
    WRITE_ONE_TO_CLEAR(SPI2_SR, (SPI_SR_EOQF));                          // ensure that the end of queue flag is cleared
    while (((SPI2_RSER & SPI_SR_TFFF) & SPI2_SR) != 0) {                 // while there is space in the buffer and the interrupt is enabled
        fnSPITxByte(2);                                                  // put next byte into the output fifo or else disable further interrupts
        WRITE_ONE_TO_CLEAR(SPI2_SR, SPI_SR_TFFF);                        // reset the interrupt flag (after writing the next byte to be transmitted)
    }
}

 

 

fnSPITxByte() handles a software queue where data to be transmitted is taken from and written to the SPI.

 

// Send a byte to the SPI
//
extern int fnTxSPIByte(QUEUE_HANDLE channel, unsigned short usTxByte, unsigned char ucChipSelect)
{
    static unsigned long ulTransmissionSet[SPI_AVAILABLE] = { 0 };
    _KINETIS_DSPI *ptrDSPI = (_KINETIS_DSPI *)DSPI_Base_Address[channel];
    if ((ucChipSelect & FIRST_SPI_MESSAGE_WORD) != 0) {
        ulTransmissionSet[channel] ^= SPI_PUSHR_CTAS_CTAR1;              // each new message switches to the opposite transmission control set so that transmissions in progress can continue uninterrupted
        ptrDSPI->SPI_MCR |= (SPI_MCR_HALT);                              // temporarily halt the operation so that the CTAR register can be written to
        if ((ulTransmissionSet[channel] & SPI_PUSHR_CTAS_CTAR1) != 0) {
            ptrDSPI->SPI_CTAR1 = ulCharacteristics[channel][ucChipSelect & SPI_CHIP_SELECT_MASK];
        }
        else {
            ptrDSPI->SPI_CTAR0 = ulCharacteristics[channel][ucChipSelect & SPI_CHIP_SELECT_MASK];
        }
        ptrDSPI->SPI_MCR &= ~(SPI_MCR_HALT);
    }
    if ((ucChipSelect & LAST_SPI_MESSAGE_WORD) != 0) {
        ptrDSPI->SPI_PUSHR = (usTxByte | SPI_PUSHR_EOQ | ulTxChipSelectLine[channel][ucChipSelect & SPI_CHIP_SELECT_MASK] | ulTransmissionSet[channel]); // write a single byte/half-word to the output FIFO - negate CS line after transmission
    }
    else {
        ptrDSPI->SPI_PUSHR = (usTxByte | SPI_PUSHR_CONT | ulTxChipSelectLine[channel][ucChipSelect & SPI_CHIP_SELECT_MASK] | ulTransmissionSet[channel]); // write a single byte/half-word to the output FIFO - assert CS line
    }
    ptrDSPI->SPI_RSER = (SPI_SRER_TFFF_RE | SPI_SRER_EOQF_RE);           // interrupt as long as the transmit fifo is not full or when final transmission terminates
    return 0;
}

 

 

If there is no more data in the queue the interrupt is disabled:

 

// This routine is used to block further transmit interrupts
//
extern void fnClearSPITxInt(QUEUE_HANDLE channel)
{
    _KINETIS_DSPI *ptrDSPI = (_KINETIS_DSPI *)DSPI_Base_Address[channel];
    ptrDSPI->SPI_RSER &= ~(SPI_SRER_TFFF_RE);                            // disable interrupts
}

 

If you compare you may find something explaining your problem.

Regards

Mark
[uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training, solutions to problems or rapid product development requirements

For professionals searching for faster, problem-free Kinetis and i.MX RT 10xx developments the uTasker project holds the key: https://www.utasker.com/kinetis/FRDM-K64F.html

0 Kudos
1,533 Views
jmueckl
Contributor IV

The uTasker code you have presented uses totally different functions than the code I pasted.  If we want to compare the code lets compare it to the SDK example frdmk64f_dspi_interrupt_b2b_master, which I am able to run without the issue I am having.  Below I post its IRQHandler which I modified in order to reduce the four byte Fifo to one byte.  I believe that I have converted it correctly.  It would be too much detail to include the code which performs my byteRead() function, but I am using the identical functions as those used in the SDK example.

I am suspecting that the issue may be a result of incorrect configuration code, so I now list the related functions of both the SDK example code and mine, in order.  There is one difference between them, as I point out below.  Can someone tell me the significance of this difference?

frdmk64f_dspi_interrupt_b2b_master

  srcClock_Hz = EXAMPLE_DSPI_MASTER_CLK_FREQ;

1 DSPI_MasterInit(EXAMPLE_DSPI_MASTER_BASEADDR, &masterConfig, srcClock_Hz);

2 EnableIRQ(EXAMPLE_DSPI_MASTER_IRQ);

3 masterCommand  = DSPI_MasterGetFormattedCommand(&commandData);

4 masterFifoSize = FSL_FEATURE_DSPI_FIFO_SIZEn(EXAMPLE_DSPI_MASTER_BASEADDR);

5 DSPI_StopTransfer(EXAMPLE_DSPI_MASTER_BASEADDR);

6 DSPI_FlushFifo(EXAMPLE_DSPI_MASTER_BASEADDR, true, true);

7 DSPI_ClearStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR, (uint32_t)kDSPI_AllStatusFlag);

/*Fill up the master Tx data*/

8 while (DSPI_GetStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR) & kDSPI_TxFifoFillRequestFlag) …

 My Code

1 DSPI_MasterInit(SPI2_PERIPHERAL, &SPI2_config, SPI2_CLK_FREQ)  

  This is the one function that is different. It was included by Config Tools:

   I attempted to remove the second parameter to no avail.

  DSPI_EnableInterrupts(SPI2_PERIPHERAL, (kDSPI_TxCompleteInterruptEnable |  

kDSPI_EndOfQueueInterruptEnable));

2 EnableIRQ(SPI2_IRQN);

3 masterCommand  = DSPI_MasterGetFormattedCommand(&commandData);

4 masterFifoSize = FSL_FEATURE_DSPI_FIFO_SIZEn(SPI2);

5 DSPI_StopTransfer(SPI2);

6 DSPI_FlushFifo(SPI2, true, true);

7 DSPI_ClearStatusFlags(SPI2, (uint32_t)kDSPI_AllStatusFlag);

8 while (DSPI_GetStatusFlags(SPI2) & kDSPI_TxFifoFillRequestFlag) …

 

Here is the IRQHandler straight out of the SDK example frdmk64f_dspi_interrupt_b2b_master.  I already posted my version.

void EXAMPLE_DSPI_MASTER_IRQHandler(void)  {

    uint32_t tmpmasterTxCount = masterTxCount;

    uint32_t tmpmasterRxCount = masterRxCount;

    uint32_t tmpmasterCommand = masterCommand;

    if (tmpmasterRxCount < TRANSFER_SIZE)  {

        while (DSPI_GetStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR) &          kDSPI_RxFifoDrainRequestFlag)  {

            masterRxData[tmpmasterRxCount] = SPI_ReadData(EXAMPLE_DSPI_MASTER_BASEADDR);

            ++masterRxCount;

            tmpmasterRxCount = masterRxCount;

            DSPI_ClearStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR, kDSPI_RxFifoDrainRequestFlag);

            if (tmpmasterRxCount == TRANSFER_SIZE) {

                break;

            }

        }

    }

    if (tmpmasterTxCount < TRANSFER_SIZE)    {

        while ((DSPI_GetStatusFlags(EXAMPLE_DSPI_MASTER_BASEADDR) & kDSPI_TxFifoFillRequestFlag) &&  ((tmpmasterTxCount - tmpmasterRxCount) < masterFifoSize))  {

            if (tmpmasterTxCount < TRANSFER_SIZE)   {

                EXAMPLE_DSPI_MASTER_BASEADDR->PUSHR = tmpmasterCommand | masterTxData[tmpmasterTxCount];

                ++masterTxCount;

                tmpmasterTxCount = 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 ((tmpmasterTxCount == TRANSFER_SIZE) && (tmpmasterRxCount == TRANSFER_SIZE))    {

        isTransferCompleted = true;

        /* Complete the transfer and disable the interrupts */

        DSPI_DisableInterrupts(EXAMPLE_DSPI_MASTER_BASEADDR,

                      kDSPI_RxFifoDrainRequestInterruptEnable | kDSPI_TxFifoFillRequestInterruptEnable);

    }

    SDK_ISR_EXIT_BARRIER;

}

Tags (2)
0 Kudos
1,524 Views
mjbcswitzerland
Specialist V

Hi

I posted the code since it shows low level accesses (and not functions), whereby the functions in the SDK are just encapsulating low level accesses but make them often less understandable (without studying each low level function separately).

Since I am not familiar with the SDK routines (but more familiar with the practical low level operations) I don't think that I would be very efficient at such an analysis. If you have a working reference that starts failing after specific modifications I think that you will need to make step-by-step/incremental changes from the working reference to identify when it starts departing from the expected behavior.

Regards

Mark

0 Kudos
1,455 Views
jmueckl
Contributor IV

After much trial and error I am still unable to find a resolution to my problem.  I considered changing to non-interrupt driven SPI functionality, but there is still the problem that the documentation is inadequate.  I did find some better documentation, but it is for older software versions that no longer pertain. 

Furthermore, no one in the community has been able to help me with several other issues I have posted.  I have determined that my best solution is to switch to an NXP processor because the documentation for NXP processors provides much more clarity.      

0 Kudos