This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SAADC does not sample

I'm trying to sample analog input but there is no value after sampling.

I tested the input by configuring it as digital input and it worked correctly.

Below is code. Any help would be greatly appreciated.

// Configure ADC and enable it
void adc_init( void )
{
    NRF_SAADC->CH[0].PSELP = 1;
    NRF_SAADC->CH[0].PSELN = 0;
    NRF_SAADC->CH[0].CONFIG = 0x20010;
    NRF_SAADC->RESOLUTION = 0;
    NRF_SAADC->RESULT.PTR = (uint32_t)adcResult;
    NRF_SAADC->RESULT.MAXCNT = 1;
    nrf_saadc_enable();
    nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
}

int main(void)
{
    // Initialize clock
    clocks_start();
    
    nrf_delay_ms(1);

    // Initialize peripherals
    gpio_init();
    adc_init();

    nrf_gpio_pin_write(LED_1,0);

    nrf_delay_ms(100);

    while (true)
    {
        nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
        while (nrf_saadc_event_check(NRF_SAADC_EVENT_DONE) == 0);
        nrf_adc_result = ((uint16_t)*adcResult >> 8) & 0x7F;
        nrf_saadc_event_clear(NRF_SAADC_EVENT_DONE);

        if (nrf_adc_result > 25)
        {
            nrf_gpio_pin_write(LED_2,0);
        }
        else
        {
            nrf_gpio_pin_write(LED_2,1);
        }

        if (nrf_adc_result > 50)
        {
            nrf_gpio_pin_write(LED_3,0);
        }
        else
        {
            nrf_gpio_pin_write(LED_3,1);
        }

        if (nrf_adc_result > 100)
        {
            nrf_gpio_pin_write(LED_4,0);
        }
        else
        {
            nrf_gpio_pin_write(LED_4,1);
        }

        nrf_gpio_pin_toggle(LED_1);
        nrf_delay_ms(250);


    }
}

Parents
  • OK, I have figured this out. My code had several problems. The first problem, which made it very difficult to debug,

    was that the SAMPLE task only executes once and then apparently the SAADC stops. So, when I want to sample

    again, I have to start the SAADC and then I can start the SAMPLE task. The second problem is that I needed to configure

    the SAADC result address as &adcResult. The third problem is that I was assuming the placement of the 8-bit value

    and the sign bit. Both were wrong. There is missing information from the nRF52832 data sheet. Even for 8-bit value,

    the sign bit is bit 15 of the result. Also, when configured for an 8-bit value, the SAADC result is not little endian.

    Here are the changes I made to the code:

    NRF_SAADC->RESULT.PTR = (uint32_t)&adcResult;
    
            nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
            while (nrf_saadc_event_check(NRF_SAADC_EVENT_STARTED) == 0);
            nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
            while (nrf_saadc_event_check(NRF_SAADC_EVENT_END) == 0);
            nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
            nrf_adc_result = (uint16_t)adcResult;
            if (nrf_adc_result >> 15 == 1)
            {
                nrf_adc_result = 0;
            }

Reply
  • OK, I have figured this out. My code had several problems. The first problem, which made it very difficult to debug,

    was that the SAMPLE task only executes once and then apparently the SAADC stops. So, when I want to sample

    again, I have to start the SAADC and then I can start the SAMPLE task. The second problem is that I needed to configure

    the SAADC result address as &adcResult. The third problem is that I was assuming the placement of the 8-bit value

    and the sign bit. Both were wrong. There is missing information from the nRF52832 data sheet. Even for 8-bit value,

    the sign bit is bit 15 of the result. Also, when configured for an 8-bit value, the SAADC result is not little endian.

    Here are the changes I made to the code:

    NRF_SAADC->RESULT.PTR = (uint32_t)&adcResult;
    
            nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
            while (nrf_saadc_event_check(NRF_SAADC_EVENT_STARTED) == 0);
            nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
            while (nrf_saadc_event_check(NRF_SAADC_EVENT_END) == 0);
            nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
            nrf_adc_result = (uint16_t)adcResult;
            if (nrf_adc_result >> 15 == 1)
            {
                nrf_adc_result = 0;
            }

Children
  • LOL: This is an example on how to not use a pointer variable.

    This is how it is supposed to look:

    static volatile int16_t nrf_adc_result;
    
    int16_t readResult(){
            NRF_SAADC->RESULT.PTR = (uint32_t)&nrf_adc_result;
    
            nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
            while (nrf_saadc_event_check(NRF_SAADC_EVENT_STARTED) == 0);
            nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
            while (nrf_saadc_event_check(NRF_SAADC_EVENT_END) == 0);
            nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
            if (nrf_adc_result <0)
            {
                nrf_adc_result = 0;
            }
    
          return nrf_adc_result;
    }

    Note that you can eliminate the pointer variable altogether here.

    I also notices that I was mistaken above: You need to read at least two books: One for basic C programming, one for programming C on a microcontroller like the ARM Cortex-M variants. The latter tells you about interrupts and the use of volatile among many other details.

Related