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.

EK-TM4C123GXL: ADC Setup for a correct Samping Rate

Part Number: EK-TM4C123GXL

Hello,

I would like to set the ADC correctly.
But I do not understand all the connections in the data sheet:
On page 1389 in the data sheet the following data can be found:
-ADC conversion clock frequency = 16Mhz
-ADC conversion rate = 1Msps
-ADC sample time = 250ns
-ADC conversion time = 1us
-latency from trigger to start of conversion = 2 ADC clocks

How are these data related?

My hardware config looks like this:

void init_SYS_CLOCK()
{
    MAP_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | 
                       SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    
    MAP_SysCtlDelay(10);
}

void init_WTIMER_5()
{
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER5);
    while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_WTIMER5));
    
    // WTimer 5A is PD6 and the trigger for adc
    MAP_TimerConfigure(WTIMER5_BASE, TIMER_CFG_SPLIT_PAIR | 
                       TIMER_CFG_A_PERIODIC);
    
    // Set ADC sampling frequency to be 44KHz every 22.725uS
    MAP_TimerLoadSet(WTIMER5_BASE, TIMER_A, (SysCtlClockGet()/44000) - 1);
    
    // Enable the ADC trigger output for Timer A.
    TimerControlTrigger(WTIMER5_BASE, TIMER_A, true);
    
    TimerEnable(WTIMER5_BASE, TIMER_A);
}

void init_uDMA_MIC0()
{
    // adc mic setup
    // uDMA Channel 14
    MAP_uDMAChannelAssign(UDMA_CH14_ADC0_0);
    
    MAP_uDMAChannelAttributeDisable(14,
                                    UDMA_ATTR_ALTSELECT | 
                                    UDMA_ATTR_HIGH_PRIORITY | 
                                    UDMA_ATTR_REQMASK);
    
    MAP_uDMAChannelControlSet(14 | UDMA_PRI_SELECT,
                              UDMA_SIZE_16 | UDMA_SRC_INC_NONE | 
                              UDMA_DST_INC_16 | UDMA_ARB_4);
    
    MAP_uDMAChannelControlSet(14 | UDMA_ALT_SELECT, 
                              UDMA_SIZE_16 | UDMA_SRC_INC_NONE | 
                              UDMA_DST_INC_16 | UDMA_ARB_4);
    
    MAP_uDMAChannelAttributeEnable(14, UDMA_ATTR_USEBURST);
}

void init_ADC0()
{
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0));
    
    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | 
                      ADC_CLOCK_RATE_FULL, 1);
    
    SysCtlDelay(10);
}

void init_ADC0_MIC0()
{
    MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_2);
    
    MAP_IntDisable(INT_ADC0SS0);
    MAP_ADCIntDisable(ADC0_BASE, 0);
    MAP_ADCSequenceDisable(ADC0_BASE, 0);
    
    //MAP_ADCHardwareOversampleConfigure(ADC0_BASE, 32);
    
    MAP_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0);
    
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH5);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH5);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH5);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH5 | ADC_CTL_IE);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_CH5);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_CH5);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_CH5);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_CH5 | 
                                 ADC_CTL_IE | ADC_CTL_END);
    
    // ------------------ //
    // Priority Levels
    // Priority 4 = 0xE0 ->lower
    // Priority 3 = 0x60
    // Priority 2 = 0x20
    // Priority 1 = 0x00 ->higher
    // ------------------ //
    
    MAP_IntPrioritySet(INT_ADC0SS0, 0xE0); // Priority 4 !!!
}

void start_ADC0()
{
    recordMusic_ADC0();
    
    MAP_ADCSequenceEnable(ADC0_BASE, 0);
    MAP_ADCIntClear(ADC0_BASE, 0);
    MAP_ADCSequenceOverflowClear(ADC0_BASE, 0);
    MAP_ADCSequenceUnderflowClear(ADC0_BASE, 0);
    MAP_ADCSequenceDMAEnable(ADC0_BASE, 0);
    MAP_ADCIntEnable(ADC0_BASE, 0);
    MAP_IntEnable(INT_ADC0SS0);
}

