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.

TM4C129XNCZAD: SSI1 cannot get AD sample data with DMA

Part Number: TM4C129XNCZAD
Other Parts Discussed in Thread: ADS131A04

Hi,

I'm debuging a program all these days.It did not work yet.

The cpu is TM4C129XNCZAD, the ADC is ADS131A04(24-bit per channel, 32KBPS), they linked with SSI1 signal pins, CLK, DI and DOUT are generally initialized, except CS pin configed as a GPIO Input pin,linked to RDY of ADS131A04. The program is as below:

GPIOPinConfigure(GPIO_PB5_SSI1CLK); // Clk
//GPIOPinConfigure(GPIO_PB4_SSI1FSS); // CS
GPIOPinConfigure(GPIO_PE5_SSI1XDAT1); // Rx
GPIOPinConfigure(GPIO_PE4_SSI1XDAT0); // Tx

GPIOPinTypeSSI(GPIO_PORTB_BASE, GPIO_PIN_5/* | GPIO_PIN_4*/);
GPIOPinTypeSSI(GPIO_PORTE_BASE, GPIO_PIN_4 | GPIO_PIN_5);

// SSI1 CS
GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_4);
GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, GPIO_PIN_4);

The initilization of SSI1 is:

//
// The SSI1 peripheral must be enabled for use.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);
//
// Sets the data clock source of SSI peripheral to SysClk
//
SSIClockSourceSet(SSI1_BASE, SSI_CLOCK_SYSTEM);
//
// Configure and enable the SSI port for SPI master mode. 
//
SSIConfigSetExpClk(SSI1_BASE, /*16384000*/g_ui32SysClock, SSI_FRF_MOTO_MODE_1,
SSI_MODE_MASTER, 15360000/*15360000*/, 8);

//
// Enable the SSI1 module.
//
SSIEnable(SSI1_BASE);

Then, I initilize the ADS131A04 by sending coresponding CMDs to ADS131A04  like this:

ads131A04SendCmd(ADS131A04_UNCLOCK_COMMAND);

ads131A04SendCmd(WRITE_REGISTER_COMMAND(CLK1, CLK_DIV_2));

ads131A04SendCmd(WRITE_REGISTER_COMMAND(ADC_ENA, ADC_ENA_ENABLE_ALL_CHANNELS));

Before WAKEUP ADS131A04, i setup the uDMA parameters as follows:

// Assignment channel to SSI1
//
uDMAChannelAssign(UDMA_CHANNEL_SSI1RX);
uDMAChannelAssign(UDMA_CHANNEL_SSI1TX);
//
// Setup uDMA TX
//
//
// Put the attributes in a known state for the uDMA SSI1 TX channel. These
// should already be disabled by default.
//
uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI1TX,
UDMA_ATTR_USEBURST |
UDMA_ATTR_ALTSELECT |
//UDMA_ATTR_HIGH_PRIORITY |
UDMA_ATTR_REQMASK);
//
// Set the USEBURST attribute for the uDMA SSI1 TX channel. This will
// force the controller to always use a burst when transferring data from
// the TX buffer to the UART. This is somewhat more effecient bus usage
// than the default which allows single or burst transfers.
//
uDMAChannelAttributeEnable(UDMA_CHANNEL_SSI1TX, /*UDMA_ATTR_USEBURST | */UDMA_ATTR_HIGH_PRIORITY);
//
// Configure the control parameters for the SSI1 TX. The uDMA SSI1 TX
// channel is used to transfer a block of data from a buffer to the SSI1.
// The data size is 8 bits. The source address increment is 8-bit bytes
// since the data is coming from a buffer. The destination increment is
// none since the data is to be written to the SSI1 data register. The
// arbitration size is set to 1, which matches the SSI1 TX FIFO trigger
// threshold.
//
uDMAChannelControlSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
UDMA_SIZE_8 |
UDMA_SRC_INC_8 |
UDMA_DST_INC_NONE |
UDMA_ARB_1);

//
// Setup uDMA RX
//
//
// Put the attributes in a known state for the uDMA SSI1 RX channel. These
// should already be disabled by default.
//
uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI1RX,
UDMA_ATTR_USEBURST |
UDMA_ATTR_ALTSELECT |
//UDMA_ATTR_HIGH_PRIORITY |
UDMA_ATTR_REQMASK);
//
// Set the USEBURST attribute for the uDMA SSI1 RX channel. This will
// force the controller to always use a burst when transferring data from
// the TX buffer to the SSI1. This is somewhat more effecient bus usage
// than the default which allows single or burst transfers.
//
uDMAChannelAttributeEnable(UDMA_CHANNEL_SSI1RX, /*UDMA_ATTR_USEBURST | */UDMA_ATTR_HIGH_PRIORITY);
//
// Configure the control parameters for the SSI1 RX. The uDMA RX
// channel is used to transfer a block of data from the SSI1 to the buffer.
// The data size is 8 bits. The source address increment is none
// since the data is coming from SSI1. The destination increment is
// INC8 since the data is from the SSI1 data register. The
// arbitration size is set to 1, which matches the UART TX FIFO trigger
// threshold.
//
uDMAChannelControlSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
UDMA_SIZE_8 | UDMA_SRC_INC_NONE |
UDMA_DST_INC_8 |
UDMA_ARB_1);

