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.

CCS/TM4C123GH6PM: Tiva Timer Triggered ADC with uDMA

Part Number: TM4C123GH6PM

Tool/software: Code Composer Studio

Hi All,

I'm looking to get set up with uDMA on a timer triggered ADC with my Tiva TM4C123GH6PM. So far, I've found this thread quite instructive:  .

The example code there works perfectly however, I am interested in getting a solution that uses ADC_TRIGGER_TIMER rather than ADC_TRIGGER_ALWAYS. The reason being that I want to have an exact sampling interval so that I can perform signal processing using the CMSIS DSP library which I've already set up. I've taken the original code and modified the ConfigADC0 function as follows:

Unfortunately, it seems that the ADC trigger never occurs and the value returned is always 9999 (which is the value initialized before the main loop).

Does anyone see where I went wrong?

Has anyone had success in creating a Tiva project that continuously performs DMA transfers with timer triggered ADC?

Cheers,

Jim

  • Hi James,
    Take a look at the example in this post:
    e2e.ti.com/.../643173
  • Thanks Bob. That's a fantastic example.

    Cheers and Happy Holidays,
    Jim
  • Bob,

    That code was fantastic. I was able to modify it and get up and running a pretty cool realtime DSP example.

    One thing I'd like to do to expand here would be to set up more than one channel for DMA. It's unclear to me how exactly to go about it with your code. Mind giving me a pointer?

    Cheers and Happy Holidays,

    Jim

  • If the goal is to sample two or more channels at (roughly) the same time, just setup the ADC sequence to convert more channels. After the first ADC interrupt, the first buffer will be filled with alternating samples from the channels. You can split the samples as you process the data, or setup two memory to memory software triggered uDMA channels to copy every other entry into a separate buffer. This method can be expanded to multiple channels.

    If the samples need to be taken with almost no time difference, you can use ADC module 1 triggering off of the same timer to sample the second channel. You would setup a second uDMAChannel to transfer the samples from ADC module 1. This method is limited to only two simultaneous channels.
  • Thanks for the tips

     

    I'm trying to implement the first solution you mentioned which should result in alternating samples appearing in the ADC_Out buffers. I tried changing a few lines in the ADC_init function: 

        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2); 

        into

        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2);

     

        and 

        

         ADCSequenceStepConfigure(ADC0_BASE,0u,0u,ADC_CTL_CH0| ADC_CTL_END | ADC_CTL_IE);

         into

        ADCSequenceStepConfigure(ADC0_BASE,0u,0u,ADC_CTL_CH0);

        ADCSequenceStepConfigure(ADC0_BASE,0u,1,ADC_CTL_CH1| ADC_CTL_END | ADC_CTL_IE);

     

    Once again the goal is that the samples in the ADC_Out buffers alternate between values captured at PE3 and PE2. However it appears that only one of the two channels' values goes into the buffer. Have I missed something or made a mistake?

  • Your code appears correct - but for the appearance of  "ADCSequenceEnable()"  which should (follow) your last,  "ADCSequenceStepConfigure()".

    As that function is (not) shown - it may appear earlier in the code - and such may prove suspect.

    You've nicely added   "GPIOPinType()"  (for your added ADC channel) - so long as you employ "Sample Sequences (other) than Sequence 3" - your ability to convert that 2nd channel should be unimpeded...

  • , I got to root cause it was simply that my arbitration size needed to be 2 and not 1. Combined with the above steps I have a solution I can work with.

    Thanks again,
    Cheers
    Jim
  • Does CMSIS library work with TivaWare (your examples)? I have tried doing a similar thing, but it just HardFaults on a CMSIS function.
  • Savo,

    Are you able to run the example code included in the CMSIS library? Check out this thread 's post in this thread: 

    I followed his instructions exactly and got the example code up and running. The way I built my particular application was:

    1. Make slight adjustments for my use case (needed a BPF)

    2. Generated coefficients from a tool I got on this website () into .txt

    3. Used a python script to parse that .txt and turn it into an external header Coeffs.h

    4. Do an extern reference to said Coeffs.h

    5. Pull in the code Bob posted that sets up the uDMA and incorporate it such that it runs in a similar fashion to the CMSIS example code.

    6. Collapsed some of the functions from Bob's code into an external .h file just to reduce clutter and make it portable to multiple applications

    As far as structure goes all I'm doing is "ping-ponging" and running the same BPF filter on each of the two channels (after conversion to float) and then running an RMS and checking against thresholds 

    Some "gotcha's" I've noticed while trying to run FIRs:

     You have to make sure that you

    a) Set up the arm_fir_instance struct for each filter you intend to use

    b) Initialize said structure with arm_fir_init using the desired coefficients

    c) Make sure all your pointers are correct

    d) Make sure your input and output buffers are the same, correct size

    e) Sample a large enough uDMA buffer such that you can finish the desired FIR in time without missing samples (otherwise you'll get aliasing). My filter is 128 taps which is good enough at a sampling rate of 48kHz but if you're sampling faster than that you probably want to start reducing the number of taps

    f) Keep in mind that there will be a Zero Input response present in the output every time you run the filter. That is the reason why I use the "offset" variable to exclude that response and keep only samples that I trust are more indicative of the process variable I want to measure

    Anyway, here's my code. Sorry if it's still a little sloppy I'm not completely done playing around

    #include <stdbool.h>
    #include <stdint.h>
    #include "DMA_Helpers.h"
    #define __FPU_PRESENT 1
    #include "TivaHelpers.h"
    
    #include "arm_math.h"
    #include "math_helper.h"
    //#include "Data.h"
    #include "Coeffs.h"
    
    #include "hw_ints.h"
    #include "hw_memmap.h"
    #include "hw_adc.h"
    #include "hw_types.h"
    #include "hw_udma.h"
    #include "debug.h"
    #include "gpio.h"
    #include "interrupt.h"
    #include "pin_map.h"
    #include "sysctl.h"
    #include "uart.h"
    #include "adc.h"
    #include "udma.h"
    #include "timer.h"
    #include "rom.h"
    #include "rom_map.h"
    #include "systick.h"
    #include "uartstdio.h"
    
    #define ADC_SAMPLE_BUF_SIZE     1024
    unsigned int samplingFrequency=48000;
    
    
    // -------------------------------------------------------------------
     // Declare State buffer of size (numTaps + blockSize - 1)
     // -------------------------------------------------------------------
    
    static float32_t firStateF32[ADC_SAMPLE_BUF_SIZE + NUM_TAPS - 1];
    
    extern const float32_t firCoeffs32[NUM_TAPS];
    
    /// ------------------------------------------------------------------
    ///  Global variables for FIR LPF Example
    ///  -------------------------------------------------------------------
    
    uint32_t blockSize = ADC_SAMPLE_BUF_SIZE/2;
    
    
    enum BUFFERSTATUS
                      { EMPTY,
                        FILLING,
                        FULL
                      };
    
    #pragma DATA_ALIGN(ucControlTable, 1024)
    uint8_t ucControlTable[1024];
    
    //Buffers to hold alternating samples from each channel
    //Two Channel
    static uint16_t ADC_Out1[ADC_SAMPLE_BUF_SIZE];
    static uint16_t ADC_Out2[ADC_SAMPLE_BUF_SIZE];
    
    static float32_t Converted_ADC_Out1_PE3[ADC_SAMPLE_BUF_SIZE/2];
    static float32_t Filtered_ADC_Out1_PE3[ADC_SAMPLE_BUF_SIZE/2];
    static float32_t Converted_ADC_Out2_PE3[ADC_SAMPLE_BUF_SIZE/2];
    static float32_t Filtered_ADC_Out2_PE3[ADC_SAMPLE_BUF_SIZE/2];
    
    
    static float32_t Converted_ADC_Out1_PE2[ADC_SAMPLE_BUF_SIZE/2];
    static float32_t Filtered_ADC_Out1_PE2[ADC_SAMPLE_BUF_SIZE/2];
    static float32_t Converted_ADC_Out2_PE2[ADC_SAMPLE_BUF_SIZE/2];
    static float32_t Filtered_ADC_Out2_PE2[ADC_SAMPLE_BUF_SIZE/2];
    
    static enum BUFFERSTATUS BufferStatus[2];
    
    
    static uint32_t g_ui32DMAErrCount = 0u;
    static uint32_t g_ui32SysTickCount;
    
    
    
    
    int firstLoop=1;
    
    void init_DMA(void);
    
    
    void uDMAErrorHandler(void)
    {
        uint32_t ui32Status;
        ui32Status = MAP_uDMAErrorStatusGet();
        if(ui32Status)
        {
            MAP_uDMAErrorStatusClear();
            g_ui32DMAErrCount++;
        }
    }
    
    // Not used in this example, but used to debug to make sure timer interrupts happen
    void Timer0AIntHandler(void)
    {
        //
        // Clear the timer interrupt flag.
        //
        TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    }
    
    void SysTickIntHandler(void)
    {
        // Update our system tick counter.
        g_ui32SysTickCount++;
    }
    
    void ADCseq0Handler()
    {
        ADCIntClear(ADC0_BASE, 0);
    
        if ((uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT) == UDMA_MODE_STOP)
                && (BufferStatus[0] == FILLING))
        {
            BufferStatus[0] = FULL;
            BufferStatus[1] = FILLING;
        }
        else if ((uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT) == UDMA_MODE_STOP)
                && (BufferStatus[1] == FILLING))
    
        {
            BufferStatus[0] = FILLING;
            BufferStatus[1] = FULL;
        }
    }
    
    int main(void)
    {
    //    int debugFlag=0;
    
    
        uint32_t i, average1, average2, samples_taken;
        float32_t RMS1, RMS2;
    
        BufferStatus[0] = FILLING;
        BufferStatus[1] = EMPTY;
        samples_taken = 0u;
    
        //Turn on GPIO C Pins 7,6,5
        //Assignments:
        //Pin 7 = Conversion
        //Pin 6 = FIR
        //Pin 5 = RMS
        init_PortC_Outputs();
    
        //Set up FIR stuff//
        //initialize structs for each instance of filter
    
        arm_fir_instance_f32 S;
    
    
        //// Call FIR init function to initialize the instance structure.
        arm_fir_init_f32(&S, NUM_TAPS, (float32_t *)&firCoeffs32[0], &firStateF32[0], blockSize);
    
    
        DMA_prime(ADC_SAMPLE_BUF_SIZE, ADC_Out1, ADC_Out2, samplingFrequency);
    
        uint32_t offset=128;
        float32_t mean1=0;
        float32_t mean2=0;
    
        while(1)
        {
    
    
    
            if(BufferStatus[0u] == FULL)
            {
                // Do something with data in ADC_Out1_PE3
                average1 = 0u;
    
    
                GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_7,0);
                for(i =0u; i < ADC_SAMPLE_BUF_SIZE; i=i+2)
                {
                    Converted_ADC_Out1_PE3[i/2] = (float32_t)3.3*ADC_Out1[i]/4095;
                    Converted_ADC_Out1_PE2[i/2] = (float32_t)3.3*ADC_Out1[i+1]/4095;
                }
                GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_7,GPIO_PIN_7);
    
                //Run FIR on data
                if(!firstLoop){
                    //To Do:
                    //1. Make List of all variables and block diagram
                    //2. Create necessary variables to store info about CH2
                    //3. Perform filtering
                    //4. Output Inductance value as well as individual voltage levels
                    GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_6,0);
                    arm_fir_f32(&S, &Converted_ADC_Out1_PE3[0], &Filtered_ADC_Out1_PE3[0], blockSize);
                    GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_6,GPIO_PIN_6);
                    //Calculate RMS Values
                    GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_5,0);
                    arm_rms_f32(&Filtered_ADC_Out1_PE3[offset],blockSize-offset,&RMS1);
                    arm_mean_f32(&Filtered_ADC_Out1_PE3[offset],blockSize-offset,&mean1);
                    RMS1=RMS1-mean1;
                    GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_5,GPIO_PIN_5);
                }
                average1=(uint32_t)1000*RMS1;
    
                BufferStatus[0u] = EMPTY;
                // Enable for another uDMA block transfer
                uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &ADC_Out1, ADC_SAMPLE_BUF_SIZE);
                uDMAChannelEnable(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT); // Enables DMA channel so it can perform transfers
                samples_taken += ADC_SAMPLE_BUF_SIZE;
            }
            if(BufferStatus[1u] == FULL)
            {
                // Do something with data in ADC_Out2_PE3
                average2 = 0u;
    
                GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_7,0);
                for(i =0u; i < ADC_SAMPLE_BUF_SIZE; i=i+2)
                {
                    Converted_ADC_Out2_PE3[i/2] = (float32_t)3.3*ADC_Out2[i]/4095;
                    Converted_ADC_Out1_PE2[i/2] = (float32_t)3.3*ADC_Out1[i+1]/4095;
                }
                GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_7,GPIO_PIN_7);
    
                //Run FIR on data
                if(!firstLoop){
                    GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_6,0);
                    arm_fir_f32(&S, &Converted_ADC_Out2_PE3[0], &Filtered_ADC_Out2_PE3[0], blockSize);
                    GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_6,GPIO_PIN_6);
                    //Calculate RMS Values
                    GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_5,0);
                    arm_rms_f32(&Filtered_ADC_Out2_PE3[offset],blockSize-offset,&RMS2);
                    arm_mean_f32(&Filtered_ADC_Out2_PE3[offset],blockSize-offset,&mean2);
                    RMS1=RMS2-mean2;
                    GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_5,GPIO_PIN_5);
                }
                average2=(uint32_t)1000*RMS1;
    
    
                BufferStatus[1u] = EMPTY;
                // Enable for another uDMA block transfer
                uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &ADC_Out2, ADC_SAMPLE_BUF_SIZE);
     //           uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO3), &ADC_Out2_PE2, ADC_SAMPLE_BUF_SIZE);
                uDMAChannelEnable(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT);
                samples_taken += ADC_SAMPLE_BUF_SIZE;
               ///purge first loop issues///
                if(firstLoop){
                    average1=0u;
                    average2=0u;
                    samples_taken=0u;
                    firstLoop=0;
                }
                UARTprintf("\t%d   \t\t%d   \t\t%d   \r", average1,average2,samples_taken);
            }
        }
    }
    
    
    void init_DMA(void)
    {
    
        uDMAEnable(); // Enables uDMA
        uDMAControlBaseSet(ucControlTable);
    
    
        uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
    
        uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0, UDMA_ATTR_USEBURST);
        // Only allow burst transfers
    
        //Single Channel
    //    uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
    //    uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
        uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_2);
        uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_2);
    
    
        uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &ADC_Out1, ADC_SAMPLE_BUF_SIZE);
        uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &ADC_Out2, ADC_SAMPLE_BUF_SIZE);
    
    
    
    
        uDMAChannelEnable(UDMA_CHANNEL_ADC0); // Enables DMA channel so it can perform transfers
    
        //ToDo Add second Channel
        //Need to change ADC settings accordingly
        //Need to change arbitration size
    }
    

  • @ poster James,

    "LIKE - LIKE - LIKE!"

    Outstanding post - "Thoughtful, Detailed, Guard-Banded (Gotcha's) and ... (non-rushed) ...  truly well Organized & CARING!"
    Great Job - goes FAR to, "Restore the banned LIKE" - which your effort  (SO obviously) warrants!

  • Hey,

    I have fixed it yesterday.  Here's the thread. But, thanks anyway.

  • @ James,

    I'm prepared to "lend" (or rent), my pistol.       (quickly/completely - "dulls the hurt" - of such (muted) appreciation...)