void recordMusic_ADC0()
{
    #ifndef DEBUG_OFF
    DEBUG_PC4 = GPIO_PIN_4; // Rec
    #endif
    
    if (mic0BuffersStatus[0] == EMPTY)
    {
        // start primary channel to buffer in ADCBufferForFft0
        MAP_uDMAChannelTransferSet(14 | UDMA_PRI_SELECT, 
                                   UDMA_MODE_PINGPONG, 
                                   (void *)(ADC0_BASE + ADC_O_SSFIFO0), 
                                   &mic0Buffers[0], 1024);
        
        MAP_uDMAChannelEnable(14 | UDMA_PRI_SELECT);
        mic0BuffersStatus[0] = FILLING;
        
        #ifndef DEBUG_OFF
        DEBUG_PA2 = GPIO_PIN_2; // ADC Start Pri1
        #endif
    }
    
    if (mic0BuffersStatus[1] == EMPTY)
    {
        // start alternate channel to buffer in ADCBufferForFft1
        MAP_uDMAChannelTransferSet(14 | UDMA_ALT_SELECT, 
                                   UDMA_MODE_PINGPONG, 
                                   (void *)(ADC0_BASE + ADC_O_SSFIFO0), 
                                   &mic0Buffers[1], 1024);
        
        MAP_uDMAChannelEnable(14 | UDMA_ALT_SELECT);
        mic0BuffersStatus[1] = FILLING;
        
        #ifndef DEBUG_OFF
        DEBUG_PA3 = GPIO_PIN_3; // ADC Start Alt1
        #endif
    }
    
    #ifndef DEBUG_OFF
    DEBUG_PA2 = 0; // ADC Start Pri1
    DEBUG_PA3 = 0; // ADC Start Alt1
    #endif
}

The CPU clock on 80Mhz.
The ADC clock on 16Mhz.
The ADC conversion rate is at 1Msps.
The WTimer5A is set to 44kHz.
If hardware oversampling is off, how do I calculate my sampling rate now?
I would like to understand the correlations.
And would also be very happy about a few calculation examples.

