This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Setting longer acquisition times and slower sampling for SAADC

Hello,

To preface, this project was redirected to me and I have very little experience using SoCs. Happy to provide any additional information and apologies for issues in post quality.

We are currently using the onboard SAADC of the nrf52832 as a way to convert the voltage outputs of a TI OPT101 photodiode into digital values for physiological measurements. In this scenario, we tend to measure in very low light settings, caring less about high sampling rates and more for longer acquisition times (~500 us). Moreover, the SoC is controlled by a master module, which sends triggers on when to acquire data. Reading the ADC (as in the code snippet posted below) should only occur when such a trigger is received and only one readout is performed at a time.

When this project was handed to me, it was using an example configuration for a one-channel, single-ended input performing single task measurements. Here is our acquisition code when it was handed to me (minor edits have been made since, but general flow remains the same): 

int32_t readADC(int analog_pin, uint8_t gain) {
  volatile int16_t result = 0;  // the adc outputs signed 16-bit
  volatile float precise_result = 0;
  volatile int32_t sumSamples = 0;

  // Configure SAADC singled-ended channel, Internal reference (0.6V) and 1/6
  // gain.
  NRF_SAADC->CH[0].CONFIG =
      (gain << 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 Analog input as positive input, no
  // negative input(single ended).
  NRF_SAADC->CH[0].PSELP = (analog_pin + 1) << SAADC_CH_PSELP_PSELP_Pos;

  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;  // This would increase for multiple channels
  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;

  // Take multiple samples.
  for (int i = 0; i < SAMPLES_TO_READ; i++) {
    // 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;

    sumSamples = sumSamples + result;
  }
  sumSamples = sumSamples / SAMPLES_TO_READ;
	//sumSamples = sumSamples;

  NRF_SAADC->TASKS_STOP = 1;
  while (NRF_SAADC->EVENTS_STOPPED == 0)
    ;
  NRF_SAADC->EVENTS_STOPPED = 0;
	
	return sumSamples;
  //return result;  // result only stored a single measurement. Instead, return sumSamples
}

In my reading, I saw that the SAADC supports up to 40 us acquisition times, and even that is well below our target range. Is there a way to define additional acquisition times that would suit our needs? 

Thank you very much for any feedback and assistance.

Parents
  • Hi Jared, 

    Thank you very much for your insight. My understanding is that oversampling is done through taking multiple samples and then averaging. Is that correct? I believe have done that "manually" in a sense in the for loop of acquisition script. 

    Best,

    Ed

Reply
  • Hi Jared, 

    Thank you very much for your insight. My understanding is that oversampling is done through taking multiple samples and then averaging. Is that correct? I believe have done that "manually" in a sense in the for loop of acquisition script. 

    Best,

    Ed

Children
  • edwardx said:
    Thank you very much for your insight. My understanding is that oversampling is done through taking multiple samples and then averaging. Is that correct? I believe have done that "manually" in a sense in the for loop of acquisition script. 

    That is correct, and indeed the loop that you have implements some form of oversampling. How is the sampled voltage versus the actual signal?

  • Hi Jared,

    That's a fair question. I'm working on placing a spare photodiode on a breakout board to measure voltages as the current ones are built into our modules. I can comment more on that once I have some data, but effectively we are only sampling noise at the moment. Our current setup samples with both the light on and off and those values overlap with each other.

    My uncertainty lies in whether a longer sampling time would help at the moment, particularly after finding (this post in the DevZone). If the ADC is not able to measure differences in the acquired signal, would sampling for longer be helpful (in constant light on the photodiode conditions). Any insight would be greatly appreciated.

    Thank you.

  • Photodiode current measurement? Don't need the SAADC although it can of course be used.

    Try reverse-biasing the photodiode with a port pin pulse; that charges the pin (and photodiode) to Vcc (say) 3 volts if you set up the pin in output mode and drive the pin high. Then turn the pin into an input and wait until the logic level changes from a '1' to a '0' as the charge slowly decays at a rate dependent on illumination level at the photodiode. This can of course be read with the SAADC, but that is not really necessary unless you want more accurate measurements. Time the decay with the RTC, and now you have a solution that takes only a few uA.

    Dynamic range? 200 very bright light to over 1,000,000 very dim light. Very low light is much easier to measure, but keep in mind opto-electronic devices may require temperature measurement and compensation.

    Oh, and series resistor? Don't bother; just use output mode S0D1 with pull-up (14k) enabled, then remember to disable the pull-up when switching to input mode as the pull-up applies to the pin always until disabled (datasheet errata). In fact don't even bother with output mode on the pin; just select input mode for the pin, enable the pull-up for a while long enough to reverse charge the photodiode, then disable the pull-up; repeat for each reading. Brilliant.

    Edit: Ok I see it's the OPT101 which is not a photodiode, it's a pricey photodiode with transimpedance amplifier. No worries, just don't connect pin 4 to pin 5 which will mess things up, simple photodiode is a better option though. That spare LED on the board would work just as well for about 1,000 times less cost; an LED reversed biased is a simple photodiode in disguise.

    /*
     * I use a charge-discharge measurement which provides a range of about 30uSecs to 250mSecs;  a
     * count of 100 is very bright light, with an active-high oscilloscope pulse of about 30uSecs
     * in length; a count of 1,000,000 is pretty dark, with a pulse (limited to) about 250mSecs in
     * length.
     */

  • Thanks to everyone for their helpful replies. I will have to try out what hmolesworth suggested, though that may take some time to implement.

    Somewhat on the same thread of that though, is there information regarding the (effective) time constant for the saadc? I know the figure provided in the documentation (Figure 6 here) is a simplified representation, but I imagine that the capacitor in the saadc does specific properties. From what I can tell, the lowest voltage from our photodiode is 7.5 mV and I was wondering if we are simply measuring too low of a voltage change.

    Also, is there way to identify the source resistance for a given part? For example if we're connecting the OPT 101 directly to the ADC?

    Thank you!

  • (Trying to line up this reply, but some of reply boxes don't show..)

    The output impedance of the OPT101 is very low compared to the input impedance of the SAADC, so the quoted SAADC figure of less than 10k input impedance (ie OPT101 output impedance) for 3uSec acquisition is very much higher than the actual output impedance of the OPT101. This means the sample period can be short, such as 3uSecs. However, the slew rate of the OPT101 is slow, so you might want to use a 40uSec acquisition time if the optical signals are triggered from the same trigger as the SAADC, for example a synchronously pulsed LED shining through skin with very short illumination time. However in that case a better technique is to use a phase offset of the SAADC trigger such that the LED turns on, illuminates the OPT101, the OPT101 settles (say 90uSec total) then trigger the SAADC with a short acquisition time of 3uSec, then turn off the LED.

    If the illumination is not pulsed, but always present then none of this applies and a short acquisition time can be used as the OPT101 is always settled. In any case 7.5mV is quite low, and so internal reg and max SAADC gain will be required. The signal can be increased by placing a large resistor (say 2M) between pins 4 and 5 instead of the usual direct connection.

    Avoid loading the OPT101 output with capacitance more than 10nF (nF not uF), ie don't hang a large capacitor on the SAADC input which is often recommended to improve SAADC performance. By the same token, don't place a resistor to Gnd of less than 10k; no resistor is required, and the SAADC input impedance  is a lot higher than 10k. Both of these points will keep the OPT101 fast and stable.

    If not familiar with pulsed-LED opto, it provides a very high signal:noise ratio for very low power. 100uSec LED pulse every 100mSec  allows (say) 100 times more current through the LED with 10% of overall power, or normal LED current with 1,000 times less battery power.

Related