This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320F28075: SPI Failing when Interrupt Enabled

Part Number: TMS320F28075

Hi all,

I am using a TMS320F28075 controller and trying to communicate to an external flash device over SPI. Everything works correctly until I enable a 20 Khz interrupt "appTask", code below. The code inside this interrupt is critical and so I cant disable it or make it smaller.

With this 20 Khz interrupt enabled, not all the data is written to or read correctly from the external flash. Only small chunks of data are successfully written/read and the amount is random.

It looks like the interrupt is interfering with SPI comms. I would have thought the SPI's peripherals are external and would work independently of the main thread or interrupts.

Is it possible to have the below interrupt enabled and have the SPI communication working correctly? Or am I doing something wrong?

Any help would be greatly appreciated. 

// -------------------------- interrupt code --------------------------------------------------

/* Set the interrupt vector */
EALLOW;
{
PieVectTable.TIMER0_INT = &appTask;
}
EDIS;

/* Configure CPU-Timer 0: */
ConfigCpuTimer( &CpuTimer0,
( SYSCLK / 1000000 ),
MAINTENANCE_PERIOD_uS );

CpuTimer0Regs.TCR.all = 0x4000; // Use write-only instruction to set TSS bit = 0

/* Enable CPU int1 which is connected to CPU-Timer 0 */
IER |= M_INT1;

/* Enable TINT0 in the PIE: Group 1 interrupt 7 */
PieCtrlRegs.PIEIER1.bit.INTx7 = 1;

/* Enable global Interrupts and higher priority real-time debug events: */
EINT;
ERTM;

// -------------------------- interrupt code --------------------------------------------------



// ------------------------- SPI port init -----------------------------------------------------

/* Control Register (SPICCR).... 'normal' values
* Clock polarity normal
* Loopback off
* SPI Character length (see SPRUHM9B Table 17-9) 0xF => 16-bits
* SPI High Speed mode disabled (not allowed with selected GPIO - see SPRUHM9B section 17.2.2)
*/
SPI_PORT.SPICCR.bit.CLKPOLARITY = 1;
SPI_PORT.SPICCR.bit.SPILBK = 0;
SPI_PORT.SPICCR.bit.SPICHAR = 0x0F;
SPI_PORT.SPICCR.bit.HS_MODE = 0; //sww12jan16

/* SPI Operation Control Register (SPICTL)....
* Disable rx overrun interrupt
* Clock phase normal
* Master mode
* Transmit enabled
* Enable SPI interrupt
*/
SPI_PORT.SPICTL.bit.OVERRUNINTENA = 0;
SPI_PORT.SPICTL.bit.CLK_PHASE = 1;
SPI_PORT.SPICTL.bit.MASTER_SLAVE = 1;
SPI_PORT.SPICTL.bit.TALK = 1;
SPI_PORT.SPICTL.bit.SPIINTENA = 1;

/* SPI Status Register (SPISTS).... 'normal' values
* Clear any outstanding flags
*/
SPI_PORT.SPISTS.all = 0;

/* SPI Baud Rate Register (SPIBRR)
* max baud rate must be less than the max gpio toggle frequency = 25MHz (See SPRUHM9B section 17.3.5 and SPRS902B section 5.7.6.1)
* and less than the slowest device - TC77 max frequency = 7MHz (could select different rates for different devices)
* For SPIBRR > 2, SPI Baud Rate LSPCLK/(SPIBRR+1), where LSPCLK = SYSCLK/4 = 30Mhz
* For SPIBRR = 0, 1, or 2, baud = LSPCLK/4 = 7.5MHz
*/
SPI_PORT.SPIBRR.bit.SPI_BIT_RATE = 4; /* 30/(4+1) = 6MHz jan16 */

/* SPI FIFO Transmit Register (SPIFFTX)
* 0xE0 => fifo enable; enhancements enable; tx enable
* 0x63 => clear outstanding INT; interrupt on FiFo <=3
* !!DAVEC!! set FiFo for best latency at 5MHz
*/
/* Reset all to default */
SPI_PORT.SPIFFTX.all = 0;
SPI_PORT.SPIFFTX.all = 0xE063;

/* SPI FIFO Receive Register (SPIFFRX)
* 0x60 => clear overflow; rx enable;
* 0x61 => clear outstanding INT; interrupt on FiFo >= 1
*/
/* Reset all to default */
SPI_PORT.SPIFFRX.all = 0;
SPI_PORT.SPIFFRX.all = 0x6061;

SPI_PORT.SPICCR.bit.SPILBK = 0;

/* SPI FIFO Control Register (SPIFFCT)
* Delay between FiFo and transmit Shift Reg.
*/
SPI_PORT.SPIFFCT.bit.TXDLY = 0x00;

