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

Simple one shot ADC example

Hi,

I'm trying to find simple ADC code for one shot modus as described in the documentation:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fsaadc.html

The question has been asked before (without any concrete answer):
https://devzone.nordicsemi.com/f/nordic-q-a/46716/simple-adc-code/184681#184681

Another here (but I don't understand/cannot follow the thread):
https://devzone.nordicsemi.com/f/nordic-q-a/7188/simple-thermistor-adc-reading

The SDK provides an example with SAADC, which I got to work fine.
The disadvantage is that this example is pretty complex and uses timers (which conflicts with other parts of my code which also need timers).
Also I'm not able to integrate this SAADC example into my project, I have the same problem as here:
https://devzone.nordicsemi.com/f/nordic-q-a/33850/nrf_section_iter-problem-with-sdk-15-and-armgcc/130057#130057

I do not understand "linker script file"?

But I also don't understand why the examples are so complex? Why is it extremely difficult to combine examples?

Simple Arduino example how to perform an analog read:

int analogPin = A3; // potentiometer wiper (middle terminal) connected to analog pin 3
                    // outside leads to ground and +5V
int val = 0;  // variable to store the value read

void setup() {
  Serial.begin(9600);           //  setup serial
}

void loop() {
  val = analogRead(analogPin);  // read the input pin
  Serial.println(val);          // debug value
}

I found this thread "Simple ADC read":
https://devzone.nordicsemi.com/f/nordic-q-a/19934/simple-analog-read-on-nrf52/77586#77586

And they point to an example on github:
github.com/.../main.c

The example doesn't give any output, so I updated the code a bit:

#include <stdio.h>
#include <nrf.h>
#include "boards.h"
#include "nrf_delay.h"
#include "nrf_drv_saadc.h"

#define VERSION_ADC      ((0 << 5) | (31 & 0x1F))    // P0.31
#define VERSION_EN       ((0 << 5) | (26 & 0x1F))    // P0.26

void version_enable_init() {
    nrf_gpio_cfg_output(VERSION_EN);  
    nrf_gpio_pin_write(VERSION_EN,1);
}

int main(void)
{
  volatile int16_t result = 0;
  volatile float precise_result = 0;
  version_enable_init();
  nrf_delay_ms(100);
  printf("starting\n");

  // Start HFCLK from crystal oscillator, this will give the SAADC higher accuracy
  NRF_CLOCK->TASKS_HFCLKSTART = 1;
  while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
  NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;

  // Configure SAADC singled-ended channel, Internal reference (0.6V) and 1/6 gain.
  NRF_SAADC->CH[0].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos) |
                            (SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos) |
                            (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                            (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos) |
                            (SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos) |
                            (SAADC_CH_CONFIG_TACQ_3us        << SAADC_CH_CONFIG_TACQ_Pos);

  // Configure the SAADC channel with VDD as positive input, no negative input(single ended).
  //NRF_SAADC->CH[0].PSELP = SAADC_CH_PSELP_PSELP_VDD << SAADC_CH_PSELP_PSELP_Pos;
  //NRF_SAADC->CH[0].PSELP = VERSION_ADC;
  NRF_SAADC->CH[0].PSELP = NRF_SAADC_INPUT_AIN7;
  NRF_SAADC->CH[0].PSELN = SAADC_CH_PSELN_PSELN_NC << SAADC_CH_PSELN_PSELN_Pos;

  // Configure the SAADC resolution.
  NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_14bit << SAADC_RESOLUTION_VAL_Pos;

  // Configure result to be put in RAM at the location of "result" variable.
  NRF_SAADC->RESULT.MAXCNT = 1;
  NRF_SAADC->RESULT.PTR = (uint32_t)&result;

  // No automatic sampling, will trigger with TASKS_SAMPLE.
  NRF_SAADC->SAMPLERATE = SAADC_SAMPLERATE_MODE_Task << SAADC_SAMPLERATE_MODE_Pos;

  // Enable SAADC (would capture analog pins if they were used in CH[0].PSELP)
  NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos;

  // Calibrate the SAADC (only needs to be done once in a while)
  NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
  while (NRF_SAADC->EVENTS_CALIBRATEDONE == 0);
  NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
  while (NRF_SAADC->STATUS == (SAADC_STATUS_STATUS_Busy <<SAADC_STATUS_STATUS_Pos));

  // Start the SAADC and wait for the started event.
  NRF_SAADC->TASKS_START = 1;
  while (NRF_SAADC->EVENTS_STARTED == 0);
  NRF_SAADC->EVENTS_STARTED = 0;

  // Do a SAADC sample, will put the result in the configured RAM buffer.
  NRF_SAADC->TASKS_SAMPLE = 1;
  while (NRF_SAADC->EVENTS_END == 0);
  NRF_SAADC->EVENTS_END = 0;

  // Convert the result to voltage
  // Result = [V(p) - V(n)] * GAIN/REFERENCE * 2^(RESOLUTION)
  // Result = (VDD - 0) * ((1/6) / 0.6) * 2^14
  // VDD = Result / 4551.1
  precise_result = (float)result / 4551.1f;
  printf("result: %0.2f\n",precise_result);
  precise_result; // to get rid of set but not used warning

  // Stop the SAADC, since it's not used anymore.
  NRF_SAADC->TASKS_STOP = 1;
  while (NRF_SAADC->EVENTS_STOPPED == 0);
  NRF_SAADC->EVENTS_STOPPED = 0;


  while (1)
  {
    __WFE();
  }
}


But the result is always 0 (or 3 when using VDD). Did I set the wrong ADC channel? I'm sure I need NRF_SAADC_INPUT_AIN7?

I'm sure it is possible to do a ADC read without timers, but how?

Parents
  • The code looks broadly correct assuming that pin P0.26 provides the excitation voltage for something connected on pin P0.31. (edited, I misread the original)

    You are missing an errata workaround, however, detailed here:

      // Calibrate the SAADC (only needs to be done once in a while)
      NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
      while (NRF_SAADC->EVENTS_CALIBRATEDONE == 0)
         ;
      NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
      while (NRF_SAADC->STATUS == (SAADC_STATUS_STATUS_Busy << SAADC_STATUS_STATUS_Pos))
         ;
      // [Errata 86] SAADC: Triggering START task after offset calibration may write a sample to RAM
      //  This anomaly applies to IC Rev. Rev 2, build codes QFAA-Ex0, CIAA-Ex0, QFAB-Ex0.
      //  Calibration should follow the pattern STOP -> STOPPED -> CALIBRATEOFFSET -> CALIBRATEDONE -> STOP -> STOPPED -> START
      // Stop the SAADC and wait for the stopped event as per Errata 86
      NRF_SAADC->TASKS_STOP = 1;
      while (NRF_SAADC->EVENTS_STOPPED == 0)
         ;
      NRF_SAADC->EVENTS_STOPPED = 0;

    Additionally I would suggest looking at the binary result in case there is some issue with IDE floating point settings in (say) printf as below:

      printf("result: 0x%04X, precise_result: %0.2f\n", result, precise_result);
      printf("result: 0x%04X, precise_result: %4.2f\n", result, precise_result);

    %0.2f does not do what you expect; try 5.2% maybe :-)

