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.

MSP432 SPI with DMA

Hi,

I'm a bit new to this...

I'm wanting to improve the performance of the SPI, I understand that using DMA is the best solution. 

Are their any example of SPI with DMA?

Currently without DMA, I have the SPI setup to send 1 byte at a time with:

SPI_transmitData(LCD_EUSCI_MODULE, data);

What I want to do is change this so I can fill a buffer with bytes, then say Send. Once thats finished sending I want to fill the buffer again and send the next set of bytes.

I tried to use this code although it appears to hang at the last line!

    DMA_enableModule();
    DMA_setControlBase(m_controlTable);

    DMA_assignChannel(DMA_CH0_EUSCIB0TX0);

    DMA_disableChannelAttribute(DMA_CH0_EUSCIB0TX0, UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);

    DMA_setChannelControl(UDMA_PRI_SELECT | DMA_CH0_EUSCIB0TX0, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_8 | UDMA_ARB_8);
    DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_CH0_EUSCIB0TX0, UDMA_MODE_BASIC, m_data_array, (void*)SPI_getTransmitBufferAddressForDMA(EUSCI_B0_MODULE), 1024);

    DMA_assignInterrupt(DMA_INT1, 0);
    Interrupt_enableInterrupt(INT_DMA_INT1);

I was planning to use the code above by filling the buffer "m_data_array" then calling DMA_enableChannel(0); although this didn't work.

