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.

RTOS/MSP432P401R: ADCBufMSP432 doesn't work with multiple channels and DMA

Part Number: MSP432P401R

Tool/software: TI-RTOS

Hi,

I have previously tried to use ADC14 + DMA to sample two channels and have had no luck. Searching online lead me to the conclusion that it doesn't work, which is unfortuante but something I just have to live with. However, I was using SDK 1.40.xx when I looked into this, and since then ADCBufMSP432 has been updated to support DMA and multiple channels, but it doesn't work. It works fine for a single channel, but fails to sample any data on all but the first channel, which I'll describe below. I also cannot choose DMA when opening a specific instance of ADCBufMSP432 because useDMA is a HWAttr which applies for all invocations. This is rather annoying because it means I can't use DMA for a single channel session, and then not use it for a different sampling session later that requires multiple channels. Anyway, onto the bug..

I'm using simplelink_msp432p4_sdk_2_10_00_14, and in configDMA DMA is always configured to not increment the source:

pingpongTransfer->dmaChannel = DMA_CH7_ADC14;
pingpongTransfer->ctlOptions = UDMA_SIZE_16 | UDMA_SRC_INC_NONE |
            UDMA_DST_INC_16 | UDMA_ARB_1;
pingpongTransfer->dmaTransferSource = (void *) &ADC14->MEM[0];

So immediately, we cannot get any data for subsequent channels because they have to be configured to sample into ADC14MEM1 - ADC14MEM31. And because of the way the ADC/DMA works on the MSP432, there's nothing we can do to make it work. We could configure it to sample into MEM0-MEM31, and then use DMA to copy it out, but that doesn't really achieve much because the processor has to wake up every 32 samples anyway.

So basically, what I'm saying is: how is this code ever meant to work? Has it ever been tested? This makes the ADCBufMSP432 driver basically unusable for me because I need to use DMA to sample a single channel at a high frequency, and then later on (after closing the single channel session), I need to sample multiple channels. This works fine without DMA, but not with DMA, and there's no way I can tell it not to use DMA because it's a HWAttr. I'm going to have to move the DMA HWAttrs into RAM and then change it before calling ADCBuf_open every time, which is a bit of a hack but will probably work around the issue.

Thanks

Campbell

  • Hello Campbell,

    I have asked my colleague who has more knowledge on this topic to review your post.

    Thanks,
    Sai
  • Campbell,
    Your assessment is correct. The uDMA peripheral, using either the ping-pong or basic modes would require an interrupt after every set of channels measured. The only way I know to collect a continuous set of samples from multiple channels with the DMA is with the scatter gather implementation.
    This is currently not architected into the ADCBuf driver. An example written with driverLib API calls can be found here:
    dev.ti.com/.../

    I will ask that a more explicit comment or guidance is given to highlight.

    Regards,
    Chris
  • Also,
    Can you not define multiple HW attributes?

    board.h
    #define Board_ADCBUF0 MSP_EXP432P401R_ADCBUF0
    #define Board_ADCBUF1 MSP_EXP432P401R_ADCBUF1

    MSP_EXP432P401R.h

    /*!
    * @def MSP_EXP432P401R_ADCBufName
    * @brief Enum of ADC hardware peripherals on the MSP_EXP432P401R dev board
    */
    typedef enum MSP_EXP432P401R_ADCBufName {
    MSP_EXP432P401R_ADCBUF0 = 0,
    MSP_EXP432P401R_ADCBUF0 = 1,

    MSP_EXP432P401R_ADCBUFCOUNT
    } MSP_EXP432P401R_ADCBufName;

    .c

    /* ADC configuration structure */
    const ADCBufMSP432_HWAttrs adcbufMSP432HWAttrs[MSP_EXP432P401R_ADCBUFCOUNT] = {
    {
    .intPriority = ~0,
    .channelSetting = adcBuf0MSP432Channels,
    .adcTimerTriggerSource = ADCBufMSP432_TIMERA1_CAPTURECOMPARE2,
    .useDMA = NULL,
    .dmaIntNum = NULL,
    .adcTriggerSource = ADCBufMSP432_TIMER_TRIGGER,
    .timerDutyCycle = 30,
    .clockSource = ADCBufMSP432_ADC_CLOCK
    },
    {
    .intPriority = ~0,
    .channelSetting = adcBuf0MSP432Channels,
    .adcTimerTriggerSource = ADCBufMSP432_TIMERA1_CAPTURECOMPARE2,
    .useDMA = 1,
    .dmaIntNum = DMA_INT0,
    .adcTriggerSource = ADCBufMSP432_TIMER_TRIGGER,
    .timerDutyCycle = 50,
    .clockSource = ADCBufMSP432_ADC_CLOCK
    }
    };

    in thread:

    adcBuf = ADCBuf_open(Board_ADCBUF0, &adcBufParams0);

    ... close

    adcBuf = ADCBuf_open(Board_ADCBUF1, &adcBufParams1);

    Regards,
    Chris
  • Hi Chris,

    The main issue I have with using multiple HWAttrs is that both could be opened at the same time, and ADCMSP432Buf_open changes the state of the ADC when it calls initHw. So I could have a conversion running which would then break when another instance of the ADC is opened, so there's a dependency between the two anyhow. I see in the code that there is a globalMutex for protecting a conversion (if it's blocking), but as far as I can tell it'd still break if another instance is opened, or if a non-blocking convert is used. However, it is cleaner than modifying HWAttrs to enable/disable DMA on the fly, so I'll probably go with that method and add another layer in my code to ensure only one is used at a time.

    Do you think it's possible to have a future update improve DMA/non-DMA usage of the driver?

    Thanks

    Campbell

  • Campbell,
    Yes. I can file a feature request (bug) against the current driver usage for the DMA/non-DMA applications.

    Best Regards,
    Chris

**Attention** This is a public forum