/* SPI Priority Control Register (SPIPRI)
* Free-run on emulation suspend
* SPISTE non-inverted (not used here anyway)
* Normal 4-wire SPI mode
*/
SPI_PORT.SPIPRI.bit.FREE = 1;
SPI_PORT.SPIPRI.bit.STEINV = 0;
SPI_PORT.SPIPRI.bit.TRIWIRE = 0;



// --------------------------------------------------------------
Thanks for your time.

  • Hi Warrick,

    Can you expand on what failure you are seeing on the SPI? What is the SPI communicating with? Does it have some real time requirement that would break if the SPI communication is delayed?

  • Hi Gus,

    Thanks for your quick response.

    I'm trying to log about 20 bytes of data, every second, to a IS25LQ512B flash device (datasheet attached). From the datasheet I cant see anything regarding real time requirement's or maximum time delays for a successful transaction but I can ask the manufacturer if that's where you think the problem could be?

    With the interrupt enabled, I cannot consistently and reliably write the full 20 bytes of data to the external flash. For example, if I write 20 bytes of data, sometimes 10 bytes are loaded into the flash, sometimes only 5 bytes, sometimes no bytes at all and sometimes all of the bytes are successfully written to the flash. The number of bytes successfully written is random. There is a similar problem when reading from the flash whereby I cant read all of the data from the flash and the number of successfully read bytes is random. I can send through oscilloscope images and other diagnostics if that would help?

    If I enable the interrupt but comment out all of the code inside this interrupt then reading/writing to the flash works correctly. If I remove all of the code from this interrupt and add a small delay (just a for loop) inside the interrupt, then the reads/writes again fail. So it definitely has something to do with enabling the interrupt and the time of execution of the interrupt. Unfortunately the code inside this interrupt is critical for us and we can't make the function smaller or disable it. I also tried to disable the interrupt before reading/writing to the external flash using "DINT and EINT" but this didn't work. 

    IS25LQ512B Datasheet.pdf

  • I see in the flash device some operations can take 16 clocks, others 32, and others even more. The entire operation is encapsulated within a CE# low pulse. How are you controlling the CE# pin of the flash device? We typically recommend that the chip enable be controlled through a GPIO pin on the MCU. The customer code would set the chip select low before sending a command and then set it high when done. However, tying it to the SPISTE pin w/ use of the FIFO can also work. The latter method is trickier since the SPI will only keep SPISTE low as long as there is data in the FIFO. If the CPU is interrupted during a SPI FIFO write and the FIFO becomes empty, then the SPISTE pin can go high. To a flash device this would signal the end of the command and the sequence would break.

  • Hi Gus,

    We are using two GPIO pins, one to to latch a <deviceSelect> pattern into a cascadable shift register, so we can select between different slaves and the other to enable the selected slave, using the CE pin. We aren't using it's automatic SPISTE slave chip-select pin and we are enabling and disabling the CE pin in the right places. I think if this was wrong, even with the interrupt disabled we would have issues.

    The SPI is also using  "__interrupt void spiTxFifoIsr( void );  __interrupt void spiRxFifoIsr( void );" to handle sending and receiving data over the SPI network. Can the TMS320F28075 handle these different interrupts without jeopardizing the SPI peripheral?

    Thanks for your time.

  • We are using two GPIO pins, one to to latch a <deviceSelect> pattern into a cascadable shift register, so we can select between different slaves and the other to enable the selected slave, using the CE pin. We aren't using it's automatic SPISTE slave chip-select pin and we are enabling and disabling the CE pin in the right places. I think if this was wrong, even with the interrupt disabled we would have issues.

    I was only speculating in case SPISTE was used, but you are not using it, so moot point.

    The SPI is also using  "__interrupt void spiTxFifoIsr( void );  __interrupt void spiRxFifoIsr( void );" to handle sending and receiving data over the SPI network. Can the TMS320F28075 handle these different interrupts without jeopardizing the SPI peripheral?

    This depends entirely on how you have architected your code. Not possible for me to say.

    If you have any additional debug information, please let me know.

  • Hi Gus,

    Thanks for the reply. The reading of the flash looks to be working even with the interrupt enabled, so it's just the writes that are failing with the 20 KHz interrupt enabled. This is the code to setup the 20 KHz appTask Interrupt. If I comment out the line "IER |= M_INT1;" then writing to the external flash works correctly. But with this interrupt enabled, the writes fail. The SPI initialization is shown in the post above. 

    This is how the interrupts have been setup, do you see any potential problems with the interrupts here?

    // ----------------------- 20 KHz interrupt setup -----------------------------
    
    voltageSoftStart.presentLimit = sCConfParam.voltage.softstart.presentLimit;
    voltageSoftStart.slewRate = 0;
    voltageSoftStart.targetLimit = 0;
    
    currentSoftStart.presentLimit = sCConfParam.current.softstart.presentLimit;
    currentSoftStart.slewRate = 0;
    currentSoftStart.targetLimit = 0;
    
    indexMinTresh = 0;
    
    /* Take the current 50Hz AC comparator states and log them.*/
    
    /* Set the interrupt vector */
    EALLOW;
    {
    PieVectTable.TIMER0_INT = &appTask;
    }
    EDIS;
    
    InitCpuTimers();
    
    /* Configure CPU-Timer 0: */
    ConfigCpuTimer( &CpuTimer0,
    ( SYSCLK / 1000000 ),
    MAINTENANCE_PERIOD_uS );
    
    // To ensure precise timing, use write-only instructions to write to the entire register. Therefore, if any
    // of the configuration bits are changed in ConfigCpuTimer and InitCpuTimers (in DSP2803x_CpuTimers.h), the
    // below settings must also be updated.
    
    CpuTimer0Regs.TCR.all = 0x4000; // Use write-only instruction to set TSS bit = 0
    
    /* Enable CPU int1 which is connected to CPU-Timer 0 */
    IER |= M_INT1;
    
    /* Enable TINT0 in the PIE: Group 1 interrupt 7 */
    PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
    
    /* Enable global Interrupts and higher priority real-time debug events: */
    EINT;
    ERTM;
    
    // ---------------------- SPI Tx init -----------------------------------------------
    
    static void spiTxIntInitialise( void )
    {
    /* Set Interrupt service address */
    EALLOW;
    {
    PieVectTable.SPI_TX_VECTOR = &spiTxFifoIsr; /* sww25nov15 was PieVectTable.TX_VECTOR = &spiTxFifoIsr; */
    }
    EDIS;
    
    /* Acknowledge any outstanding interrupt */
    spiTxIntAcknowledge();
    
    /* Enable interrupt group in CPU */
    IER |= SPI_INTGRP; //sww25nov15 was IER |= M_INT6; */  // M_INT6  0x0020
    
    /* Start with interrupts disabled. (nothing going on yet) */
    SPI_RXDIS; /* sww25nov15 was RXDIS; */  // (PieCtrlRegs.PIEIER6.bit.INTx9 = 0)
    SPI_TXDIS; /* sww25nov15 was TXDIS; */  // (PieCtrlRegs.PIEIER6.bit.INTx10 = 0)
    
    /* Enable global Interrupts and higher priority real-time debug events: */
    EINT;
    ERTM;
    }
    
    // ------------------------- SPI Rx init  -------------------------------
    
    static void spiRxIntInitialise( void )
    {
    /* Set Interrupt service address */
    EALLOW;
    {
    PieVectTable.SPI_RX_VECTOR = &spiRxFifoIsr; /* sww25nov15 was PieVectTable.RX_VECTOR = &spiRxFifoIsr; */
    }
    EDIS;
    
    /* Acknowledge any outstanding interrupt */
    spiRxIntAcknowledge();
    
    /* Enable interrupt group in CPU */
    IER |= SPI_INTGRP; //sww25nov15 was IER |= M_INT6; */  // M_INT6  0x0020
    
    /* Start with Rx interrupt disabled. (nothing going on yet) */
    SPI_RXDIS; /* sww25nov15 was RXDIS; */  // (PieCtrlRegs.PIEIER6.bit.INTx9 = 0)
    
    /* Enable global Interrupts and higher priority real-time debug events: */
    EINT;
    ERTM;
    }
    
    // --------------------------SPI Tx ISR -------------------------------------------
    
    __interrupt void spiTxFifoIsr( void )
    {
    #if PERF_PROFILE_GPIO
    /* Profile Mark */
    uP_DIAGNOSTIC4_SET();
    #endif
    
    /* Service Tx buffer */
    while( ( SPI_PORT.SPIFFTX.bit.TXFFST < 2 ) &&
    ( isrTxCount != 0 ) )
    {
    SPI_PORT.SPITXBUF = isrTxBuffer[ isrTxIndex++ ];
    isrTxCount--;
    
    }
    
    /* Finished sending? Disable Tx interrupts */
    if( 0 == isrTxCount )
    {
    SPI_TXDIS; /* sww25nov15 was TXDIS; */
    }
    
    /* Acknowledge Device and CPU interrupt */
    
    /* Clear interrupt flag */
    SPI_PORT.SPIFFTX.bit.TXFFINTCLR = 1;
    
    /* Pie */
    SPI_PIEACK = 1; /* sww25nov15 was PieCtrlRegs.PIEACK.bit.ACK6 = 1; */
    
    #if PERF_PROFILE_GPIO
    /* Profile Mark */
    uP_DIAGNOSTIC4_CLEAR();
    #endif
    
    }
    
    // --------------------------- SPI Rx ISR ------------------------------------------
    
    
    
    __interrupt void spiRxFifoIsr( void )
    {
    uint16_t temp;
    
    #if PERF_PROFILE_GPIO
    /* Profile Mark */
    uP_DIAGNOSTIC4_SET();
    #endif
    
    /* Service Rx buffer */
    /* FiFo Status 0 => empty */
    while( SPI_PORT.SPIFFRX.bit.RXFFST != 0 )
    {
    temp = SPI_PORT.SPIRXBUF;
    
    /* Transfer to client buffer if it is valid */
    if( isrRxBuffer != NULL )
    {
    isrRxBuffer[ isrRxIndex++ ] = temp;
    }
    }
    
    /* Service Rx overrun (prev character lost) */
    if( 1 == SPI_PORT.SPIFFRX.bit.RXFFOVF )
    {
    /* Clear hardware
    * (!! Should really abort txn. as we can't rely on char count
    * if overflow)
    * Indicate overflow status to caller
    * */
    SPI_PORT.SPIFFRX.bit.RXFFOVFCLR = 1;
    //DAVEC: isrRxIndex = isrRxCount;
    isrTxnStatus = SPI_DV_RXOVF;
    }
    
    /* Set Rx FiFo for any remaining characters (max 4 characters) */
    temp = isrRxCount - isrRxIndex;
    if( temp > 1 )
    {
    temp = 1;
    }
    if( temp > 0 )
    {
    SPI_PORT.SPIFFRX.bit.RXFFIL = temp;
    }
    
    /* Finished? (Expected nChars received) */
    else
    {
    /* sww19jan16 if new mux data has just been sent then generate active edge to latch data */
    if( SPI_MUX == isrSpiTarget )
    {
    uP_SPI_MUX_CS_ACTIVE();
    }
    
    /* Flag device ready unless error (=> API transaction complete) */
    if( SPI_DV_BUSY == isrTxnStatus )
    {
    isrTxnStatus = SPI_OK;
    }
    
    /* Disable Rx Interrupts */
    SPI_RXDIS; /* sww25nov15 was RXDIS; */
    
    }
    
    /* Acknowledge device and CPU interrupt */
    spiRxIntAcknowledge();
    
    #if PERF_PROFILE_GPIO
    /* Profile Mark */
    uP_DIAGNOSTIC4_CLEAR();
    #endif
    
    }
    
    static void spiRxIntAcknowledge( void )
    {
    /* Clear port overflow and interrupt flags */
    SPI_PORT.SPIFFRX.bit.RXFFOVFCLR = 1;
    SPI_PORT.SPIFFRX.bit.RXFFINTCLR = 1;
    
    /* Pie */
    SPI_PIEACK = 1; /* sww25nov15 was PieCtrlRegs.PIEACK.bit.ACK6 = 1; */
    }
    static void spiTxIntAcknowledge( void )
    {
    /* Clear interrupt flag */
    SPI_PORT.SPIFFTX.bit.TXFFINTCLR = 1;
    
    /* Pie */
    SPI_PIEACK = 1; /* sww25nov15 was PieCtrlRegs.PIEACK.bit.ACK6 = 1; */
    }
    
    // ---------------------------------------------------------------------

  • I don't see anything immediately obvious. I do not see the control of the GPIO being used for chip select in the SPI TX ISR, but I'm assuming that's somewhere else. That would make sense, I guess: some other code takes care of queuing the flash commands, and the SPITX ISR is only transferring the data in isrTxBuffer. 

    Have you tried isolating the failure to something specific or repeatable? Maybe try to capture the SPI bus signals during a flash write command & see if these look odd or wrong? 

    Since you mentioned that adding the ISR code (or delay inside the ISR) breaks the SPI comm, have you profiled this ISR code? In other words, how many MIPS does it leave for other tasks, including the SPI comms? Could it be that the ISR is completely taking up all the CPU bandwidth? Here again I am wondering of the flash will ignore any write commands that are interrupted or delayed.  

  • FYI, there is an "insert code" command that you can use when pasting code. It makes it 1000% easier for us to review code. 

  • Hi Gus, thanks for going through the code and yes the GPIO control is done elsewhere - thought that was too many lines of code to post.

    And sorry i didn't realize there was an easier way to post code on here, I'll do that next time. 

    I did check how much CPU time the ISR was using and it was using less than I was expecting. So I think there are enough clock cycles left for other tasks. I'll check that again though and I'll try capture the failed packets using an oscilloscope, hopefully that will give me more information into how it's failing.

    Thanks for your time with this.

    Kind Regards,

    Warrick