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.

TMS320F28388D: SPI transmission with more than 16 words

Part Number: TMS320F28388D
Other Parts Discussed in Thread: TMDSCNCD28388D, C2000WARE

Hi all,

I am using a TMDSCNCD28388D controlCARD as an SPI slave for controlling a power converter. The application is receiving references from a top-level control unit (SPI master) and generate the modulation and PWM signals in the DSP to drive the IGBTs, and at the same time, the DSP sends back analog measurements and different faults feedback signals to the main controller to compute the new setpoints. This application may require a number of words larger than 16 to exchange all the variables needed, especially when having more than one converter controlled by the same unit.

Leaving the application on the side, the focus is on how to achieve an SPI transmission of a number of words larger than 16. I have tried both using FIFO and non-FIFO modes (using the functions SPI_writeDataBlockingFIFO/SPI_readDataBlockingFIFO and SPI_writeDataBlockingNonFIFO/SPI_readDataBlockingNonFIFO, respectively), and I always find a limitation when the number of words is larger than 16. With 16 words or less, the communication works as intended. But I cannot succeed when sending/receiving a higher number of words, can someone help with this?

As an addition, SPI interrupts are preferably avoided as EtherCAT communication is also used and it has the top priority.

Thank you in advance! Slight smile Best regards,

\Miguel

  • Hi,

    If you want to exchange more than 16 words, you obviously need to empty the FIFO before it gets filled.

    In case you don't want to use interrupt, I would recommend you to make usage of DMA for that.

    Regards,

    Clément

  • Hi Clement,

    Thanks for your answer!

    I do not have experience with the DMA of this DSP, but as I can see in the next example provided by TI (C:\ti\c2000\C2000Ware_4_00_00_00\driverlib\f2838x\examples\c28x\spi\spi_ex5_loopback_dma.c), it uses interrupts both from the DMA and from the SPI FIFO. This should be avoided as the EtherCAT interrupts are aimed to be the only ones used. Therefore my question is:

    Is there any way to empty the RX FIFO to receive new data and fill the TX FIFO with the new variables to send to the master after the first 16 words without using interrupts?

    I hope you or someone can help me. Thanks once more!

    BR,

    Miguel

  • Miguel,

    Did you already check SPI_transmitNBytes and SPI_receiveNBytes functions (polling based functions) available in the recently released C2000Ware?

    I would also encourage you to check below examples which don't use interrupt.

    spi_ex6_eeprom - Uses polling method

    spi_ex7_eeprom_dma - Uses DMA to transmit and receive.

    Regards,

    Manoj

  • Well I was assuming you had some kind of real time cycle so you could "fire and forget" the DMA till next cycle and let him manage.

    This is basically how we manage in our design, at each real time cycle we tell the DMA where to take/store data and then at the next cycle data are in memory and we retrieve it.

    No IT usage in the loop.

    Clément

  • Hi Manoj,

    Thanks for your reply!

    Yes, I have checked all the examples available in C2000 Ware as well as the files spi.h and spi.c for the DSP I am using. I will elaborate on my system a bit more so maybe that gives you a better overview.

    The DSP in my application works as an SPI SLAVE, so I doubt I can use the EEPROM examples as the DSP acts as a master there.

    My SPI SLAVE is connected to a Real-Time System (RTS from now on) (a PLECS RT Box 3 specifically) which can emulate different power converters that we aim to control. This RTS has an SPI MASTER block, which can only be a MASTER and it manages the CLK and CS lines at a low level, so it is not possible to trigger or to control the start of SPI communication in any way. It only has a setting for a sample time for the SPI block which is a multiple of the RTS cycle time. Each SPI sample time, the RTS sends sequentially the number of words defined (which can be up to a maximum of 127), controlling internally the CLK and CS lines. This block of course has general settings like CLK polarity and phase, etc. Additionally, the only available inputs and outputs are a Tx port for the data to be sent and an Rx port for receiving the data from the slave. These ports are vectorized and their sizes are equal to the number of words set in the SPI Master block.

    Therefore, I want to read all the words sent by the RTS (converter analog variables and fault status) in the DSP and at the same time, transfer from the DSP to the RTS the new compare values for the PWM modulation. Due to control requirements, I need to use 16-bit words, and I cannot use the SPI_transmitNBytes and SPI_receiveNBytes functions as the RTS does not allow me to concatenate 8-bit words after the transmission. And this would also reduce the number of words that I could send by 2.

    As I mentioned, I am able to establish communication between the DSP and the RTS for a number of words <= to 16. When I try to send more, there is no activity on the DSP registers. Even the first 16 words are not sent/received. Could you maybe provide some solution or hint now that you know more about my application and limitations?

    Thank you in advance! BR,

    Miguel

  • Hi Clement,

    Thanks again for your help and time!

    Actually, I am working on a kind of real-time application, so that could be a possibility. Do you know where I can find an example about how to configure and use the DMA in that way? Or could you share some basic code that I could use as a starting point? That would be very appreciated.

    Additionally, if you do not mind, you can maybe have a look at my reply to Manoj where I elaborate more on my application. Maybe, this gives you a better overview of what I am attempting to do and you come up with some ideas/solutions that I could try to implement. Slight smile


    Thanks once more! BR,

    Miguel

  • Hi Miguel,

    No I don't have sorry, when we did that I started from the Technical Reference Manual to define the DMA configuration.
    In the end, code examples will not give you the answer to everything and especially not on how the peripherals effectively work.

    That is specifically true for the DMA which is a complicated peripheral to configure, in particular when doing exchanges from memory to registers.

    I did read your explanations on your application and I see some potential issues:

    1. If your number of words to receieve is variable, you will need to think about some SW mechanism to manage that wrt. the DMA configuration.
    2. You said you tried sending more than 16 words but it means you tried to put more data than the FIFO is able to receive so this feels odd.
    3. If your DSP is slave, care must be taken to the initial exchanges if your RTS and DSP are not synchronized.

    Clément

  • As I mentioned, I am able to establish communication between the DSP and the RTS for a number of words <= to 16. When I try to send more, there is no activity on the DSP registers. Even the first 16 words are not sent/received. Could you maybe provide some solution or hint now that you know more about my application and limitations?

    I would use DMA - SPI to achieve what you are trying to do. DMA needs to be configured to control both your transmit and receive operation of SPI slave. Assign a DMA channel for SPI receiver operation and another DMA channel for DMA transmit operation. Have SPIRXDMA triggered for each word SPI receives from RTS and have SPITXDMA trigger whenever SPI TX buffer is empty.

    spi_ex7_eeprom_dma example code shows how to have SPI-DMA working in master mode. Understanding this example code is good place to start as it shows how DMA needs to be configured for RX / TX channel.

  • Hi Clement,

    Hereby find my answer to your points:

    1. The number of words is not variable. It will depend on the converter to be controlled but we are planning to have a set of #defines for the number of words to be sent/received depending on the converter topology. But what we know for sure is that for the simplest converter (2level 3-phase inverter), the number of words exceeds 16.

    2. I am trying to send 32 words as a test case for the SPI communication. My intention is to do 2 transmissions of 16 words. What I am doing is to fill the FIFO with the initial16 words and after those are transmitted and the 16 from the master received, repeat the process by filling the FIFO with the next 16 ones. But it seems this is where I am failing and the FIFO is overflowing. Do you have any comments on how to achieve this? If it is even possible, of course.

    3. Yes, I have realized this can be an issue. But luckily synchronization can be achieved.

    Thanks again! BR,

    Miguel

  • Hi Manoj,

    I am looking into this alternative at the moment. But it seems the learning curve of using the DMA is quite steep. I hope I can manage to get something in good time.

    However, analyzing the proposed example, I can see how it uses FIFO and DMA interrupts. I read in the TRM that when the DMA handles a peripheral, this should not interfere with the CPU. Is this true? Would these interrupts affect the EtherCAT interrupts handled by the CPU and that must have the highest priority? Or in your opinion, this would not be a problem and it would not affect EtherCAT communication?

    Thanks once more! BR,

    Miguel

  • Hi Miguel,

    Which FIFO is overflowing ? Reception or transmission ?

    In any case, you need to only write the amount of words that is free in the FIFO, it's up to your code to check that.

    With regards to your quesion about the DMA, yes it will not interfere with the CPU as long as you are not targeting memories on which both the CPU and DMA access at the same time otherwise arbitration will occur.

    Interrupts of the DMA or SPI won't propagate to the CPU if you don't allow them to (through the PIE) so you should not fear about that.

    The DMA, if configured to, reacts on words reception or emission.

    BR,
    Clément

  • Hi Clement,

    I think both because the DSP does not receive anything from the master, and the master receives 2 times the first 16 words loaded into the FIFO. The last 16 are lost/no loaded. Regarding your point, I cannot write into the FIFO if first I load 16 words, then it is full for the next 16 words. Is it possible to empty the FIFO and load it with the second package of 16 words? I think I do not fully understand how the FIFO works then.

    Thanks for your comments on the DMA, that is really useful.

    BR,

    Miguel

  • Miguel,

    Well a FIFO is a queue, once a word is out of it you can add one.

    You don't need to empty anything manually if words have been sent, the FIFO is emptied Thinking

    Your FIFO gets emptied by the MASTER retrieving data.

    Clément

  • I read in the TRM that when the DMA handles a peripheral, this should not interfere with the CPU. Is this true?

    Yes, DMA can be used to handle SPI transmit and receive channel without CPU intervention.

    Would these interrupts affect the EtherCAT interrupts handled by the CPU and that must have the highest priority? Or in your opinion, this would not be a problem and it would not affect EtherCAT communication?

    Two things here.

    1) EtherCAT belongs to connectivity manager subsystem (ARM M4) which has its own interrupt handlers and shouldn't be affected by C28x-SPI which is connected to C28x-CPUs

    2) You have the flexibility to not trigger CPU interrupts (C28x interrupts) for SPI transactions by not enabling interrupts for SPI / even DMA in EPIE.

    Regards,

    Manoj

  • Hi Clement and Manoj,

    I have been studying and working with the SPI + DMA solution. So far I have gotten a good understanding of the DMA module and its configuration. I have done my own code based on the examples, starting with just 16 words, and I have managed to fill my received data values with the data sent by the master; however, even though my send data buffer is filled, the master does not receive any data from my slave.

    I attach my code to see if you can help me with this issue:

    //
    // Included Files
    //
    #include "driverlib.h"
    #include "device.h"
    //#include "board.h"

    //
    // Defines
    //
    #define GPIO_PIN_SPIA_SIMO 58
    #define GPIO_PIN_SPIA_SOMI 59
    #define GPIO_PIN_SPIA_CLK 60
    #define GPIO_PIN_SPIA_STEN 61

    #define SPI_slave_BASE SPIA_BASE
    #define SPI_slave_BITRATE 32000000

    #define buff_length 16 // Number of words to be transmitted/received

    #define FIFO_LVL 8 // FIFO interrupt level
    #define BURST FIFO_LVL // Each burst will empty the FIFO
    #define TRANSFER buff_length/FIFO_LVL // It will take 4 bursts of 8 to transfer
    // all data in rData

    //
    // Globals
    //
    uint16_t sData[buff_length]; // Send data buffer
    uint16_t rData[buff_length]; // Receive data buffer

    // Place buffers in GSRAM
    #pragma DATA_SECTION(sData, "ramgs0");
    #pragma DATA_SECTION(rData, "ramgs1");

    volatile uint16_t done = 0; // Flag to set when all data transferred

    //
    // Function Prototypes
    //
    void initDMA(void);
    void initSPIFIFO(void);
    __interrupt void dmaCh5ISR(void);
    __interrupt void dmaCh6ISR(void);

    //
    // Main
    //
    void main(void)
    {
    uint16_t i;

    //
    // Initialize device clock and peripherals
    //
    Device_init();

    //
    // Disable pin locks and enable internal pullups.
    //
    Device_initGPIO();

    //
    // Initialize PIE and clear PIE registers. Disables CPU interrupts.
    //
    Interrupt_initModule();

    //
    // Initialize the PIE vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    //
    Interrupt_initVectorTable();

    //
    // Board initialization
    //
    //Board_init();

    //
    // Interrupts that are used in this example are re-mapped to ISR functions
    // found within this file.
    //
    Interrupt_register(INT_DMA_CH5, &dmaCh5ISR);
    Interrupt_register(INT_DMA_CH6, &dmaCh6ISR);

    //
    // Set up DMA for SPI use, initialize the SPI for FIFO mode
    //
    initDMA();
    initSPIFIFO();


    //
    // Initialize the data buffers
    //
    for(i = 0; i < buff_length; i++)
    {
    sData[i] = i;
    rData[i]= 0;
    }

    //
    // Enable interrupts required for this example
    //
    Interrupt_enable(INT_DMA_CH5);
    Interrupt_enable(INT_DMA_CH6);

    //
    // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
    //
    EINT;
    ERTM;

    //
    // Start the DMA channels
    //
    DMA_startChannel(DMA_CH6_BASE);
    DMA_startChannel(DMA_CH5_BASE);

    //
    // Wait until the DMA transfer is complete
    //
    while(!done);

    //
    // When the DMA transfer is complete the program will stop here
    //
    ESTOP0;
    }

    //
    // Function to configure SPI A in FIFO mode.
    //
    void initSPIFIFO()
    {
    //
    // Configuration of SPI pins
    //

    // SIMO
    GPIO_setPinConfig(GPIO_58_SPIA_SIMO);
    GPIO_setPadConfig(GPIO_58_SPIA_SIMO, GPIO_PIN_TYPE_PULLUP);
    GPIO_setQualificationMode(GPIO_58_SPIA_SIMO, GPIO_QUAL_ASYNC);

    // SOMI
    GPIO_setPinConfig(GPIO_59_SPIA_SOMI);
    GPIO_setPadConfig(GPIO_59_SPIA_SOMI, GPIO_PIN_TYPE_PULLUP);
    GPIO_setQualificationMode(GPIO_59_SPIA_SOMI, GPIO_QUAL_ASYNC);

    // CLK
    GPIO_setPinConfig(GPIO_60_SPIA_CLK);
    GPIO_setPadConfig(GPIO_60_SPIA_CLK, GPIO_PIN_TYPE_PULLUP);
    GPIO_setQualificationMode(GPIO_60_SPIA_CLK, GPIO_QUAL_ASYNC);

    // STEn
    GPIO_setPinConfig(GPIO_61_SPIA_STEN);
    GPIO_setPadConfig(GPIO_61_SPIA_STEN, GPIO_PIN_TYPE_PULLUP);
    GPIO_setQualificationMode(GPIO_61_SPIA_STEN, GPIO_QUAL_ASYNC);

    //
    // Must put SPI into reset before configuring it
    //
    SPI_disableModule(SPI_slave_BASE);

    //
    // FIFO configuration
    //
    SPI_enableFIFO(SPI_slave_BASE);
    SPI_clearInterruptStatus(SPI_slave_BASE, SPI_INT_RXFF | SPI_INT_TXFF);
    SPI_setFIFOInterruptLevel(SPI_slave_BASE, (SPI_TxFIFOLevel)FIFO_LVL,
    (SPI_RxFIFOLevel)FIFO_LVL);

    SPI_setConfig(SPI_slave_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA0,
    SPI_MODE_SLAVE, 32000000, 16);
    SPI_enableHighSpeedMode(SPI_slave_BASE);
    SPI_disableLoopback(SPI_slave_BASE);
    SPI_setEmulationMode(SPI_slave_BASE, SPI_EMULATION_FREE_RUN);

    SPI_enableModule(SPI_slave_BASE);
    }

    //
    // DMA setup for both TX and RX channels.
    //
    void initDMA()
    {
    //
    // Initialize DMA
    //
    DMA_initController();

    //
    // Configure DMA Ch5 for TX. When there is enough space in the FIFO, data
    // will be transferred from the sData buffer to the SPI module's transmit
    // buffer register.
    //

    DMA_configAddresses(DMA_CH5_BASE, (uint16_t *)(SPIA_BASE + SPI_O_TXBUF),
    sData);
    DMA_configBurst(DMA_CH5_BASE, BURST, 1, 0);
    DMA_configTransfer(DMA_CH5_BASE, TRANSFER, 1, 0);
    DMA_configMode(DMA_CH5_BASE, DMA_TRIGGER_SPIATX, DMA_CFG_ONESHOT_DISABLE |
    DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT);

    //
    // Configure DMA Ch5 interrupts
    //
    DMA_setInterruptMode(DMA_CH5_BASE, DMA_INT_AT_END);
    DMA_enableInterrupt(DMA_CH5_BASE);
    DMA_enableTrigger(DMA_CH5_BASE);

    //
    // Configure DMA Ch6 for RX. When the FIFO contains at least 8 words to
    // read, data will be transferred from the SPI module's receive buffer
    // register to the rData buffer.
    //

    DMA_configAddresses(DMA_CH6_BASE, rData,
    (uint16_t *)(SPIA_BASE + SPI_O_RXBUF));
    DMA_configBurst(DMA_CH6_BASE, BURST, 0, 1);
    DMA_configTransfer(DMA_CH6_BASE, TRANSFER, 0, 1);
    DMA_configMode(DMA_CH6_BASE, DMA_TRIGGER_SPIARX, DMA_CFG_ONESHOT_DISABLE |
    DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT);

    //
    // Configure DMA Ch6 interrupts
    //
    DMA_setInterruptMode(DMA_CH6_BASE, DMA_INT_AT_END);
    DMA_enableInterrupt(DMA_CH6_BASE);
    DMA_enableTrigger(DMA_CH6_BASE);
    }

    //
    // DMA Channel 5 ISR
    //
    __interrupt void dmaCh5ISR(void)
    {
    DMA_stopChannel(DMA_CH5_BASE);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    return;
    }

    //
    // DMA Channel 6 ISR
    //
    __interrupt void dmaCh6ISR(void)
    {
    //uint16_t i;

    DMA_stopChannel(DMA_CH6_BASE);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);

    //
    // Check for data integrity
    //
    /*for(i = 0; i < 128; i++)
    {
    if (rData[i] != i)
    {
    // Something went wrong. rData doesn't contain expected data.
    ESTOP0;
    }
    }

    done = 1;*/
    return;
    }

    I hope you can help me troubleshoot this. Thanks in advance!

    Best regards,

    Miguel

  • UPDATE TO PREVIOUS POST!

    I am able to also transmit from slave to master, the problem was in the connection hardware...

    However, the code attached only works for 16 words. If I go above that number, the communication does not happen. As seen without the DMA version. Can you help with this?

    Thanks once more!

    Miguel

  • In DMA ISR routine, you are stopping the DMA channel after 16 words are transmitted / received. That is the reason it is not working when you are trying to transfer more than 16 words

    In below configuration,

    From your code snippet:

    #define buff_length 16 // Number of words to be transmitted/received

    #define FIFO_LVL 8 // FIFO interrupt level
    #define BURST FIFO_LVL // Each burst will empty the FIFO
    #define TRANSFER buff_length/FIFO_LVL // It will take 4 bursts of 8 to transfer

    BURST_SIZE is 8 words. This means you want DMA to perform 8 word transfers on DMA trigger event from SPI.

    TRANSFER_SIZE is 2 bursts. This mean one DMA transfer is complete after two bursts (8 words / burst). So, after transmitting / receiving two bursts (16 words) DMA channel is stopped (or halted). Did you try increasing the transfer size to say 3 (or) 4.

    A TRANSFER_SIZE of 3 means, you are expecting 24 words transfer.

    A TRANSFER_SIZE of 4 means, you are expecting 32 words transfer.

    Regards,

    Manoj

  • Dear Manoj,

    As can be seen in the code, the transfer size is calculated based on buff_length and FIFO_LVL. So, if any of them changes in value, the transfer size will adjust correspondingly. In my case, I have changed buff_length from 16 to 32, and when I debug the code my buffer of received data is filled with '0'

    Could you think of any other reasons that explain why is not working?

    Thanks! Best regards,

    Miguel

  • Miguel,

    Are you operating DMA in one shot mode?

    Did you check whether there was a overflow condition when you read 0? Also, is the master sending non-zero words?

    Please probe your SPI bus for any clues.

    Regards,

    Manoj

  • Hi Manoj,

    Yes, I am operating the DMA in one-shot mode.

    I have found the issue and it was on the Real-Time System side (master). Its step size needs to be increased accordingly to the number of words to be transmitted. However, if this time is too short, it does not give any error or warning.

    Thank you for your help and support! I feel now I am stronger with both the SPI and DMA of this DSP. You can close the thread Slight smile

    Have a good day! Best regards,

    Miguel

  • Good to know that you were able to fix the issue. I shall close this thread