In the IntHander routinue of ADS131A04 RDY :

// Clear Int Flag
GPIOIntClear(PORT_ADS131A04_DRDY, GPIOINTFLAG_ADS131A04_DRDY);

// Enable INT
GPIOIntEnable(PORT_ADS131A04_DRDY, GPIOINTFLAG_ADS131A04_DRDY);

// Set up the transfer parameters for the uDMA SSI1 RX channel. This will
// configure the transfer source and destination and the transfer size.
// Basic mode is used because the peripheral is making the uDMA transfer
// request. The source is the SSI1 data registerthe and the destination
// is RX buffer.
//
uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
UDMA_MODE_AUTO, gui8SSI1RxDMABufA,
(void *)(SSI1_BASE + SSI_O_DR),
15/*sizeof(gui8SSI1TxDMABuf)*/);
//
// Set up the transfer parameters for the uDMA SSI1 TX channel. This will
// configure the transfer source and destination and the transfer size.
// Basic mode is used because the peripheral is making the uDMA transfer
// request. The source is the TX buffer and the destination is the SSI1
// data register.
//
uDMAChannelTransferSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
UDMA_MODE_AUTO, gui8SSI1TxDMABuf,
(void *)(SSI1_BASE + SSI_O_DR),
15/*sizeof(gui8SSI1TxDMABuf)*/);
//
// The uDMA TX channel must be re-enabled.
//
uDMAChannelEnable(UDMA_CHANNEL_SSI1TX);
uDMAChannelEnable(UDMA_CHANNEL_SSI1RX);
//
// Enable the SSI1 DMA TX/RX interrupts.
//
SSIDMAEnable(SSI1_BASE, SSI_DMA_RX | SSI_DMA_TX);
SSIIntEnable(SSI1_BASE, SSI_DMARX | SSI_DMATX);
//
// Requests a uDMA channel to start a transfer.
//
uDMAChannelRequest(UDMA_CHANNEL_SSI1TX);
uDMAChannelRequest(UDMA_CHANNEL_SSI1RX);
//
// Assert CS signal of ADS131A04 
//
ADS131A04_CS_ENABLE;

But, the SSI1 & uDMA do not work correctly. The fact is: the number of ADS131A04 RDY IntHandler and SSI1 IntHandler entered is right(32K), but the AD sample data were all 0! On the oscilloscope, the frequrency of CLK is less than 156Kb, this is abviously wrong. The right CLK frequency might be 32 x 24 x 5=3840Kb.

So, i want to know the problem of my program. What is the difference of SSI1 CLK between Normal and DMA mode? Where is the mistakes(sentence or process) of my program?

