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

  • 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.

Reply
  • 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.

Children
  • 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.

  • I can empathize as I similarly struggled with the reply boxes at times. Awesome. Your expertise in this is extremely helpful. We're using this set up for fNIRS (functional near infrared spectroscopy), if you're familiar with the modality. For any given optode source/detector pairing, we are hoping to turn on the LED, introduce a slight delay, acquire a measurement, and then rinse and repeat across multiple channels. It sounds like your second paragraph may be what I need to look into, particularly as we expect power delivered to the detector to be on the nanowatt scale.

    If I could ask you to expound on that a little, I did a bit of testing yesterday and was confused slightly by my results. Similar to your suggestion, there is a 200uSec delay between when the LED is turned on and when the SAADC measurement is acquired. I did this for both 3uSec and 40uSec, noting changes in acquisition time (measured using a timer function) that reflects this difference, but no changes in SAADC output. With the detector uncovered and facing upwards (room light on), I am getting a relatively consistent output of ~11500 for both acquisition times. Similarly, through a set of optical phantoms of varying absorption, I am getting either ~0 (correcting for negative SAADC readings) or very low values that are consistent between the acquisition times.

    Is there some form of internal correction that normalizes with respect to time that I am glossing over? Additionally, would this observation be consistent with your suggestion for incorporating a large resistor between pins 4 and 5 on OPT101?

    Thank you again for all of your help.

  • Interesting project, and I have some suggestions and pointers which may help. In essence your technique and results are correct and as to be expected. With the internal 1M resistor alone the gain is insufficient; dark output will be typically 7.5mV with a 1M feedback resistor which gives a quoted sensitivity of 0.45V/uW at 650nm ie 0.45mV/nw. A feedback resistor of 10M gives 4.5mV/nw so at 10nw illumination we get only 45mV; with a feedback resistor of 50M we get 22.5mV/nw with a 3dB bandwidth of 340Hz, A 10nW illumination level gives 225mV signal. However the bandwidth reduction requires a longer illumination time, so best to choose a series resistor which trades off amplification (signal level) for required LED illumination time. The 50M resistor requires between 3 and 14 mSec illumination, 14mSec (5 time constants) to get the full 12-bit resolution. So from these figures it is clear that there is no useable signal with 1M and the acquisition time is irrelevant, may as well stick to 3uSec (or 40uSec).

    To connect the 50M resistor (or whatever value suits depending on the nw illumination level) I would recommend the following circuit:

    Note the internal resistor is not required, and the external compensation capacitor can be left off for larger values and gains.

    Use PPI to get the phase offset of the SAADC trigger such that the LED turns on, illuminates the OPT101, the OPT101 settles (up to 14mSec worst-case with high gain) then trigger the SAADC with a short acquisition time of 3uSec, then turn off the LED. No software intervention required; could also simply use a PWM as the goal is to have no jitter in timings which would lead to signal degradation..

    Trick's: the OPT101 must not be allowed to saturate, and to get the best possible signal:noise ratio all the non-useful wavelengths must be masked. Assuming dual-band regions of interest at 690nm and 830nm select matching narrow-band optical filters placed on each OPT101 sensor for that band assuming you can dedicate individual OPT101s for each band. 10nm bandwidth optical filters are cheap but work very well, eg from Edmund Scientific.

Related