ADC measurements with ulp-fsm are unreliable.

CKiamy
Posts: 19
Joined: Wed Nov 23, 2022 3:01 pm

ADC measurements with ulp-fsm are unreliable.

Postby CKiamy » Wed Jan 25, 2023 2:57 pm

I am building an application where I would like to read the data from one of the ADC pins using the ULP, and then use this measurement data in the main application after ULP wakeup. I modified the ulp-adc example that Espressif provides to test the ADC to see if it fits my needs.
  1. /*
  2.  * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
  3.  *
  4.  * SPDX-License-Identifier: Unlicense OR CC0-1.0
  5.  */
  6. /* ULP Example: using ADC in deep sleep
  7.  
  8.    This example code is in the Public Domain (or CC0 licensed, at your option.)
  9.  
  10.    Unless required by applicable law or agreed to in writing, this
  11.    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  12.    CONDITIONS OF ANY KIND, either express or implied.
  13.  
  14.    This file contains assembly code which runs on the ULP.
  15.  
  16.    ULP wakes up to run this code at a certain period, determined by the values
  17.    in SENS_ULP_CP_SLEEP_CYCx_REG registers. On each wake up, the program
  18.    measures input voltage on the given ADC channel 'adc_oversampling_factor'
  19.    times. Measurements are accumulated and average value is calculated.
  20.    Average value is compared to the two thresholds: 'low_thr' and 'high_thr'.
  21.    If the value is less than 'low_thr' or more than 'high_thr', ULP wakes up
  22.    the chip from deep sleep.
  23. */
  24.  
  25. /* ULP assembly files are passed through C preprocessor first, so include directives
  26.    and C macros may be used in these files
  27.  */
  28. #include "soc/rtc_cntl_reg.h"
  29. #include "soc/soc_ulp.h"
  30. #include "example_config.h"
  31.  
  32.     .set adc_channel, EXAMPLE_ADC_CHANNEL
  33.  
  34.     /* Configure the number of ADC samples to average on each measurement.
  35.        For convenience, make it a power of 2. */
  36.     .set adc_oversampling_factor_log, 0x1000
  37.     .set adc_oversampling_factor, (1 << adc_oversampling_factor_log)
  38.  
  39.     /* Define variables, which go into .bss section (zero-initialized data) */
  40.     .bss
  41.  
  42.     /* Low threshold of ADC reading.
  43.        Set by the main program. */
  44.     .global low_thr
  45. low_thr:
  46.     .long 0
  47.  
  48.     /* High threshold of ADC reading.
  49.        Set by the main program. */
  50.     .global high_thr
  51. high_thr:
  52.     .long 0
  53.  
  54.     /* Counter of measurements done */
  55.     .global sample_counter
  56. sample_counter:
  57.     .long 0
  58.  
  59.     .global last_result
  60. last_result:
  61.     .long 0
  62.  
  63.     /* Code goes into .text section */
  64.     .text
  65.     .global entry
  66. entry:
  67.     /* increment sample counter */
  68.     move r3, sample_counter
  69.     ld r2, r3, 0
  70.     add r2, r2, 1
  71.     st r2, r3, 0
  72.  
  73.     /* do measurements using ADC */
  74.     /* r0 will be used as accumulator */
  75.     move r0, 0
  76.     /* initialize the loop counter */
  77.     stage_rst
  78. measure:
  79.  
  80.     /* measure and add value to accumulator */
  81.     adc r1, 0, adc_channel + 1
  82.     add r0, r0, r1
  83.     /* increment loop counter and check exit condition */
  84.     stage_inc 1
  85.     jumps measure, adc_oversampling_factor, lt
  86.  
  87.     /* divide accumulator by adc_oversampling_factor.
  88.        Since it is chosen as a power of two, use right shift */
  89.     rsh r0, r0, adc_oversampling_factor_log
  90.     /* averaged value is now in r0; store it into last_result */
  91.     move r3, last_result
  92.     st r0, r3, 0
  93.  
  94.     /* compare with high_thr; wake up if value > high_thr */
  95.     move r3, high_thr
  96.     ld r3, r3, 0
  97.     sub r3, r3, r2
  98.     jump wake_up, ov
  99.  
  100.     /* value within range, end the program */
  101.     .global exit
  102. exit:
  103.     halt
  104.  
  105.     .global wake_up
  106. wake_up:
  107.     /* Check if the system can be woken up */
  108.     READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
  109.     and r0, r0, 1
  110.     jump exit, eq
  111.  
  112.     /* Wake up the SoC, end program */
  113.     wake
  114.     WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
  115.     halt
  116.  
where the high threshold was changed to 1, so that the program only runs once, and used an oversampling factor of 4096. The problem is that the measurements reported by the esp32 using this program...
  1.        
  2.  ulp_last_result &= UINT16_MAX;
  3.  printf("Value=%d\n", ulp_last_result);
are pretty unstable/unreliable, as in I am getting +/- 70 decimal values each time I run the ulp program. If i measure using the one-shot method and oversample by the same rate, I get a much more reliable reading, around +/- 2 decimal points. It is important to me however, that the esp32 reads values using the ulp mode, if it is at all possible, reliable.

Am I doing something very obviously wrong in my assembly code? This is my first time getting my hands dirty with assembly.

kanuz_khan
Posts: 2
Joined: Wed Feb 07, 2024 7:27 am

Re: ADC measurements with ulp-fsm are unreliable.

Postby kanuz_khan » Fri Feb 23, 2024 7:04 am

Did you find a solution to this, I'm facing similar issue where adc gives random values

MicroController
Posts: 1707
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: ADC measurements with ulp-fsm are unreliable.

Postby MicroController » Fri Feb 23, 2024 10:19 pm

This piece of code:

Code: Select all

    .set adc_oversampling_factor_log, 0x1000
    .set adc_oversampling_factor, (1 << adc_oversampling_factor_log)
tries to set the number of samples to take (adc_oversampling_factor) to 2**4096 which doesn't make sense. You need a number less than 2**16 for the ULP to be able to handle it.
Then, because the ULP's registers are only 16 bits wide, the accumulated sum of the ADC samples cannot exceed (2**16)-1=65535, so with an ADC range of 12 bits (0...4095) you cannot accumulate more than 16 samples without risking an overflow/wrap-around.

Who is online

Users browsing this forum: No registered users and 121 guests