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.

Changing sampling rate of ADC in cc2541

Other Parts Discussed in Thread: CC2541

Hi,


I want to sample a signal at different sampling rates. For that, I need to change the sampling rate of the ADC. How to change the sampling rate of the ADC ?
Is it fixed ?
The decimation factor changes the conversion time and not the rate at which it is sampled according to my understanding.

Any help on this appreciated.

Thanks,
Krishna Prasad

  • Hi,

    The ADC sampling rate in CC2541 is fixed, so there is no way to change. You can only change the decimation factor to get more averaging (better result).
  • Thanks Christin.

    I would like to know by using a timer, and writing an ISR for the timer, in which I start the ADC conversion and read the ADC data registers will it make a difference in sampling rate ?
    Say I am doing this periodically.

    Thanks,
    Krishna Prasad
  • Hi Krishna,

    Yes, you can use a timer to lower the effective sample rate (the rate at which you get/read samples). However, you don't have to write an ISR and manually trigger the ADC conversion. Instead, setup the ADC to trigger on Timer 1 Channel 0 compare event. Then setup Timer 1 in either up/down or modulo mode to get periodic triggers. You can control the sample rate then via Timer 1. See ADCCON1.STSEL = 0x10

  • Could you please provide a code snippet of how to setup both the timer and the adc to do what you suggest?

    We need to sample at 125hz (~ every 8 milliseconds).

    I have been struggling with this point for about 2 weeks and I am not able to get an adc interrupt and I even get stuck when  I read the fifo.

    Thanks

  • Hi Aharon,

    I created this code based on the document www.ti.com/lit/swru191

    #include "hal_dma.h" // Which you can find in most installers

    #define NUMBER_OF_ADC_CHANNELS 1 // Maximum 8
    #define NUMBER_OF_SAMPLES_PER_NOTIFICATION 2 // We can let the DMA perform a few samples before we decide to notify the CPU
    #define TRANSFERCOUNT (NUMBER_OF_ADC_CHANNELS * NUMBER_OF_SAMPLES_PER_NOTIFICATION)
    static volatile uint8 pingOrPong = 0;
    static uint16 adcSampleStorage_Ping[TRANSFERCOUNT]; // Number of samples to store in ping buffer
    static uint16 adcSampleStorage_Pong[TRANSFERCOUNT]; // Number of samples to store in pong buffer
    #define DMA_CHANNEL 0
    halDMADesc_t dmaCh0;

    void startADC()
    {
    halDMADesc_t *ch = &dmaCh0;
    HAL_DMA_SET_SOURCE(ch, &X_ADCL); // Address of ADC data low
    HAL_DMA_SET_DEST(ch, (uint8 *)adcSampleStorage_Ping); // Copy samples to global buffer
    HAL_DMA_SET_VLEN(ch, HAL_DMA_VLEN_USE_LEN); // Use length field configured next
    HAL_DMA_SET_LEN(ch, TRANSFERCOUNT); // Number of words to transfer before issuing DMA done interrupt
    HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_WORD); // Transfer 16 bit per word
    HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_SINGLE); // ADC will generate one trigger per sample
    HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_ADC_CHALL); // Trigger on any sample conversion
    HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_0); // Always read from the same address
    HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_1); // Increment with 1 word in destination per transfer
    // We implement interrupt service routine for the DMA done interrupt
    HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_ENABLE);
    HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);
    HAL_DMA_CLEAR_IRQ(DMA_CHANNEL);
    // Enable DMA interrupt
    DMAIE = 1;
    // Arm DMA
    HAL_DMA_ARM_CH(DMA_CHANNEL);

    // Now the DMA is setup, setup ADC
    #include "hal_adc.h"

    // Configure ADC (in this example we only setup a sequence of two channels (CH0 and CH1)
    // Setup ADC pin for channel 0 and 1
    APCFG = (APCFG & 0xFC) | 0x03; // These settings override P0SEL settings
    // Use internal 1.15V as reference as reference, resolution 12 bits, scan up to and including channel 1
    ADCCON2 = (HAL_ADC_RESOLUTION_12 << 4) | HAL_ADC_CHANNEL_1;
    // Start ADC conversion on Timer 1 Channel 0 compare event
    ADCCON1 = 0x23; // ADCCON1[5:4] = ADCCON1.STSEL = 0x10 --> Timer 1 Channel 0 compare event triggers sample. Two LSBs must be set to 0x03

    // Now ADC is setup, kick of everything by setting up Timer 1.
    // First setup tick frequency to 250000kHz. This allows us to get 125Hz from the 16 bits counter (250kHz/2000 = 125Hz)
    T1CTL = 0x0C; // This will set tick frequency to Tick/128. Without clock tick generator modifications this means 250kHz. It also suspends operation.
    // Clear count, just in case timer was running
    T1CNTL = 0;
    // Setup Channel 0 in compare mode, no capture, interrupt enabled
    T1CCTL0 = 0x40 | 0x04; // In order: interrupt enable, compare mode
    // Setup period, based on timer in modulo mode. Write low byte first, this is latched when high byte is written.
    T1CC0L = (2000 & 0x00FF);
    T1CC0H = ((2000 & 0xFF00) >> 8) & 0x00FF;

    // Now kick off everything by starting timer!
    T1CTL |= 0x02; // Count repeatedly from 0 to T1CC0 which has been setup to 2000 --> period 125Hz
    }

    #include "hal_mcu.h"
    /* It takes about 20-100us for the interrupt to be handled after interrupt is fired. This depends on what else is
    * running at the time, for example if some routine is currently disabling interrupts.
    * The interrupt service has (1/125) seconds to execute, to avoid overflow.
    * This is solved by limiting what needs to be done here. We try to limit it by simply changing destination buffer.
    * An event should here be sent to another task that can be executed outside of interrupt context.
    * The event has (1/125 * NUMBER_OF_SAMPLES_PER_NOTIFICATION) seconds to use the samples, to avoid missing any samples.
    */
    HAL_ISR_FUNCTION(dmaIsr, DMA_VECTOR)
    {
    HAL_ENTER_ISR();

    halDMADesc_t *ch = &dmaCh0;

    // Clear interrupt
    HAL_DMA_CLEAR_IRQ(DMA_CHANNEL);

    // Change between ping and pong buffers
    if (pingOrPong == 0)
    {
    // Currently using Ping, change to Pong
    HAL_DMA_SET_DEST(ch, (uint8 *)adcSampleStorage_Pong);
    pingOrPong = 1;
    }
    else
    {
    // Currently using Pong, change to Ping
    HAL_DMA_SET_DEST(ch, (uint8 *)adcSampleStorage_Ping);
    pingOrPong = 0;
    }

    // Re-Arm DMA, Timer keeps triggering ADC so this needs to happen before the next sample
    HAL_DMA_ARM_CH(DMA_CHANNEL);

    HAL_EXIT_ISR();
    }

  • Thanks Torbjorn. I will try the method and come back :)

  • Thanks Torbjorn
    Started to use your code but got stuck on the link as APCFG , ADCCON2 and ADCCON1 were unxefined.
    What do I have to include to have access to them?
  • Hi,

    Those are defined in the IAR include file for your device; "ioCC25xx.h" (ioCC2541 in your case, because you're using CC2541?)

  • I am trying your code in the 2650.
    Where do I find the definitions over there?
  • You can't use the code provided above for CC26xx. If you want to use ADC, please download the sensor controller studio www.ti.com/.../sensor-controller-studio
    and take a look at our TRM chapter 17 for AUX module
  • Hi Christin
    I finally was able to reproduce the SEnsor Controller Studio example with the light sensor.
    We are using the Smart RF06 board and would like to connect an external analog signal to one of the ADC ports for example port 1.
    Where is it on the board?
    Thanks
  • Thanks. It is working fine