How do i know when the buffer has been transmitted?

  • Hello John,

    There is a I2C with DMA example as part of the MSP432 Driverlib package (dma_eusci_i2c_loopback). Have you tried modifying this code for your own purposes? You should be able to switch out the I2C initialization for SPI and completely remove the DMA section of code for receiving bytes (channel 5). Doing so should give you the desired application of sending a data array through DMA.

    Regards,
    Ryan
  • That's exactly what I have done.

    I'm using C++ is that the issue?

    I just can't get the code ti run past this line:

    Interrupt_enableInterrupt(INT_DMA_INT1);

    In that I2C example i see this function:

    void dma_1_interrupt(void)
    {
        /* Disabling the completion interrupt and disabling the DMA channels */
        MAP_DMA_disableChannel(2);
        MAP_DMA_disableInterrupt(INT_DMA_INT1);
        sendStopCondition = true;
    }

    Although I see know reference to how this would be called? 

  • John,

    The programming language you used shouldn't be of importance. A quick note that is unrelated to your issue, on the DMA_setChannelControl you should be using UDMA_DST_INC_0 so that you do not increment the destination which is the TX buffer. The dma_1_interrupt ISR is called by the DMA flag which is set after the DMA finishes sending the entire data_array.

    Can you post your code in its entirety? I'm going to try to bring in the author of this example code to help debug your issue.

    Regards,
    Ryan
  • Hey John,

    I was able to write up some demo code that performs the application you requested, attached.  Just change the eUSCI_A0 module for that of your own choosing.

    dma_eusci_spi.c
    /*
     * -------------------------------------------
     *    MSP432 DriverLib - v2_20_00_08 
     * -------------------------------------------
     *
     * --COPYRIGHT--,BSD,BSD
     * Copyright (c) 2014, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * --/COPYRIGHT--*/
    /*******************************************************************************
     * MSP432 DMA - eUSCI SPI Transfer Using DMA
     *
     * Description: In this code example, the MSP432 's DMA controller is used in 
     * conjunction with the eUSCI_A0 SPI configuration to demonstrate how to use
     * hardware triggered DMA transfers. One DMA transfer is setup, Channel 0 is
     * for the DMA transfer. Once the transfers are setup and initialized, the
     * device is put to sleep. While sleeping, the DMA controller will automatically
     * transfer the data from the const array and out through the uESCI A0 SPI
     * module as a master. When the DMA transfer is complete, an interrupt will be
     * fired and the master will disable further interrupts
     *
     * This program runs infinitely until manually halted by the user.
     *
     *                MSP432P401
     *             ------------------
     *         /|\|                  |
     *          | |                  |
     *          --|RST    P1.1 (CLK) |
     *            |       P1.3 (MOSI)|
     *            |                  | 
     *
     * Author: Ryan Brown
     ******************************************************************************/
    /* DriverLib Includes */
    #include "driverlib.h"
    
    /* Standard Includes */
    #include <stdint.h>
    
    #include <string.h>
    #include <stdbool.h>
    
    /* SPI Master Configuration Parameter */
    const eUSCI_SPI_MasterConfig spiMasterConfig =
    {
            EUSCI_A_SPI_CLOCKSOURCE_ACLK,              // ACLK Clock Source
            32768,                                     // ACLK = LFXT = 32.768khz
            500000,                                    // SPICLK = 500khz
            EUSCI_A_SPI_MSB_FIRST,                     // MSB First
            EUSCI_A_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT,    // Phase
            EUSCI_A_SPI_CLOCKPOLARITY_INACTIVITY_HIGH, // High polarity
            EUSCI_A_SPI_3PIN                           // 3Wire SPI Mode
    };
    
    /* DMA Control Table */
    #ifdef ewarm
    #pragma data_alignment=256
    #else
    #pragma DATA_ALIGN(controlTable, 256)
    #endif
    uint8_t controlTable[256];
    
    /* Extern */
    extern uint8_t data_array[];
    
    int main(void)
    {
        /* Halting Watchdog */
        MAP_WDT_A_holdTimer();
    
        /* Starting and enabling LFXT (32kHz) */
        GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_PJ,
                GPIO_PIN0 | GPIO_PIN1, GPIO_PRIMARY_MODULE_FUNCTION);
        CS_setExternalClockSourceFrequency(32768, 0);
        CS_initClockSignal(CS_ACLK, CS_LFXTCLK_SELECT, CS_CLOCK_DIVIDER_1);
        CS_startLFXT(CS_LFXT_DRIVE0);
    
        /* Selecting P1.1 P1.2 and P1.3 in SPI mode */
        GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
                GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
    
        /* Configuring SPI in 3wire master mode */
        SPI_initMaster(EUSCI_A0_MODULE, &spiMasterConfig);
    
        /* Enable SPI module */
        SPI_enableModule(EUSCI_A0_MODULE);
    
        /* Configuring DMA module */
        MAP_DMA_enableModule();
        MAP_DMA_setControlBase(controlTable);
    
        /* Assigning Channel 0 to EUSCIA0TX0 */
        MAP_DMA_assignChannel(DMA_CH0_EUSCIA0TX);
    
         /* Disabling channel attributes */
        MAP_DMA_disableChannelAttribute(DMA_CH0_EUSCIA0TX,
                                         UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
                                         UDMA_ATTR_HIGH_PRIORITY |
                                         UDMA_ATTR_REQMASK);
    
        /* Setting Control Indexes */
        MAP_DMA_setChannelControl(UDMA_PRI_SELECT | DMA_CH0_EUSCIA0TX,
                UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1);
        MAP_DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_CH0_EUSCIA0TX,
                UDMA_MODE_BASIC, data_array,
                (void*) MAP_SPI_getTransmitBufferAddressForDMA(EUSCI_A0_MODULE), 1024);
    
        /* Assigning/Enabling Interrupts */
        MAP_DMA_assignInterrupt(DMA_INT1, 0);
        MAP_Interrupt_enableInterrupt(INT_DMA_INT1);
    
        /* Now that the DMA is primed and setup, enabling the channels. The EUSCI
         * hardware should take over and transfer/receive all bytes */
        MAP_DMA_enableChannel(0);
    
        /* Polling to see if the TX buffer is ready */
        while (!(SPI_getInterruptStatus(EUSCI_A0_MODULE,EUSCI_A_SPI_TRANSMIT_INTERRUPT)));
    
        MAP_PCM_gotoLPM0InterruptSafe();
    
        while(1);
    }
    
    /* Completion interrupt for eUSCIA0 TX */
    void dma_1_interrupt(void)
    {
        /* Disabling the completion interrupt and disabling the DMA channels */
        MAP_DMA_disableChannel(0);
        MAP_DMA_disableInterrupt(INT_DMA_INT1);
    }
    

    Regards, Ryan

  • Hi Ryan,

    I modified you code a bit - it was not working for me as is. Moved to DCO clock and used EUSCI_A1 instead of A0. I also added a general GPIO pin to be used as CS. Here is the modified source code:
    http://pastebin.com/kGuX4V3D

    Something very peculiar is going on with the code and I would appreciate your help: the interrupt routine is triggered before the execution of line 140 which is very odd.

    I added an extra line to call disableChannel(2) before the DMA setup, but still if I put a breakpoint in the interrupt handler and line 140, the interrupt handler is executed first.

    What is even more interesting is that if I use logic analyzer there is no data transmitted over the SPI by the time the interrupt is triggered and the data is sent afterwards.

    Can you help identify the source of this spurious interrupt?

    A different side question: can someone explain the meaning of DMA_CH2_EUSCIB2TX3 as DMA channel, what is TX3 on EUSCI_B2?

    ~Venelin
  • Hi Venelin,

    The website you have posted your code to is blocked by my server, please upload the code directly using the "Insert code using Syntaxhighlighter" tool under rich formatting. Remember that GPIO pins and most DMA command lines have to be changed to replace EUSCI_A1 to EUSCI_A0, failing to do so in any one spot will most likely cause the code to fail.

    Look at "Table 6-8. DMA Sources" in the MSP432P401R Datasheet regarding your question about DMA_CH2_EUSCIB2TX3. Further material can be found in the DMA section of the User's Guide.

    Regards,
    Ryan
  • sample_msp432_spi_with_dma.c
    /*
     * -------------------------------------------
     *    MSP432 DriverLib - v2_20_00_08
     * -------------------------------------------
     *
     * --COPYRIGHT--,BSD,BSD
     * Copyright (c) 2014, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * --/COPYRIGHT--*/
    /*******************************************************************************
     * MSP432 DMA - eUSCI SPI Transfer Using DMA
     *
     * Description: In this code example, the MSP432 's DMA controller is used in 
     * conjunction with the eUSCI_A0 SPI configuration to demonstrate how to use
     * hardware triggered DMA transfers. One DMA transfer is setup, Channel 0 is
     * for the DMA transfer. Once the transfers are setup and initialized, the
     * device is put to sleep. While sleeping, the DMA controller will automatically
     * transfer the data from the const array and out through the uESCI A0 SPI
     * module as a master. When the DMA transfer is complete, an interrupt will be
     * fired and the master will disable further interrupts
     *
     * This program runs infinitely until manually halted by the user.
     *
     *                MSP432P401
     *             ------------------
     *         /|\|                  |
     *          | |                  |
     *          --|RST    P1.1 (CLK) |
     *            |       P1.3 (MOSI)|
     *            |                  |
     *
     * Author: Ryan Brown
     ******************************************************************************/
    /* DriverLib Includes */
    #include "driverlib.h"
    
    /* Standard Includes */
    #include <stdint.h>
    
    #include <string.h>
    #include <stdbool.h>
    
    /* SPI Master Configuration Parameter */
    const eUSCI_SPI_MasterConfig spiMasterConfig =
    {
            EUSCI_A_SPI_CLOCKSOURCE_SMCLK,             // MCLK Clock Source
    		12000000,                                  // MCLK = 12MHz
            400000,                                    // SPICLK = 400khz
            EUSCI_A_SPI_MSB_FIRST,                     // MSB First
            EUSCI_A_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT,    // Phase
    		EUSCI_SPI_CLOCKPOLARITY_INACTIVITY_LOW,    // Low polarity
            EUSCI_A_SPI_3PIN                           // 3Wire SPI Mode
    };
    
    /* DMA Control Table */
    #ifdef ewarm
    #pragma data_alignment=256
    #else
    #pragma DATA_ALIGN(controlTable, 256)
    #endif
    uint8_t controlTable[256];
    
    /* Extern */
    extern uint8_t data_array[];
    
    int main(void)
    {
        /* Halting Watchdog */
        WDT_A_holdTimer();
    
        CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_12);
    
        /* Port 2.4 as GPIO used for CS active low */
    	GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN4);
        GPIO_setOutputHighOnPin(GPIO_PORT_P2, GPIO_PIN4);
    
        /* Selecting P2.1 P2.2 and P2.3 in SPI mode */
        GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P2,
                GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
    
        /* Configuring SPI in 3wire master mode */
        SPI_initMaster(EUSCI_A1_BASE, &spiMasterConfig);
    
        /* Enable SPI module */
        SPI_enableModule(EUSCI_A1_BASE);
    
        /* Configuring DMA module */
        DMA_enableModule();
        DMA_setControlBase(controlTable);
    
        DMA_disableChannel(2);
        /* Assigning Channel 2 to EUSCIA0TX0 */
        DMA_assignChannel(DMA_CH2_EUSCIA1TX);
    
         /* Disabling channel attributes */
        DMA_disableChannelAttribute(DMA_CH2_EUSCIA1TX,
                                    UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
                                    UDMA_ATTR_HIGH_PRIORITY |
                                    UDMA_ATTR_REQMASK);
    
        /* Setting Control Indexes */
        DMA_setChannelControl(UDMA_PRI_SELECT | DMA_CH2_EUSCIA1TX,
                UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1);
        DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_CH2_EUSCIA1TX,
                UDMA_MODE_BASIC, data_array,
                (void*) MAP_SPI_getTransmitBufferAddressForDMA(EUSCI_A1_BASE), 1024);
    
        /* Assigning/Enabling Interrupts */
        DMA_assignInterrupt(DMA_INT1, 2);
        Interrupt_enableInterrupt(INT_DMA_INT1);
    
        /* Drive CS Low to indicate start of the dtransfer */
        GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN4);
    
        /* Now that the DMA is primed and setup, enabling the channels. The EUSCI
         * hardware should take over and transfer/receive all bytes */
        DMA_enableChannel(2);
    
        /* Polling to see if the TX buffer is ready */
        while (!(SPI_getInterruptStatus(EUSCI_A1_BASE,EUSCI_A_SPI_TRANSMIT_INTERRUPT)));
    
        //PCM_gotoLPM0InterruptSafe();
    
        while(1);
    }
    
    /* Completion interrupt for eUSCIA0 TX */
    void dma_1_interrupt(void)
    {
        /* Disabling the completion interrupt and disabling the DMA channels */
        DMA_disableChannel(2);
        DMA_disableInterrupt(INT_DMA_INT1);
        /* Drive CS High to indicate end of the dtransfer */
        GPIO_setOutputHighOnPin(GPIO_PORT_P2, GPIO_PIN4);
    }

    Hi Ryan,

    I'm attaching the source file.

    Regarding the DMA sources: the table does not explain what is eUSCI_B2 TX3. As far as I read the datasheet the eUSCI has one transmit buffer which is 2 byte long (with the upper byte being reserved). I would assume eUSCI_B2 TX0 maps to the first byte, eUSCI_B2 TX1 to the second, but eUSCI_B2 TX2 and eUSCI_B2 TX3 would be what? Maybe I'm not understanding something correctly.

    ~Venelin

  • Venelin,

    I don't see where you enable the FPU for DCO Frequency calculation, can you confirm that your SMCLK is operating at 12 MHz? Also, have you manually tried resetting the interrupt flag during your initialization? It seems that the flag is already set therefore the ISR is serviced immediately without being set by the SPI, you can make sure it is clear with DMA_clearInterruptFlag(2).

    The TX[x] and RX[x] just refers to the [x] transmit or receive buffer for a given DMA channel, you will see that it increments across the row of Table 6-8. It has nothing to do with the particular eUSCI module configuration.

    Regards,
    Ryan
  • I don't know how to measure the DCO frequency, however the SPI produces clock at even 400kHz, so the clocks should be somewhat operational.

    I did try to clear the interrupt flag before I enable the interrupt, but an interrupt is generated again. I also tried clearing the flag inside the interrupt handler and if I don't call DMA_disableInterrupt(INT_DMA_INT1) another interrupt is generated immediately.

    My speculation is that a disabled DMA channel generates completion interrupts for some reason. If I move the two lines DMA_assignInterrupt(DMA_INT1, 2); and Interrupt_enableInterrupt(INT_DMA_INT1); after DMA_enableChannel(2); the interrupt behaves correctly as far as I can tell.

    However I'm not sure that is a correct code sequence since  the interrupt may be lost, between enabling the channel and enabling the interrupt and indeed every once in a while I experience the interrupt not arriving at all.

  • Venelin,

    You did correct to switch the order of Interrupt_enableInterrupt and DMA_enableChannel per this E2E thread: e2e.ti.com/.../439876

    Tell me more about the lost interrupts, how often do they happen?  Can you provide any screenshots of the issue?

    Regards,
    Ryan

  • Hi Ryan,

    I get "Access denied" if I try to read the thread. Can you post the gist of it here?

    Thanks,
    Venelin
  • Sure thing Venelin, sorry about that.  It basically concluded with this code:  

    1373.dma_eusci_spi_loopback.c
    /*******************************************************************************
     * MSP432 DMA - eUSCI SPI Transfer Using DMA
     *
     * Description: In this code example, the MSP432 's DMA controller is used in
     * conjunction with an SPI loopback configuration to demonstrate how to use
     * hardware triggered DMA transfers. Four DMA transfers are setup using four
     * separate DMA channels. For each SPI instance, a DMA channel is setup to
     * receive and transfer from the SIMO and SOMI ports respectively. After an
     * arbitrary string is sent through SPI via the loopback configuration, an
     * ISR counter is incremented and triggers a NOP condition in the main loop
     * where the user can set a breakpoint to examine memory contents.
     *
     *
     *                      MSP432P401
     *             ---------------------------
     *         /|\|                          |
     *          | |                          |
     *          --|RST           P1.5 (CLK)  |-------------
     *            |              P1.7 (SIMO) |----------  |
     *            |              P1.6 (SOMI) |-------- |  |
     *            |                          |       | |  |
     *            |                          |       | |  |
     *            |              P3.6 (SIMO) |-------- |  |
     *            |              P3.7 (SOMI) |----------  |
     *            |              P3.5 (CLK)  |-------------
     *            |                          |
     *            |                          |
     *
     * Author: Timothy Logan
     ******************************************************************************/
    
    /* DriverLib Includes */
    #include "driverlib.h"
    
    /* Standard Includes */
    #include <stdint.h>
    #include <string.h>
    #include <stdbool.h>
    
    /* SPI Configuration Parameter */
    const eUSCI_SPI_MasterConfig spiMasterConfig =
    { EUSCI_B_SPI_CLOCKSOURCE_SMCLK, 12000000, 1000000,
            EUSCI_B_SPI_MSB_FIRST,
            EUSCI_B_SPI_PHASE_DATA_CAPTURED_ONFIRST_CHANGED_ON_NEXT,
            EUSCI_B_SPI_CLOCKPOLARITY_INACTIVITY_HIGH, EUSCI_B_SPI_3PIN };
    
    const eUSCI_SPI_SlaveConfig spiSlaveConfig =
    { EUSCI_B_SPI_MSB_FIRST,
            EUSCI_B_SPI_PHASE_DATA_CAPTURED_ONFIRST_CHANGED_ON_NEXT,
            EUSCI_B_SPI_CLOCKPOLARITY_INACTIVITY_HIGH,
            EUSCI_B_SPI_3PIN
            };
    
    /* DMA Control Table */
    #if defined(__TI_COMPILER_VERSION__)
    #pragma DATA_ALIGN(MSP_EXP432P401RLP_DMAControlTable, 1024)
    #elif defined(__IAR_SYSTEMS_ICC__)
    #pragma data_alignment=1024
    #elif defined(__GNUC__)
    __attribute__ ((aligned (1024)))
    #elif defined(__CC_ARM)
    __align(1024)
    #endif
    static DMA_ControlTable MSP_EXP432P401RLP_DMAControlTable[32];
    
    #define MAP_SPI_MSG_LENGTH    26
    
    uint32_t isrCounter = 0;
    uint8_t mstxData[26] = "Hello, this is master SPI";
    uint8_t msrxData[26] =
    { 0 };
    uint8_t sltxData[26] = "Hello, this is slave SPI";
    uint8_t slrxData[26] =
    { 0 };
    
    int main(void)
    {
        volatile uint32_t ii;
        
        /* Halting Watchdog */
        MAP_WDT_A_holdTimer();
    
        /* Configure CLK, MOSI & MISO for SPI0 (EUSCI_B0) */
        MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P1,
                GPIO_PIN5 | GPIO_PIN6, GPIO_PRIMARY_MODULE_FUNCTION);
        MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
                GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);
    
        /* Configure SLAVE CLK, MOSI and SPMI (EUSCI_B2) */
        MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P3,
                GPIO_PIN5 | GPIO_PIN6 | GPIO_PIN7,
                GPIO_PRIMARY_MODULE_FUNCTION);
    
        /* Configuring SPI module */
        MAP_SPI_initSlave(EUSCI_B2_MODULE, &spiSlaveConfig);
        MAP_SPI_initMaster(EUSCI_B0_MODULE, &spiMasterConfig);
    
        /* Enable the SPI module */
        MAP_SPI_enableModule(EUSCI_B2_MODULE);
        MAP_SPI_enableModule(EUSCI_B0_MODULE);
    
        /* Configuring DMA module */
        MAP_DMA_enableModule();
        MAP_DMA_setControlBase(MSP_EXP432P401RLP_DMAControlTable);
    
        /* Assign DMA channel 0 to EUSCI_B0_TX0, channel 1 to EUSCI_B0_RX0 */
        MAP_DMA_assignChannel(DMA_CH0_EUSCIB0TX0);
        MAP_DMA_assignChannel(DMA_CH1_EUSCIB0RX0);
        MAP_DMA_assignChannel(DMA_CH4_EUSCIB2TX0);
        MAP_DMA_assignChannel(DMA_CH5_EUSCIB2RX0);
    
        /* Setup the TX transfer characteristics & buffers */
        MAP_DMA_setChannelControl(DMA_CH0_EUSCIB0TX0 | UDMA_PRI_SELECT,
        UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1);
        MAP_DMA_setChannelTransfer(DMA_CH0_EUSCIB0TX0 | UDMA_PRI_SELECT,
        UDMA_MODE_BASIC, mstxData,
                (void *) MAP_SPI_getTransmitBufferAddressForDMA(EUSCI_B0_BASE),
                MAP_SPI_MSG_LENGTH);
    
        /* Setup the RX transfer characteristics & buffers */
        MAP_DMA_setChannelControl(DMA_CH1_EUSCIB0RX0 | UDMA_PRI_SELECT,
        UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1);
        MAP_DMA_setChannelTransfer(DMA_CH1_EUSCIB0RX0 | UDMA_PRI_SELECT,
        UDMA_MODE_BASIC,
                (void *) MAP_SPI_getReceiveBufferAddressForDMA(EUSCI_B0_BASE),
                msrxData,
                MAP_SPI_MSG_LENGTH);
    
        /* Slave Settings */
        MAP_DMA_setChannelControl(DMA_CH4_EUSCIB2TX0 | UDMA_PRI_SELECT,
        UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1);
        MAP_DMA_setChannelTransfer(DMA_CH4_EUSCIB2TX0 | UDMA_PRI_SELECT,
        UDMA_MODE_BASIC, sltxData,
                (void *) MAP_SPI_getTransmitBufferAddressForDMA(EUSCI_B2_BASE),
                MAP_SPI_MSG_LENGTH);
    
        /* Setup the RX transfer characteristics & buffers */
        MAP_DMA_setChannelControl(DMA_CH5_EUSCIB2RX0 | UDMA_PRI_SELECT,
        UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1);
        MAP_DMA_setChannelTransfer(DMA_CH5_EUSCIB2RX0 | UDMA_PRI_SELECT,
        UDMA_MODE_BASIC,
                (void *) MAP_SPI_getReceiveBufferAddressForDMA(EUSCI_B2_BASE),
                slrxData,
                MAP_SPI_MSG_LENGTH);
    
        /* Enable DMA interrupt */
        MAP_DMA_assignInterrupt(INT_DMA_INT1, 1);
        MAP_DMA_clearInterruptFlag(DMA_CH1_EUSCIB0RX0 & 0x0F);
    
        /* Assigning/Enabling Interrupts */
        MAP_Interrupt_enableInterrupt(INT_DMA_INT1);
        MAP_DMA_enableInterrupt(INT_DMA_INT1);
        MAP_DMA_enableChannel(5);
        MAP_DMA_enableChannel(4);
    
        /* Delaying for forty cycles to let the master catch up with the slave */
        for(ii=0;ii<50;ii++);
    
        MAP_DMA_enableChannel(1);
        MAP_DMA_enableChannel(0);
    
        /* Polling to see if the master receive is finished */
        while (1)
        {
            if (isrCounter > 0)
            {
                __no_operation();
            }
        }
    
    }
    
    void DMA_1_interrupt(void)
    {
        isrCounter++;
        MAP_DMA_clearInterruptFlag(0);
        MAP_DMA_clearInterruptFlag(1);
    
        /* Disable the interrupt to allow execution */
        MAP_Interrupt_disableInterrupt(INT_DMA_INT1);
        MAP_DMA_disableInterrupt(INT_DMA_INT1);
    }
    

    Along with the suggestion that MAP_Interrupt_enableInterrupt should come after MAP_DMA_enableChannel to ensure that there is data to be sent and the completion interrupt will only happen after all of the appropriate data has been sent.

    Regards, Ryan

  • "The TX[x] and RX[x] just refers to the [x] transmit or receive buffer for a given DMA channel, you will see that it increments across the row of Table 6-8. It has nothing to do with the particular eUSCI module configuration."


    Hi Ryan, can you elaborate please  ie.  If I have channel 0 programmed with srccfg 5 (ie. eUSCI_B1 TX3), what event causes channel 0 dma to trigger?

    If the TX3 denotes TX[3] what transmit buffer does this refer to?

    thanks

  • Hi Eric,

    The UCTXIFG bit from the UCB1IFG register. Granted Table 6-36. DMA Sources in Section 6.5.1 DMA Source Mapping of the datasheet (Rev E) is confusing by using the TX[x] and RX[x] notations where TX and RX would suffice since each is differentiable by eUSCI peripheral notation. It is meant to explain that SRCCFG = 5 is the fifth eUSCI TX DMA source available on channel 0, once again confusing since the notation is not used at first and then starts at 0 with SRCCFG = 2.

    Regards,
    Ryan
  • To Venelin

    For Ryan's example code, maybe we are missing the declaration of the interrupt in the interrupt vector table.  Check out the screenshot attached below.