mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
examples: add ULP ADC example
This commit is contained in:
parent
1e0710f1b2
commit
98e15df7f6
@ -109,6 +109,17 @@ esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten);
|
||||
*/
|
||||
int adc1_get_voltage(adc1_channel_t channel);
|
||||
|
||||
/**
|
||||
* @brief Configure ADC1 to be usable by the ULP
|
||||
*
|
||||
* This function reconfigures ADC1 to be controlled by the ULP.
|
||||
* Effect of this function can be reverted using adc1_get_voltage function.
|
||||
*
|
||||
* Note that adc1_config_channel_atten, adc1_config_width functions need
|
||||
* to be called to configure ADC1 channels, before ADC1 is used by the ULP.
|
||||
*/
|
||||
void adc1_ulp_enable();
|
||||
|
||||
/**
|
||||
* @brief Read Hall Sensor
|
||||
*
|
||||
|
@ -604,6 +604,20 @@ int adc1_get_voltage(adc1_channel_t channel)
|
||||
return adc_value;
|
||||
}
|
||||
|
||||
void adc1_ulp_enable(void)
|
||||
{
|
||||
portENTER_CRITICAL(&rtc_spinlock);
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_START_FORCE);
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_SAR1_EN_PAD_FORCE_M);
|
||||
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_AMP, 0x2, SENS_FORCE_XPD_AMP_S);
|
||||
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S);
|
||||
SET_PERI_REG_BITS(SENS_SAR_MEAS_CTRL_REG, 0xfff, 0x0, SENS_AMP_RST_FB_FSM_S); //[11:8]:short ref ground, [7:4]:short ref, [3:0]:rst fb
|
||||
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT1_REG, SENS_SAR_AMP_WAIT1, 0x1, SENS_SAR_AMP_WAIT1_S);
|
||||
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT1_REG, SENS_SAR_AMP_WAIT2, 0x1, SENS_SAR_AMP_WAIT2_S);
|
||||
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_SAR_AMP_WAIT3, 0x1, SENS_SAR_AMP_WAIT3_S);
|
||||
portEXIT_CRITICAL(&rtc_spinlock);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
DAC
|
||||
---------------------------------------------------------------*/
|
||||
|
@ -432,6 +432,7 @@
|
||||
#define RTC_CNTL_MIN_SLP_VAL_M ((RTC_CNTL_MIN_SLP_VAL_V)<<(RTC_CNTL_MIN_SLP_VAL_S))
|
||||
#define RTC_CNTL_MIN_SLP_VAL_V 0xFF
|
||||
#define RTC_CNTL_MIN_SLP_VAL_S 8
|
||||
#define RTC_CNTL_MIN_SLP_VAL_MIN 2
|
||||
/* RTC_CNTL_ULP_CP_SUBTIMER_PREDIV : R/W ;bitpos:[7:0] ;default: 8'd1 ; */
|
||||
/*description: */
|
||||
#define RTC_CNTL_ULP_CP_SUBTIMER_PREDIV 0x000000FF
|
||||
|
@ -882,6 +882,23 @@ esp_err_t ulp_load_binary(uint32_t load_addr, const uint8_t* program_binary, siz
|
||||
*/
|
||||
esp_err_t ulp_run(uint32_t entry_point);
|
||||
|
||||
/**
|
||||
* @brief Set one of ULP wakeup period values
|
||||
*
|
||||
* ULP coprocessor starts running the program when the wakeup timer counts up
|
||||
* to a given value (called period). There are 5 period values which can be
|
||||
* programmed into SENS_ULP_CP_SLEEP_CYCx_REG registers, x = 0..4.
|
||||
* By default, wakeup timer will use the period set into SENS_ULP_CP_SLEEP_CYC0_REG,
|
||||
* i.e. period number 0. ULP program code can use SLEEP instruction to select
|
||||
* which of the SENS_ULP_CP_SLEEP_CYCx_REG should be used for subsequent wakeups.
|
||||
*
|
||||
* @param period_index wakeup period setting number (0 - 4)
|
||||
* @param period_us wakeup period, us
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if period_index is out of range
|
||||
*/
|
||||
esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -19,9 +19,11 @@
|
||||
#include "esp_attr.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_clk.h"
|
||||
#include "esp32/ulp.h"
|
||||
|
||||
#include "soc/soc.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/sens_reg.h"
|
||||
|
||||
@ -46,9 +48,11 @@ esp_err_t ulp_run(uint32_t entry_point)
|
||||
// wait for at least 1 RTC_SLOW_CLK cycle
|
||||
ets_delay_us(10);
|
||||
// set entry point
|
||||
SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_PC_INIT_V, entry_point, SENS_PC_INIT_S);
|
||||
REG_SET_FIELD(SENS_SAR_START_FORCE_REG, SENS_PC_INIT, entry_point);
|
||||
// disable force start
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M);
|
||||
// set time until wakeup is allowed to the smallest possible
|
||||
REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_MIN_SLP_VAL, RTC_CNTL_MIN_SLP_VAL_MIN);
|
||||
// make sure voltage is raised when RTC 8MCLK is enabled
|
||||
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M);
|
||||
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_CORE_FOLW_8M);
|
||||
@ -100,3 +104,15 @@ esp_err_t ulp_load_binary(uint32_t load_addr, const uint8_t* program_binary, siz
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us)
|
||||
{
|
||||
if (period_index > 4) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint64_t period_us_64 = period_us;
|
||||
uint64_t period_cycles = (period_us_64 << RTC_CLK_CAL_FRACT) / esp_clk_slowclk_cal_get();
|
||||
REG_SET_FIELD(SENS_ULP_CP_SLEEP_CYC0_REG + period_index * sizeof(uint32_t),
|
||||
SENS_SLEEP_CYCLES_S0, (uint32_t) period_cycles);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -149,9 +149,13 @@ Declaration of the entry point symbol comes from the above mentioned generated h
|
||||
ULP program flow
|
||||
----------------
|
||||
|
||||
ULP coprocessor is started by a timer. The timer is started once ``ulp_run`` is called. The timer counts a number of RTC_SLOW_CLK ticks (by default, produced by an internal 150kHz RC oscillator). The number of ticks is set using ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers (x = 0..4). When starting the ULP for the first time, ``SENS_ULP_CP_SLEEP_CYC0_REG`` will be used to obtain the number of timer ticks. Later the ULP program can select another ``SENS_ULP_CP_SLEEP_CYCx_REG`` register using ``sleep`` instruction.
|
||||
ULP coprocessor is started by a timer. The timer is started once ``ulp_run`` is called. The timer counts a number of RTC_SLOW_CLK ticks (by default, produced by an internal 150kHz RC oscillator). The number of ticks is set using ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers (x = 0..4). When starting the ULP for the first time, ``SENS_ULP_CP_SLEEP_CYC0_REG`` will be used to set the number of timer ticks. Later the ULP program can select another ``SENS_ULP_CP_SLEEP_CYCx_REG`` register using ``sleep`` instruction.
|
||||
|
||||
Once the timer counts the number of ticks set by the selected ``SENS_ULP_CP_SLEEP_CYCx_REG`` register, ULP coprocessor powers up and starts running the program from the entry point set in the call to ``ulp_run``.
|
||||
The application can set ULP timer period values (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4) using ``ulp_wakeup_period_set`` function.
|
||||
|
||||
.. doxygenfunction:: ulp_set_wakeup_period
|
||||
|
||||
Once the timer counts the number of ticks set in the selected ``SENS_ULP_CP_SLEEP_CYCx_REG`` register, ULP coprocessor powers up and starts running the program from the entry point set in the call to ``ulp_run``.
|
||||
|
||||
The program runs until it encounters a ``halt`` instruction or an illegal instruction. Once the program halts, ULP coprocessor powers down, and the timer is started again.
|
||||
|
||||
|
9
examples/system/ulp_adc/Makefile
Normal file
9
examples/system/ulp_adc/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := ulp-adc-example
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
36
examples/system/ulp_adc/README.md
Normal file
36
examples/system/ulp_adc/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# ULP ADC Example
|
||||
|
||||
This example demonstrates how to use the ULP coprocessor to poll ADC in deep sleep.
|
||||
|
||||
ULP program periodically measures the input voltage on GPIO34. The voltage is compared to two thresholds. If the voltage is less than the low threshold, or higher than the high threshold, ULP wakes up the system.
|
||||
|
||||
By default, thresholds are set to 1.35V and 1.75V, approximately.
|
||||
|
||||
Average current drawn by the ESP32 in this example with the default configuration (10Hz measurement period, 4x averaging) is 80 uA.
|
||||
|
||||
## Example output
|
||||
|
||||
Below is the output from this example. GPIO15 is pulled down to ground to supress output from ROM bootloader.
|
||||
|
||||
```
|
||||
Not ULP wakeup
|
||||
Entering deep sleep
|
||||
|
||||
Deep sleep wakeup
|
||||
ULP did 1531 measurements
|
||||
Thresholds: low=1500 high=2000
|
||||
Value=973 was below threshold
|
||||
Entering deep sleep
|
||||
|
||||
Deep sleep wakeup
|
||||
ULP did 2 measurements
|
||||
Thresholds: low=1500 high=2000
|
||||
Value=0 was below threshold
|
||||
Entering deep sleep
|
||||
|
||||
Deep sleep wakeup
|
||||
ULP did 2 measurements
|
||||
Thresholds: low=1500 high=2000
|
||||
Value=0 was below threshold
|
||||
Entering deep sleep
|
||||
```
|
23
examples/system/ulp_adc/main/component.mk
Normal file
23
examples/system/ulp_adc/main/component.mk
Normal file
@ -0,0 +1,23 @@
|
||||
#
|
||||
# ULP support additions to component makefile.
|
||||
#
|
||||
# 1. ULP_APP_NAME must be unique (if multiple components use ULP)
|
||||
# Default value, override if necessary:
|
||||
ULP_APP_NAME ?= ulp_$(COMPONENT_NAME)
|
||||
#
|
||||
# 2. Specify all assembly source files here.
|
||||
# Files should be placed into a separate directory (in this case, ulp/),
|
||||
# which should not be added to COMPONENT_SRCDIRS.
|
||||
ULP_S_SOURCES = $(addprefix $(COMPONENT_PATH)/ulp/, \
|
||||
adc.S \
|
||||
)
|
||||
#
|
||||
# 3. List all the component object files which include automatically
|
||||
# generated ULP export file, $(ULP_APP_NAME).h:
|
||||
ULP_EXP_DEP_OBJECTS := ulp_adc_example_main.o
|
||||
#
|
||||
# 4. Include build rules for ULP program
|
||||
include $(IDF_PATH)/components/ulp/component_ulp_common.mk
|
||||
#
|
||||
# End of ULP support additions to component makefile.
|
||||
#
|
115
examples/system/ulp_adc/main/ulp/adc.S
Normal file
115
examples/system/ulp_adc/main/ulp/adc.S
Normal file
@ -0,0 +1,115 @@
|
||||
/* ULP Example: using ADC in deep sleep
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
This file contains assembly code which runs on the ULP.
|
||||
|
||||
ULP wakes up to run this code at a certain period, determined by the values
|
||||
in SENS_ULP_CP_SLEEP_CYCx_REG registers. On each wake up, the program
|
||||
measures input voltage on the given ADC channel 'adc_oversampling_factor'
|
||||
times. Measurements are accumulated and average value is calculated.
|
||||
Average value is compared to the two thresholds: 'low_thr' and 'high_thr'.
|
||||
If the value is less than 'low_thr' or more than 'high_thr', ULP wakes up
|
||||
the chip from deep sleep.
|
||||
*/
|
||||
|
||||
/* ULP assembly files are passed through C preprocessor first, so include directives
|
||||
and C macros may be used in these files
|
||||
*/
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/soc_ulp.h"
|
||||
|
||||
/* ADC1 channel 6, GPIO34 */
|
||||
.set adc_channel, 6
|
||||
|
||||
/* Configure the number of ADC samples to average on each measurement.
|
||||
For convenience, make it a power of 2. */
|
||||
.set adc_oversampling_factor_log, 2
|
||||
.set adc_oversampling_factor, (1 << adc_oversampling_factor_log)
|
||||
|
||||
/* Define variables, which go into .bss section (zero-initialized data) */
|
||||
.bss
|
||||
|
||||
/* Low threshold of ADC reading.
|
||||
Set by the main program. */
|
||||
.global low_thr
|
||||
low_thr:
|
||||
.long 0
|
||||
|
||||
/* High threshold of ADC reading.
|
||||
Set by the main program. */
|
||||
.global high_thr
|
||||
high_thr:
|
||||
.long 0
|
||||
|
||||
/* Counter of measurements done */
|
||||
.global sample_counter
|
||||
sample_counter:
|
||||
.long 0
|
||||
|
||||
.global last_result
|
||||
last_result:
|
||||
.long 0
|
||||
|
||||
/* Code goes into .text section */
|
||||
.text
|
||||
.global entry
|
||||
entry:
|
||||
/* increment sample counter */
|
||||
move r3, sample_counter
|
||||
ld r2, r3, 0
|
||||
add r2, r2, 1
|
||||
st r2, r3, 0
|
||||
|
||||
/* do measurements using ADC */
|
||||
/* r0 will be used as accumulator */
|
||||
move r0, 0
|
||||
/* initialize the loop counter */
|
||||
stage_rst
|
||||
measure:
|
||||
/* measure and add value to accumulator */
|
||||
adc r1, 0, adc_channel + 1, 0
|
||||
add r0, r0, r1
|
||||
/* increment loop counter and check exit condition */
|
||||
stage_inc 1
|
||||
jumps measure, adc_oversampling_factor, lt
|
||||
|
||||
/* divide accumulator by adc_oversampling_factor.
|
||||
Since it is chosen as a power of two, use right shift */
|
||||
rsh r0, r0, adc_oversampling_factor_log
|
||||
/* averaged value is now in r0; store it into last_result */
|
||||
move r3, last_result
|
||||
st r0, r3, 0
|
||||
|
||||
/* compare with low_thr; wake up if value < low_thr */
|
||||
move r3, low_thr
|
||||
ld r3, r3, 0
|
||||
sub r3, r0, r3
|
||||
jump wake_up, ov
|
||||
|
||||
/* compare with high_thr; wake up if value > high_thr */
|
||||
move r3, high_thr
|
||||
ld r3, r3, 0
|
||||
sub r3, r3, r0
|
||||
jump wake_up, ov
|
||||
|
||||
/* value within range, end the program */
|
||||
.global exit
|
||||
exit:
|
||||
halt
|
||||
|
||||
.global wake_up
|
||||
wake_up:
|
||||
/* Check if the system can be woken up */
|
||||
READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1)
|
||||
and r0, r0, 1
|
||||
jump exit, eq
|
||||
|
||||
/* Wake up the SoC, end program */
|
||||
wake
|
||||
WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
|
||||
halt
|
86
examples/system/ulp_adc/main/ulp_adc_example_main.c
Normal file
86
examples/system/ulp_adc/main/ulp_adc_example_main.c
Normal file
@ -0,0 +1,86 @@
|
||||
/* ULP Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_deep_sleep.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rtc_io.h"
|
||||
#include "driver/adc.h"
|
||||
#include "driver/dac.h"
|
||||
#include "esp32/ulp.h"
|
||||
#include "ulp_main.h"
|
||||
|
||||
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
|
||||
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
|
||||
|
||||
/* This function is called once after power-on reset, to load ULP program into
|
||||
* RTC memory and configure the ADC.
|
||||
*/
|
||||
static void init_ulp_program();
|
||||
|
||||
/* This function is called every time before going into deep sleep.
|
||||
* It starts the ULP program and resets measurement counter.
|
||||
*/
|
||||
static void start_ulp_program();
|
||||
|
||||
void app_main()
|
||||
{
|
||||
esp_deep_sleep_wakeup_cause_t cause = esp_deep_sleep_get_wakeup_cause();
|
||||
if (cause != ESP_DEEP_SLEEP_WAKEUP_ULP) {
|
||||
printf("Not ULP wakeup\n");
|
||||
init_ulp_program();
|
||||
} else {
|
||||
printf("Deep sleep wakeup\n");
|
||||
printf("ULP did %d measurements since last reset\n", ulp_sample_counter & UINT16_MAX);
|
||||
printf("Thresholds: low=%d high=%d\n", ulp_low_thr, ulp_high_thr);
|
||||
ulp_last_result &= UINT16_MAX;
|
||||
printf("Value=%d was %s threshold\n", ulp_last_result,
|
||||
ulp_last_result < ulp_low_thr ? "below" : "above");
|
||||
}
|
||||
printf("Entering deep sleep\n\n");
|
||||
start_ulp_program();
|
||||
ESP_ERROR_CHECK( esp_deep_sleep_enable_ulp_wakeup() );
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
static void init_ulp_program()
|
||||
{
|
||||
esp_err_t err = ulp_load_binary(0, ulp_main_bin_start,
|
||||
(ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
/* Configure ADC channel */
|
||||
/* Note: when changing channel here, also change 'adc_channel' constant
|
||||
in adc.S */
|
||||
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_11db);
|
||||
adc1_config_width(ADC_WIDTH_12Bit);
|
||||
adc1_ulp_enable();
|
||||
|
||||
/* Set low and high thresholds, approx. 1.35V - 1.75V*/
|
||||
ulp_low_thr = 1500;
|
||||
ulp_high_thr = 2000;
|
||||
|
||||
/* Set ULP wake up period to 100ms */
|
||||
ulp_set_wakeup_period(0, 100000);
|
||||
}
|
||||
|
||||
static void start_ulp_program()
|
||||
{
|
||||
/* Reset sample counter */
|
||||
ulp_sample_counter = 0;
|
||||
|
||||
/* Start the program */
|
||||
esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
11
examples/system/ulp_adc/sdkconfig.defaults
Normal file
11
examples/system/ulp_adc/sdkconfig.defaults
Normal file
@ -0,0 +1,11 @@
|
||||
# Enable ULP
|
||||
CONFIG_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=1024
|
||||
# Some flash chips need extra time to wake up
|
||||
# Set this a bit higher to improve out-of-the-box experience
|
||||
CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=1000
|
||||
# Set log level to Warning to produce clean output
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL=2
|
||||
CONFIG_LOG_DEFAULT_LEVEL_WARN=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL=2
|
Loading…
Reference in New Issue
Block a user