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.

Using SPI, DMA and DMAE0 on MSP432

Other Parts Discussed in Thread: CC1200

I found this thread.

In this thread it says that i can't use DMA with SPI with DMAE0 and send more than 1 byte. If I understood correctly, if I put the source DMA_CH0_EUSCIB0TX0 then the SPI generates the DMA request when the TX_SPI is ready. On the other hand, if I but the source to be DMAE0 then the DMA doesn't know if the SPI is ready and just copies the memory from one address to another without check the SPI ready signal.

I found on the internet that many systems have this feature that can use and combine both, so I first wanted to check here.

Thanks in advanced.
jasaleja

  • Hello Jasaleja,

    I think the issue you are highlighting is that only one trigger can be associated with a DMA channel, so you cannot trigger a channel from the DMAE0 and then from the SPI without a reconfiguration of the channel.

    This example uses a software event to start the scatter gather method and is based upon the SPI trigger source: e2e.ti.com/.../529109.

    Regards,
    Chris
  • Hi Chris,

    I am trying to make DMA help me with the CC1200 RF chip in infinite mode. I'll simplify the problem. The transfer or receive buffer in CC1200 is only 128 k and to make my packets longer I need to use infinite mode which demands that I refill the transfer buffer in real time or to read in realm time from the receive buffer while receiving. There are some GPIOs that tell me when the buffers are ready to be written to or read from and I wanted to use those GPIOs to singal the DMA when to transfer to the SPI. This would help me because in the other case I would have to wake up the MCU and start DMA manually(which is going to be the case). I hope the explanation was clear.

    On the other hand I looked into your example(thanks for that) but there were a few things I could not figure out.

    1) In you code you made a list with tasks. In the Techincal reference manual it says that the DMA jumps from primary to alternate data structure. Does that mean that every other data structure in the task list automatically alternate?

    2) Nowhere, really nowhere could I find what UDMA_MODE_ALT_SELECT was and why do you combine it with other UDMA modes?

    3) No matter how much i tried to look I could not understand what was the difference between MEM and PER SCATTER_GATHER?

    4) The last thing is that I wanted to try and write here a simplified task list what my DMA needs to do. Could you write the real task list how it should look like?

    1. Task: Send command to CC1200 that tell it I want to read receive buffer. Source would be commandBuffer and destination would be EUSCI_B0_TX. Command is 1 byte.
    2. Task: Send 32 dummy bytes so that the CC1200 returns 32 bytes from receive buffer. Sourcew is dummyByte, and destination is EUSCI_B0_TX. 

    At the same time I want the DMA to transfer from EUSCI_B0_RX to my receiveBuffer during the second task. 

    I have no idea how to create this. Please give me some guidelines and additional examples.



  • Jasaleia,
    I think you have addressed this accurately. You will need to interrupt the CPU from a GPIO event and start the DMA transfer. The example I provided was an attempt to show how to interleave GPIO activity but in hind-site really is not applicable to your application. The example is intended for transmission (chip select) and does not apply to receiving/triggering the DMA.

    Regarding your other questions…

    (1) In the scatter-gather mode the DMA primary is used to load the task into the DMA alternate and then the alternate DMA is executed. The tasks will always reside in the alternate and the primary is just used to move the task from memory into the alternate’s memory location.
    (2) If you look at the figures 9-6 (www.ti.com/.../slau356d.pdf and 9-7 (www.ti.com/.../slau356d.pdf, in the users guide and the uDMA TRM from ARM, you will find that the LSB is set in the cycle_ctr portion of the channel_cfg. The definition of UDMA_MODE_ALT_SELECT addresses this. You can also see this in Table 9-13 where it distinguishes between the alternate and primary structures.
    (3) MEM mode is intended to be an ‘automatic’ trigger. In other words, there is an initial trigger defined by the channel and all subsequent transfers take place as soon as the previous is finished. In the PERipheral mode, each DMA does not take place without a trigger. This is also shown in figures 9-6 and 9-7 and the difference is ‘auto-request’ verse just a request.
    (4) Generating code examples will take some time and I cannot commit to a specific time frame. The example that I provided is a good place to start. There is also another thread where two separate DMA channels are used for Rx and Tx which might be helpful. e2e.ti.com/.../2027097

    Regards,
    Chris
  • Thank you Chris for a very detailed answer. 

    One more question that came up is, can I setup different request for different tasks?
    After looking at the examples it doesn't seem it can be set, but it doesn't hurt to ask.

    For example the first task uses DMAE0 and the second and all after use EUSCIB0_TX.

    Best wishes,
    jasaleja

  • Jasaleja,
    I am not sure I understand, but I will try. Only one trigger can be associated with a channel. Now you can associate DMAE0 to channel 6 and eUSCIB0_TX to channel 0. Within Channel 6 and Channel 0 you can setup separate DMA tasks (ping-pong, basic, auto, scatter-gather). There is no dependency or relationship between channel 6 and 0 other than you can set the arbitration so that one DMA task will complete before starting another.
    In your example I would think that you would trigger DMA channel 6 from the DMAE0. The DMA on channel 6 performs some number of tasks and the last task is to load the first TX byte (this assumes that the TXIFG was cleared earlier). Once this byte is loaded the eUSCI triggers the eUSCIB0_TX on channel 0 and all successive triggers to load info into the TX buffer would be from the eUSCIB0_TX. Another option is to hold the eUSCI in reset and then have the last task for channel 6 be to write to the eUSCI control register to clear the reset bit and the TXIFG (and trigger to the DMA) would be automatic.

    Best Regards,
    Chris
  • I tried doing the first thing that you suggested but mu DMA_INT1 keeps triggering even though I didn't use the DMA yet. I don't understand what could be the source of the problem.

    //*****************************************************************************
    //
    // MSP432 main.c template - Empty main
    //
    //****************************************************************************
    
    #include "driverlib.h"
    #include "IQmathLib.h"
    
    #pragma DATA_ALIGN(MSP_EXP432P401RLP_DMAControlTable, 1024)
    static DMA_ControlTable MSP_EXP432P401RLP_DMAControlTable[32];
    
    void InitMCU();
    
    void main(void)
    {
    	InitMCU();
    
    	uint8_t buffer = 0x55;
    
    	// Clear interrupt status
    	SPI_clearInterruptFlag(EUSCI_B0_BASE, EUSCI_B_SPI_TRANSMIT_INTERRUPT);
    
    	// Activate DMA SPI transfer with external pin
    	DMA_setChannelControl(DMA_CH6_EXTERNALPIN | UDMA_PRI_SELECT,
    	UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_1);
    	DMA_setChannelTransfer(DMA_CH6_EXTERNALPIN | UDMA_PRI_SELECT,
    			UDMA_MODE_BASIC,
    			&buffer,
    			(void *) SPI_getTransmitBufferAddressForDMA(EUSCI_B0_BASE),
    			1);
    
    	// Setup the TX transfer characteristics & buffers
    	DMA_setChannelControl(DMA_CH0_EUSCIB0TX0 | UDMA_PRI_SELECT,
    	UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1);
    	DMA_setChannelTransfer(DMA_CH0_EUSCIB0TX0 | UDMA_PRI_SELECT,
    			UDMA_MODE_BASIC,
    			&buffer,
    			(void *) SPI_getTransmitBufferAddressForDMA(EUSCI_B0_BASE),
    			1);
    
    
    	DMA_clearInterruptFlag(0);
    
    	MAP_DMA_enableChannel(0);
    	MAP_DMA_enableChannel(6);
    
    	GPIO_setOutputHighOnPin(GPIO_PORT_P2, GPIO_PIN6);
    
    	while(1);
    }
    
    // DMA INT1 ISR
    void DMA_INT1_IRQHandler(void)
    {
    	return;
    }
    
    // SPI Master Configuration Parameter for CC1200
    const eUSCI_SPI_MasterConfig spiMasterConfig =
    {
    		EUSCI_B_SPI_CLOCKSOURCE_SMCLK,				// SMCLK Clock Source
            6000000,									// SMCLK = HFXT/8 = 6MHz
    		6000000,									// SPICLK = 6MHz
            EUSCI_B_SPI_MSB_FIRST,						// MSB First
            EUSCI_B_SPI_PHASE_DATA_CAPTURED_ONFIRST_CHANGED_ON_NEXT,    // Phase
            EUSCI_B_SPI_CLOCKPOLARITY_INACTIVITY_LOW,	// Low polarity
            EUSCI_B_SPI_3PIN							// 3Wire SPI Mode
    };
    
    // Initialize the MCU
    void InitMCU()
    {
    	// Halting the Watchdog
    	WDT_A_holdTimer();
    
    	// Enabling MASTER interrupts
    	Interrupt_disableMaster();
    
    	// Set power state of MCU
    	PCM_setPowerState(PCM_AM_LDO_VCORE1);
    
    	// Configuring pins for peripheral/crystal usage and LED for output
    	GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_PJ,
    			GPIO_PIN3 | GPIO_PIN2, GPIO_PRIMARY_MODULE_FUNCTION);
    
    	// Just in case the user wants to use the getACLK, getMCLK, etc. functions,
    	// let's set the clock frequency in the code
    	CS_setExternalClockSourceFrequency(32000,48000000);
    
    	// Starting HFXT in non-bypass mode without a timeout. Before we start
    	// we have to change VCORE to 1 to support the 48MHz frequency
    	//PCM_setCoreVoltageLevel(PCM_VCORE1);
    	FlashCtl_setWaitState(FLASH_BANK0, 2);
    	FlashCtl_setWaitState(FLASH_BANK1, 2);
    	CS_startHFXT(false);
    
    	// Initializing MCLK to HFXT (effectively 48MHz)
    	CS_initClockSignal(CS_MCLK, CS_HFXTCLK_SELECT, CS_CLOCK_DIVIDER_1);
    
    	// Initializing SMCLK to HFXT (effectively 6MHz)
    	CS_initClockSignal(CS_SMCLK, CS_HFXTCLK_SELECT, CS_CLOCK_DIVIDER_8);
    
    	// Selecting pins P1.5, P1.6 and P1.7 in SPI mode
    	GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
    			GPIO_PIN7 | GPIO_PIN6 | GPIO_PIN5, GPIO_PRIMARY_MODULE_FUNCTION);
    
    	GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN6);
    	GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN6);
    
    	// Selecting pin 7.0 in DMAE0
    	GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P7, GPIO_PIN0, GPIO_PRIMARY_MODULE_FUNCTION);
    
    	// Configuring SPI in 3wire master mode for CC1200
    	SPI_initMaster(EUSCI_B0_BASE, &spiMasterConfig);
    
    	// Enable SPI module for CC1200
    	SPI_enableModule(EUSCI_B0_BASE);
    
    	// Configuring DMA module
    	DMA_enableModule();
    	DMA_setControlBase(MSP_EXP432P401RLP_DMAControlTable);
    
    	// Assign DMA channel 0 to EUSCI_B0_TX0, channel 1 to EUSCI_B0_RX0
    	DMA_assignChannel(DMA_CH0_EUSCIB0TX0);
    	DMA_assignChannel(DMA_CH1_EUSCIB0RX0);
    	DMA_assignChannel(DMA_CH6_EXTERNALPIN);
    
        // Enable DMA interrupt
        MAP_DMA_assignInterrupt(INT_DMA_INT1, 0);
        MAP_DMA_clearInterruptFlag(0);
    
        /* Assigning/Enabling Interrupts */
        MAP_Interrupt_enableInterrupt(INT_DMA_INT1);
        MAP_DMA_enableInterrupt(INT_DMA_INT1);
    
        // Enabling MASTER interrupts
    	Interrupt_enableMaster();
    }

  • The TIFG is set automatically when the eUSCI module is released from reset (www.ti.com/.../slau356d.pdf ). The SPI_enableModule API clears the reset in the eUSCI peripheral, so either before or after but definitely before you configure the DMA you can clear the TXIFG, SPI_clearInterruptFlag(), There is another issue with the order of operation where you are assigning channels, enabling interrupts, and then configuring the DMA.

    Chris

  • I knew that but for some reason I thought the the interrupt won't occur until the DMA did a transfer.
    I am sorry for asking so many questions, but it is my first time making an embedded application on my own.

    So I should keep interrupt disabled until I setup the channel controls and channel transfers and I should(as I did) clear the transmit interrupt in the SPI.
    I did these things in this example and It worked. But it won't work when I put it in the whole project, but that is probably some other problem.

    Thanks for all the help.

  • You are welcome. I am going to include your other post here, e2e.ti.com/.../556453 , for continuity.

    Regards,
    Chris

**Attention** This is a public forum