Reply
  • The code looks broadly correct assuming that pin P0.26 provides the excitation voltage for something connected on pin P0.31. (edited, I misread the original)

    You are missing an errata workaround, however, detailed here:

      // Calibrate the SAADC (only needs to be done once in a while)
      NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
      while (NRF_SAADC->EVENTS_CALIBRATEDONE == 0)
         ;
      NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
      while (NRF_SAADC->STATUS == (SAADC_STATUS_STATUS_Busy << SAADC_STATUS_STATUS_Pos))
         ;
      // [Errata 86] SAADC: Triggering START task after offset calibration may write a sample to RAM
      //  This anomaly applies to IC Rev. Rev 2, build codes QFAA-Ex0, CIAA-Ex0, QFAB-Ex0.
      //  Calibration should follow the pattern STOP -> STOPPED -> CALIBRATEOFFSET -> CALIBRATEDONE -> STOP -> STOPPED -> START
      // Stop the SAADC and wait for the stopped event as per Errata 86
      NRF_SAADC->TASKS_STOP = 1;
      while (NRF_SAADC->EVENTS_STOPPED == 0)
         ;
      NRF_SAADC->EVENTS_STOPPED = 0;

    Additionally I would suggest looking at the binary result in case there is some issue with IDE floating point settings in (say) printf as below:

      printf("result: 0x%04X, precise_result: %0.2f\n", result, precise_result);
      printf("result: 0x%04X, precise_result: %4.2f\n", result, precise_result);

    %0.2f does not do what you expect; try 5.2% maybe :-)

Children
No Data
Related