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.

MSPM0G3507: ADC Ping Pong Example gives MEMRESx overflow out of the box

Part Number: MSPM0G3507

Tool/software:

Hi,
I have a Launchpad 3507. When I load the adc ping pong example and add a check for an MEMRESx Overflow, it'll be set directly.

Any ideas why that happens?

This is the modified example, added the global flag and removed the breakpoint (I know that a breakpoint will result in an overflow). Run it and have gOverflow added to the watch

/*
 * Copyright (c) 2021, 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.
 */

#include "ti_msp_dl_config.h"

#define ADC_SAMPLE_SIZE (64)

/* When FIFO is enabled 2 samples are compacted in a single word */
#define ADC_FIFO_SAMPLES (ADC_SAMPLE_SIZE / 2)

uint16_t gADCSamplesPing[ADC_SAMPLE_SIZE];
uint16_t gADCSamplesPong[ADC_SAMPLE_SIZE];

volatile bool gCheckADC;
volatile bool gPing;
volatile bool gOverflow;
volatile bool gUnderflow;


int main(void) {
  gOverflow = false;
  gUnderflow = false;
  gPing = true;

  SYSCFG_DL_init();

  /* Configure DMA source, destination and size */
  DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)DL_ADC12_getFIFOAddress(ADC12_0_INST));
  DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&gADCSamplesPing[0]);
  DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_SAMPLES);
  DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);

  /* Setup interrupts on device */
  NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);

  gCheckADC = false;

  /* The ADC is configured for Repeat Single Conversion,
   * so the ADC will continue until DL_ADC12_disableConversions() is called */
  DL_ADC12_startConversion(ADC12_0_INST);

  while (1) {

    while (false == gCheckADC) {
      __WFE();
    }
    /* Breakpoint to check the buffers and watch the Ping Pong operation.
     * The data will be alternating between each buffer.
     */
    //__BKPT(0);

    /* Switch to send ADC Results to Pong Buffer */
    if (gPing) {

      DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&gADCSamplesPong[0]);
      DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_SAMPLES);
      DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
      DL_ADC12_enableDMA(ADC12_0_INST);

      gPing = false;
    }
    /* Switch to send ADC Results to Ping Buffer */
    else {

      DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&gADCSamplesPing[0]);
      DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_SAMPLES);
      DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
      DL_ADC12_enableDMA(ADC12_0_INST);

      gPing = true;
    }
  }

  gCheckADC = false;
}