Thanks a lot!

  • Hi Frank,

     To isolate the problem, have you tried to first run your program without the uDMA? It is easier to debug one thing at a time. Once the non uDMA SSI operation is working you can then add the uDMA. 

    Frank CAI said:
    oscilloscope, the frequrency of CLK is less than 156Kb, this is abviously wrong. The right CLK frequency might be 32 x 24 x 5=3840Kb.

    Can you explain what is the 5 in your calculation?

    You have the below SSI Clock configuration. Your system clock is most likely 120MHz. You can't divide down to exactly 15360000 as the divider is only an integer divider. What SSI clock frequency did you see in the scope? 

    SSIConfigSetExpClk(SSI1_BASE, /*16384000*/g_ui32SysClock, SSI_FRF_MOTO_MODE_1,
    SSI_MODE_MASTER, 15360000/*15360000*/, 8);

  • Hi Charles,
    Thank you for replying me so quickly.

    First, I run my program well without uDMA, the Whole codes for ADS131A04 Ready IntHandler is here:
    void IntADS131A04ReadyHandler(void)
    {
    // Clear the Interrupt flag
    GPIOIntClear(PORT_ADS131A04_DRDY, GPIOINTFLAG_ADS131A04_DRDY);

    // Enable INT
    GPIOIntEnable(PORT_ADS131A04_DRDY, GPIOINTFLAG_ADS131A04_DRDY);

    if (u32DMAReadyCnt++ > 32000) {
    u32DMAReadyCnt = 0;
    UARTprintf("DMA Rdy...\n");
    }
    #if CONDASM_ADS131A04_DMA
    // Set up the transfer parameters for the uDMA SSI1 RX channel. This will
    // configure the transfer source and destination and the transfer size.
    // Basic mode is used because the peripheral is making the uDMA transfer
    // request. The source is the SSI1 data registerthe and the destination
    // is RX buffer.
    //
    uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
    UDMA_MODE_AUTO, gui8SSI1RxDMABufA,
    (void *)(SSI1_BASE + SSI_O_DR),
    15/*sizeof(gui8SSI1TxDMABuf)*/);
    //
    // Set up the transfer parameters for the uDMA SSI1 TX channel. This will
    // configure the transfer source and destination and the transfer size.
    // Basic mode is used because the peripheral is making the uDMA transfer
    // request. The source is the TX buffer and the destination is the SSI1
    // data register.
    //
    uDMAChannelTransferSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
    UDMA_MODE_AUTO, gui8SSI1TxDMABuf,
    (void *)(SSI1_BASE + SSI_O_DR),
    15/*sizeof(gui8SSI1TxDMABuf)*/);
    //
    // The uDMA TX channel must be re-enabled.
    //
    uDMAChannelEnable(UDMA_CHANNEL_SSI1TX);
    uDMAChannelEnable(UDMA_CHANNEL_SSI1RX);
    //
    // Enable the SSI1 DMA TX/RX interrupts.
    //
    SSIDMAEnable(SSI1_BASE, SSI_DMA_RX | SSI_DMA_TX);
    SSIIntEnable(SSI1_BASE, SSI_DMARX | SSI_DMATX);
    //
    // Requests a uDMA channel to start a transfer.
    //
    uDMAChannelRequest(UDMA_CHANNEL_SSI1TX);
    uDMAChannelRequest(UDMA_CHANNEL_SSI1RX);
    //
    // Assert CS signal for ADS131A04
    //
    ADS131A04_CS_ENABLE;
    #else
    uint16_t receive = 0;
    uint8_t u8TempByte, u8Loop;

    //
    // Assert CS signal for ADS131A04
    //
    ADS131A04_CS_ENABLE;

    for (u8Loop = 0; u8Loop < 15; u8Loop++) {
    gui8SSI1RxDMABufA[u8Loop] = ads131A04SPIOut(0);
    //SysCtlDelay(20);
    }
    // Dessert CS signal
    ADS131A04_CS_DISABLE;
    #endif
    }
    These codes work well, the sample data are right, but nearly the CPU whole time is occupied by this loop, so i want to change the transfer mode to uDMA.

    Second, i explain "The right CLK frequency might be 32 x 24 x 5=3840Kb":
    Yes, my SysClk is 120MHz.
    When ADS131A04 is WAKEUP, it sends out its whole data in one frame, including 1 Status, 4 Channel Data and 1 CRC. Because CRC is
    disabled in ADS131A04, so i must read out 5 Device Words, 24 bits per Device Word. The sample rate is 32KBPS, so the CLK must
    be greater than 3840Kb. So i set the bit rate of SSI1 to 15360K(=3840K * 4), so to let CPU have some time to do other works.

    regards,

    Frank.
  • Hi,  

      You wrote the below snippet of code for the RX channel. 

    uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
    UDMA_MODE_AUTO, gui8SSI1RxDMABufA,
    (void *)(SSI1_BASE + SSI_O_DR),
    15/*sizeof(gui8SSI1TxDMABuf)*/);

    Please look at the function prototype of this API below. The third parameter is supposed to be the source address pointer. If you are reading from the RX channel then you should be reading from (void *)(SSI1_BASE + SSI_O_DR). I think you got the source and destination swapped.

    void uDMAChannelTransferSet (uint32_t ui32ChannelStructIndex, uint32_t ui32Mode, void
    *pvSrcAddr, void pvDstAddr, uint32_t ui32TransferSize)

  • Ohohoh, it's very carelessly,thank you very munch.

  • May we ask, 'Did Vendor's Charles (neat) FIX - succeed?     (your post did not (completely) - convey that fact.)

    And - if it did - might you 'share' the 'measured improvement' - resulting from your (proper) use of the MCU's µDMA.

    Should you 'make that effort' - and report here - (others) may note 'Means to FURTHER IMPROVE' your code - which benefits, 'ALL HERE!'   (especially you!)

  • Hi, Charles,

    Thank you very much. According to your note, i swap the parameters in function uMDAChannelTransferSet, and then the program works well.

    Sorry for anwering you so late.

    regards

    Frank