ulp: add build system integration and example
This commit is contained in:
committed by
Ivan Grokhotkov
parent
573cc7d36f
commit
a6e4e89592
@@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
#
|
||||
# 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/, \
|
||||
pulse_cnt.S \
|
||||
)
|
||||
#
|
||||
# 3. List all the component object files which include automatically
|
||||
# generated ULP export file, $(ULP_APP_NAME).h:
|
||||
ULP_EXP_DEP_OBJECTS := ulp_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.
|
||||
#
|
||||
@@ -0,0 +1,135 @@
|
||||
/* ULP Example: pulse counting
|
||||
|
||||
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 checks
|
||||
the input on GPIO0. If the value is different from the previous one, the
|
||||
program "debounces" the input: on the next debounce_max_count wake ups,
|
||||
it expects to see the same value of input.
|
||||
If this condition holds true, the program increments edge_count and starts
|
||||
waiting for input signal polarity to change again.
|
||||
When the edge counter reaches certain value (set by the main program),
|
||||
this program running triggers a wake up 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/rtc_io_reg.h"
|
||||
#include "soc/soc_ulp.h"
|
||||
|
||||
/* Define variables, which go into .bss section (zero-initialized data) */
|
||||
.bss
|
||||
/* Next input signal edge expected: 0 (negative) or 1 (positive) */
|
||||
.global next_edge
|
||||
next_edge:
|
||||
.long 0
|
||||
|
||||
/* Counter started when signal value changes.
|
||||
Edge is "debounced" when the counter reaches zero. */
|
||||
.global debounce_counter
|
||||
debounce_counter:
|
||||
.long 0
|
||||
|
||||
/* Value to which debounce_counter gets reset.
|
||||
Set by the main program. */
|
||||
.global debounce_max_count
|
||||
debounce_max_count:
|
||||
.long 0
|
||||
|
||||
/* Total number of signal edges acquired */
|
||||
.global edge_count
|
||||
edge_count:
|
||||
.long 0
|
||||
|
||||
/* Number of edges to acquire before waking up the SoC.
|
||||
Set by the main program. */
|
||||
.global edge_count_to_wake_up
|
||||
edge_count_to_wake_up:
|
||||
.long 0
|
||||
|
||||
/* RTC IO number used to sample the input signal.
|
||||
Set by main program. */
|
||||
.global io_number
|
||||
io_number:
|
||||
.long 0
|
||||
|
||||
/* Code goes into .text section */
|
||||
.text
|
||||
.global entry
|
||||
entry:
|
||||
/* Read the value of lower 16 RTC IOs into R0 */
|
||||
READ_RTC_FIELD(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT)
|
||||
/* Load io_number, extract the state of input */
|
||||
move r3, io_number
|
||||
ld r3, r3, 0
|
||||
rsh r0, r0, r3
|
||||
and r0, r0, 1
|
||||
/* State of input changed? */
|
||||
move r3, next_edge
|
||||
ld r3, r3, 0
|
||||
add r3, r0, r3
|
||||
and r3, r3, 1
|
||||
jump changed, eq
|
||||
/* Not changed */
|
||||
/* Reset debounce_counter to debounce_max_count */
|
||||
move r3, debounce_max_count
|
||||
move r2, debounce_counter
|
||||
ld r3, r3, 0
|
||||
st r3, r2, 0
|
||||
/* End program */
|
||||
halt
|
||||
|
||||
.global changed
|
||||
changed:
|
||||
/* Input state changed */
|
||||
/* Has debounce_counter reached zero? */
|
||||
move r3, debounce_counter
|
||||
ld r2, r3, 0
|
||||
add r2, r2, 0 /* dummy ADD to use "jump if ALU result is zero" */
|
||||
jump edge_detected, eq
|
||||
/* Not yet. Decrement debounce_counter */
|
||||
sub r2, r2, 1
|
||||
st r2, r3, 0
|
||||
/* End program */
|
||||
halt
|
||||
|
||||
.global edge_detected
|
||||
edge_detected:
|
||||
/* Reset debounce_counter to debounce_max_count */
|
||||
move r3, debounce_max_count
|
||||
move r2, debounce_counter
|
||||
ld r3, r3, 0
|
||||
st r3, r2, 0
|
||||
/* Flip next_edge */
|
||||
move r3, next_edge
|
||||
ld r2, r3, 0
|
||||
add r2, r2, 1
|
||||
and r2, r2, 1
|
||||
st r2, r3, 0
|
||||
/* Increment edge_count */
|
||||
move r3, edge_count
|
||||
ld r2, r3, 0
|
||||
add r2, r2, 1
|
||||
st r2, r3, 0
|
||||
/* Compare edge_count to edge_count_to_wake_up */
|
||||
move r3, edge_count_to_wake_up
|
||||
ld r3, r3, 0
|
||||
sub r3, r3, r2
|
||||
jump wake_up, eq
|
||||
/* Not yet. End program */
|
||||
halt
|
||||
|
||||
.global wake_up
|
||||
wake_up:
|
||||
/* Wake up the SoC, end program */
|
||||
wake
|
||||
halt
|
||||
@@ -0,0 +1,106 @@
|
||||
/* 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 "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 "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");
|
||||
|
||||
static void init_ulp_program();
|
||||
static void update_pulse_count();
|
||||
|
||||
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, initializing ULP\n");
|
||||
init_ulp_program();
|
||||
} else {
|
||||
printf("ULP wakeup, saving pulse count\n");
|
||||
update_pulse_count();
|
||||
}
|
||||
|
||||
printf("Entering deep sleep\n\n");
|
||||
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);
|
||||
|
||||
/* Initialize some variables used by ULP program.
|
||||
* Each 'ulp_xyz' variable corresponds to 'xyz' variable in the ULP program.
|
||||
* These variables are declared in an auto generated header file,
|
||||
* 'ulp_main.h', name of this file is defined in component.mk as ULP_APP_NAME.
|
||||
* These variables are located in RTC_SLOW_MEM and can be accessed both by the
|
||||
* ULP and the main CPUs.
|
||||
*
|
||||
* Note that the ULP reads only the lower 16 bits of these variables.
|
||||
*/
|
||||
ulp_debounce_counter = 3;
|
||||
ulp_debounce_max_count = 3;
|
||||
ulp_next_edge = 0;
|
||||
ulp_io_number = 11; /* GPIO0 is RTC_IO 11 */
|
||||
ulp_edge_count_to_wake_up = 10;
|
||||
|
||||
/* Initialize GPIO0 as RTC IO, input, disable pullup and pulldown */
|
||||
gpio_num_t gpio_num = GPIO_NUM_0;
|
||||
rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_dis(gpio_num);
|
||||
rtc_gpio_pullup_dis(gpio_num);
|
||||
rtc_gpio_hold_en(gpio_num);
|
||||
|
||||
/* Set ULP wake up period to T = 20ms (3095 cycles of RTC_SLOW_CLK clock).
|
||||
* Minimum pulse width has to be T * (ulp_debounce_counter + 1) = 80ms.
|
||||
*/
|
||||
REG_SET_FIELD(SENS_ULP_CP_SLEEP_CYC0_REG, SENS_SLEEP_CYCLES_S0, 3095);
|
||||
|
||||
/* Start the program */
|
||||
err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
static void update_pulse_count()
|
||||
{
|
||||
const char* namespace = "plusecnt";
|
||||
const char* count_key = "count";
|
||||
|
||||
ESP_ERROR_CHECK( nvs_flash_init() );
|
||||
nvs_handle handle;
|
||||
ESP_ERROR_CHECK( nvs_open(namespace, NVS_READWRITE, &handle));
|
||||
uint32_t pulse_count = 0;
|
||||
esp_err_t err = nvs_get_u32(handle, count_key, &pulse_count);
|
||||
assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND);
|
||||
printf("Read pulse count from NVS: %5d\n", pulse_count);
|
||||
|
||||
/* ULP program counts signal edges, convert that to the number of pulses */
|
||||
uint32_t pulse_count_from_ulp = (ulp_edge_count & UINT16_MAX) / 2;
|
||||
/* In case of an odd number of edges, keep one until next time */
|
||||
ulp_edge_count = ulp_edge_count % 2;
|
||||
printf("Pulse count from ULP: %5d\n", pulse_count_from_ulp);
|
||||
|
||||
/* Save the new pulse count to NVS */
|
||||
pulse_count += pulse_count_from_ulp;
|
||||
ESP_ERROR_CHECK(nvs_set_u32(handle, count_key, pulse_count));
|
||||
ESP_ERROR_CHECK(nvs_commit(handle));
|
||||
nvs_close(handle);
|
||||
printf("Wrote updated pulse count to NVS: %5d\n", pulse_count);
|
||||
}
|
||||
Reference in New Issue
Block a user