void ADC12_0_INST_IRQHandler(void) {
  gOverflow = gOverflow || DL_ADC12_getRawInterruptStatus(ADC12_0_INST,DL_ADC12_INTERRUPT_OVERFLOW);
  gUnderflow = gUnderflow || DL_ADC12_getRawInterruptStatus(ADC12_0_INST,DL_ADC12_INTERRUPT_UNDERFLOW);
  switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {
  case DL_ADC12_IIDX_DMA_DONE:
    gCheckADC = true;
    break;
  default:
    break;
  }
}

  • Hi Kai,

    Can you change the DMA trigger to an earlier value? Try MEMRES6 is loaded, instead of MEMRES11. Since the ADC is in repeat mode, it will keep sampling forever and with FIFOs it will use all the MEMRES registers and write to the next data, this can overwrite previous values if the DMA did not read the data, which causes the overflow interrupt.

    I'll look into code example to balance it for the ADCs input 

    Regards,
    Luke

  • Setting the dma trigger to mem10 already results in an underflow. The sample time is 10us, so it makes sense to me. Additionally an overflow is set, which makes me questioning the trustworthyness of the overflow.

  • Hi Kai,

    Can you check when the underflow and overflows are getting triggered. For example, is the overflow on the first DMA trigger and the underflow on the second?

    Regards,
    Luke

  • Hi,
    1)with dma trigger on mem10:
    Underflow is set when first dma interrupt happens
    Overflow is set when second dma interrupt happens

    2)with dma trigger on mem11

    Overflow is set when second dma interrupt happens

    Code 

    /*
     * Copyright (c) 2021, 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.
     */
    
    #include "ti_msp_dl_config.h"
    
    #define ADC_SAMPLE_SIZE (64)
    
    /* When FIFO is enabled 2 samples are compacted in a single word */
    #define ADC_FIFO_SAMPLES (ADC_SAMPLE_SIZE / 2)
    
    uint16_t gADCSamplesPing[ADC_SAMPLE_SIZE];
    uint16_t gADCSamplesPong[ADC_SAMPLE_SIZE];
    
    volatile bool gCheckADC;
    volatile bool gPing;
    volatile bool gOverflow;
    volatile bool gUnderflow;
    volatile uint32_t gOverflowCounter;
    volatile uint32_t gUnderflowCounter;
    
    
    int main(void) {
      gOverflow = false;
      gUnderflow = false;
      gOverflowCounter = 0U;
      gUnderflowCounter = 0U;
      gPing = true;
    
      SYSCFG_DL_init();
    
      /* Configure DMA source, destination and size */
      DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)DL_ADC12_getFIFOAddress(ADC12_0_INST));
      DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&gADCSamplesPing[0]);
      DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_SAMPLES);
      DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
    
      /* Setup interrupts on device */
      NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);
    
      gCheckADC = false;
    
      /* The ADC is configured for Repeat Single Conversion,
       * so the ADC will continue until DL_ADC12_disableConversions() is called */
      DL_ADC12_startConversion(ADC12_0_INST);
    
      while (1) {
    
        while (false == gCheckADC) {
          __WFE();
        }
        /* Breakpoint to check the buffers and watch the Ping Pong operation.
         * The data will be alternating between each buffer.
         */
        //__BKPT(0);
    
        /* Switch to send ADC Results to Pong Buffer */
        if (gPing) {
    
          DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&gADCSamplesPong[0]);
          DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_SAMPLES);
          DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
          DL_ADC12_enableDMA(ADC12_0_INST);
    
          gPing = false;
        }
        /* Switch to send ADC Results to Ping Buffer */
        else {
    
          DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&gADCSamplesPing[0]);
          DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_SAMPLES);
          DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
          DL_ADC12_enableDMA(ADC12_0_INST);
    
          gPing = true;
        }
      }
    
      gCheckADC = false;
    }
    
    void ADC12_0_INST_IRQHandler(void) {
      gOverflow = gOverflow || DL_ADC12_getRawInterruptStatus(ADC12_0_INST,DL_ADC12_INTERRUPT_OVERFLOW);
      gUnderflow = gUnderflow || DL_ADC12_getRawInterruptStatus(ADC12_0_INST,DL_ADC12_INTERRUPT_UNDERFLOW);
      if(!gOverflow){
        gOverflowCounter++;
      }
      if(!gUnderflow){
        gUnderflowCounter++;
      }
      switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {
      case DL_ADC12_IIDX_DMA_DONE:
        gCheckADC = true;
        break;
      default:
        break;
      }
    }

  • Thanks Kai, I'll see if I can replicate this on my end and investigate the conditions further. 

    Do you need the ADC samples in continuous repeat mode, and you want to verify you don't miss any samples?

  • Do you need the ADC samples in continuous repeat mode, and you want to verify you don't miss any samples?

    Exactly, I bascially want to do whats done in the example, but I've noticed the overflow flag is set for my setup. Therefore I checked the example and discovered the same behavior.

    Thanks!

  • Hi Kai,

    Do you have a target sampling rate?

    I am able to replicate the same with the current 10us, and adjusting the parameters of Sampling Speed, DMA Trigger source (which MEMRES is loaded) and the early interrupt thresholds change the behavior of the the overflow and underflow conditions.

    Currently I am investigating configurations for avoiding the overflow condition.

    Regards,
    Luke

  • Hi,
    this is what I have in my actual project (250ns on 4 channels, but with hw averaging, therefore 125ns):
    hw averaging ist not mandatory.


    If you can find the root cause of the overflow it would be nice to understand what causes it.

    Thanks

  • Hi Kai,

    I adjusted the buffer size from 64 to 12 to match the same as the memory register and this stopped the overflow conditional on the example.

    For why this adjust fixes the overflow - the DMA done interrupt doesn't trigger until the DMA transactions finish, with the listed value of 32 total transactions in the example. So while the DMA will complete the whole buffer size, the ADC will also continue to run and overwrite previous transactions. By matching the DMA transactions to the same number of ADC MEMRES registers the DMA done will trigger and the ADC will start its transactions without writing over old values.

    I wasn't able to clear the overflow for your sampling speed though, so I believe the speed of sampling is too fast for the reconfiguring. What you could do is repeat your channels to use all 12 MEMCTL registers, then the last MEMCTL set trigger mode to "valid trigger will step to next memory conversion register". Then after you reconfigure the dma start the ADC transactions again. You should be able to keep the ADC in repeat mode, but call the startConversions function.

    Regards,
    Luke

  • Hi,
    I see, the problem in the demo is that 32 isn't a multiple of 12.

    Two other questions:

    Wrt my problem, I couldn't find any documentation for the difference of the "valid trigger" and "trigger".
    1) Do you know where I can find that?

    For my actual code I noticed that as soon as I turn on compiler optimizations, the overflow flag is not set during continous sampling. It is set though, when I start transmitting data in parallel (via dma and spi).
    2)Is there a best practice how you can approach such a change in load. E.g. change the memresX interrupt for the adc dma, or trigger the renabling of the dma earlier?

    Thanks!

  • Hi Kai,

    A valid trigger is instance of the trigger source, for example if you're triggering from an event or software, another SW call or Event will be a "valid" trigger, otherwise it will move to the next channel as soon as the previous is finished.

    There are early interrupts you can have the DMA set to do, so the interrupt will fire before all the transfers have finished. For race conditions it is usually easier to handle if you guarantee one doesn't lap the other. So if you have the last ADC channel wait for the DMA to finish then restart the ADC sampling you'd know that the ADC won't overflow. The problem is that will restrict your total throughput as you will be limited by the DMA finish.

    Is your SPI transaction predictable, ie: we're the controller or the SPI request will happen every X amount of time?

    If the SPI is predictable then you can adjust your settings to compensate for the additional handling.

    Regards,
    Luke