I just checked my settings with a Logig Analyzer and found out that the timer frequency has no influence on the sampling rate.
Only when I turn on hardware oversampling, I get a low sampling rate.

  • Hi,

      If you want to create a sample rate of 44Khz then the easiest way is to configure the timer for 44kHz. I think you already know how to do that. For the ADC side, you want to use timer as the trigger source (ADC_TRIGGER_TIMER), not processor trigger (ADC_TRIGGER_PROCESSOR) as you currently have. With this setup, every 44Khz, the timer triggers the ADC to take a sample. The ADC generates a DMA request after the conversion. uDMA reads the sampled data to the buffer. Take a look at the example C:\ti\TivaWare_C_Series-2.2.0.295\examples\boards\ek-tm4c123gxl\adc_udma_pingpong where it uses the timer to generate 16Khz sample rate. You can reference this example and change the timer timeout to 44Khz. 

  • Where i use ADC_TRIGGER_PROCESSOR ?

    I dont understand it when i use ss0 with 7 steps this is not working.

    When i use only one step in ss0 it is working.

  • Where i use ADC_TRIGGER_PROCESSOR ?

    Perhaps I misread. I thought at one point of time you had ADC_TRIGGER_PROCESSOR. But if you have timer trigger like below then it is correct. 

     ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0);

    I dont understand it when i use ss0 with 7 steps this is not working.

    What is not working?

    I see you connect CH5 to eight steps for sequencer 0. I assume you want to take eight samples of the same channel and perhaps calculating an average in software. However, I also see you seem to only reserve one 16-bit word for the buffer instead of four 16-bit words for the buffer array. Is that what you want? I will suggest you keep it simple at one step per sequencer like the example and later gradually increase the number of of steps to 2,3,... to see the effect of each increment of step. 

    MAP_uDMAChannelTransferSet(14 | UDMA_PRI_SELECT,
    UDMA_MODE_PINGPONG,
    (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    &mic0Buffers[0], 1024);

  • What is not working?

    I would like to save 1024 samples with a fixed sample rate of 40kHz.
    These 1024 samples will be processed further.
    The recording of the samples must not be interrupted during this processing.
    For this reason I have 2 buffers.
    uint16_t mic0Buffers[2][1024];
    The primary and secondary uDMA channel should always collect 1024.
    I would like to take 0 at the ADC0 Sample Sequencers, because this has FiFo of 8 samples.
    The uDMA works more effective with more data at the same time. With this I mean a high arbittion size and in burst mode.
    So my thought was as follows:
    That I let collect the first 4 samples by the FiFo and then give an Interupt on the uDMA. So that 4 samples can be transferred at once and the FiFo can continue to collect samples in the next 4 samples in the FiFo. And again...
    But I do not get this set in the hardware.
    If I configure the Sample Sequencers 0 with a step and set the uDMA with an arbitionssize of 1, then it works.
    But as soon as I work with more steps, the recording time of the 1024 samples decreases. Why is this so?

  • Hi,

    But as soon as I work with more steps, the recording time of the 1024 samples decreases. Why is this so?

    I think the reason is in each timer trigger (you set it at 44Khz) you are trying to take 8 samples instead of 1 sample. Bear in mind on each timer trigger, it is taking 8 back-to-back samples and these 8 samples are not taken 44Khz rate but rather the whatever ADC clock rate you have (e.g. 16Mhz). 

  • I think the reason is in each timer trigger (you set it at 44Khz) you are trying to take 8 samples instead of 1 sample.

    I have changed this to a sequence.

    Bear in mind on each timer trigger, it is taking 8 back-to-back samples and these 8 samples are not taken 44Khz rate but rather the whatever ADC clock rate you have (e.g. 16Mhz)

    Thank you for this information. Now I also know what is done with the ADC conversion clock frequency = 16Mhz.

    I would like to work with the hardware oversample as well. But I don't understand yet how exactly this setting affects my samples.
    I have reduced this ADC conversion rate = 1Msps to 500ksps with my config. It says so in the example.
    If I now set the hardware oversample to 16 I get 31.250sps. And that would be lower than my 40khz from my timer. how do I calculate this correctly now, that my samples are recorded in a distance of 40khz despite hardware oversample?


    The connected microphone can measure 20Khz.
    That means I don't have to sample more like 40Khz.
    Is that correct?

    And I want to sample all 1024 samples exactly with this timer. Is this configuration correct for that?

    EXTERN uint16_t mic0Buffers[2][1024];
    EXTERN volatile enum UDMA_TRANSFER_STATE mic0BuffersStatus[2];
    

    void init_SYS_CLOCK()
    {
        // 80Mhz Clock
        MAP_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | 
                           SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
        
        MAP_SysCtlDelay(10);
    }

    void init_WTIMER_5()
    {
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER5);
        while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_WTIMER5));
        
        // WTimer 5A is PD6 and the trigger for adc
        MAP_TimerConfigure(WTIMER5_BASE, TIMER_CFG_SPLIT_PAIR | 
                           TIMER_CFG_A_PERIODIC);
        
        // set ADC sampling frequency to be 40KHz
        MAP_TimerLoadSet(WTIMER5_BASE, TIMER_A, 
                         (SysCtlClockGet()/40000) - 1);
        
        // enable the ADC trigger output for Timer A.
        TimerControlTrigger(WTIMER5_BASE, TIMER_A, true);
        
        TimerEnable(WTIMER5_BASE, TIMER_A);
    }

    void init_uDMA_MIC0()
    {
        // adc mic setup
        // uDMA Channel 14
        MAP_uDMAChannelAssign(UDMA_CH14_ADC0_0);
        
        MAP_uDMAChannelAttributeDisable(14,
                                        UDMA_ATTR_ALTSELECT | 
                                        UDMA_ATTR_HIGH_PRIORITY | 
                                        UDMA_ATTR_REQMASK);
        
        MAP_uDMAChannelControlSet(14 | UDMA_PRI_SELECT,
                                  UDMA_SIZE_16 | UDMA_SRC_INC_NONE | 
                                  UDMA_DST_INC_16 | UDMA_ARB_1);
        
        MAP_uDMAChannelControlSet(14 | UDMA_ALT_SELECT, 
                                  UDMA_SIZE_16 | UDMA_SRC_INC_NONE | 
                                  UDMA_DST_INC_16 | UDMA_ARB_1);
        
        MAP_uDMAChannelAttributeEnable(14, UDMA_ATTR_USEBURST);
    }

    void init_ADC0()
    {
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
        while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0));
        
        ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | 
                          ADC_CLOCK_RATE_HALF, 1);
        
        SysCtlDelay(10);
    }

    void init_ADC0_MIC0()
    {
        MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_2);
        
        MAP_IntDisable(INT_ADC0SS0);
        MAP_ADCIntDisable(ADC0_BASE, 0);
        MAP_ADCSequenceDisable(ADC0_BASE, 0);
        
        //MAP_ADCHardwareOversampleConfigure(ADC0_BASE, 16);
        MAP_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0);
        
        MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH5 | ADC_CTL_IE | ADC_CTL_END);
        
        // ------------------ //
        // Priority Levels
        // Priority 4 = 0xE0 ->lower
        // Priority 3 = 0x60
        // Priority 2 = 0x20
        // Priority 1 = 0x00 ->higher
        // ------------------ //
        
        MAP_IntPrioritySet(INT_ADC0SS0, 0xE0); // Priority 4 !!!
    }

    void start_ADC0()
    {
        recordMusic_ADC0();
        
        MAP_ADCSequenceEnable(ADC0_BASE, 0);
        MAP_ADCIntClear(ADC0_BASE, 0);
        MAP_ADCSequenceOverflowClear(ADC0_BASE, 0);
        MAP_ADCSequenceUnderflowClear(ADC0_BASE, 0);
        MAP_ADCSequenceDMAEnable(ADC0_BASE, 0);
        MAP_ADCIntEnable(ADC0_BASE, 0);
        MAP_IntEnable(INT_ADC0SS0);
    }

    void recordMusic_ADC0()
    {
        if (mic0BuffersStatus[0] == EMPTY)
        {
            // start primary channel to buffer in ADCBufferForFft0
            MAP_uDMAChannelTransferSet(14 | UDMA_PRI_SELECT, 
                                       UDMA_MODE_PINGPONG, 
                                       (void *)(ADC0_BASE + ADC_O_SSFIFO0), 
                                       &mic0Buffers[0], 1024);
            
            MAP_uDMAChannelEnable(14 | UDMA_PRI_SELECT);
            mic0BuffersStatus[0] = FILLING;
        }
        
        if (mic0BuffersStatus[1] == EMPTY)
        {
            // start alternate channel to buffer in ADCBufferForFft1
            MAP_uDMAChannelTransferSet(14 | UDMA_ALT_SELECT, 
                                       UDMA_MODE_PINGPONG, 
                                       (void *)(ADC0_BASE + ADC_O_SSFIFO0), 
                                       &mic0Buffers[1], 1024);
            
            MAP_uDMAChannelEnable(14 | UDMA_ALT_SELECT);
            mic0BuffersStatus[1] = FILLING;
        }
    }

    void ADC0Seq0IntHandler(void)
    {
        ADCIntClear(ADC0_BASE, 0);
        
        if (uDMAChannelModeGet(14 | UDMA_PRI_SELECT) == UDMA_MODE_STOP)
        {
            mic0BuffersStatus[0] = FULL;
        }
        
        if (uDMAChannelModeGet(14 | UDMA_ALT_SELECT) == UDMA_MODE_STOP)
        {
            mic0BuffersStatus[1] = FULL;
        }
    }

  • Hi,

      Are you sure oversampling is really what you want? See below description. Look at taking A, B, C and D samples on the input signal. Let's suppose you use oversampling and these A,B,C, and D will be taken at 44Khz. Do you want these four samples to be averaged? If your input signal is a music, do you want to take 16 samples and then average them. Taking 16 samples at 44Khz and then average them does not make sense to me for a music sampling.  I would rather just take one sample every 44Khz. In any case, oversample by 16 is too much in my opinion. It is your application and you should know what is best for you. I can image oversample by 2 or 4. In this case, you will set the timer to trigger at 44Khz x 2=88Khz or 44 x 44Khz x 4 = 176Khz. At these sample rate, you can take average of either 2 or 4 samples. 

    What you want might be what you had earlier. You use timer to trigger at 44Khz rate. Each trigger will take 8 samples as these 8 samples are back-to-back at 16Mhz (1MSPS). Average these 8 samples if you really think taking multiple samples will give you better fidelity. In the next trigger you average 8 samples.