mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-19 17:35:54 +08:00
添加智能灯固件代码
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ulp-example)
|
||||
@@ -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-example
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# ULP Pulse Counting Example
|
||||
|
||||
This example demonstrates how to program the ULP coprocessor to count pulses on an IO while the main CPUs are either running some other code or are in deep sleep. See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
|
||||
ULP program written in assembly can be found across `ulp/pulse_cnt.S` and `ulp/wake_up.S` (demonstrating multiple ULP source files). The build system assembles and links this program, converts it into binary format, and embeds it into the .rodata section of the ESP-IDF application.
|
||||
|
||||
At runtime, the main code running on the ESP32 (found in main.c) loads ULP program into the `RTC_SLOW_MEM` memory region using `ulp_load_binary` function. Main code configures the ULP program by setting up values of some variables and then starts it using `ulp_run`. Once the ULP program is started, it runs periodically, with the period set by the main program. The main program enables ULP wakeup source and puts the chip into deep sleep mode.
|
||||
|
||||
When the ULP program finds an edge in the input signal, it performs debouncing and increments the variable maintaining the total edge count. Once the edge count reaches certain value (set by the main program), ULP triggers wake up from deep sleep. Note that the ULP program keeps running and monitoring the input signal even when the SoC is woken up.
|
||||
|
||||
Upon wakeup, the main program saves total edge count into NVS and returns to deep sleep.
|
||||
|
||||
In this example the input signal is connected to GPIO0. Note that this pin was chosen because most development boards have a button connected to it, so the pulses to be counted can be generated by pressing the button. For real world applications this is not a good choice of a pin, because GPIO0 also acts as a bootstrapping pin. To change the pin number, check the ESP32 Chip Pin List document and adjust `gpio_num` and `ulp_io_number` variables in main.c.
|
||||
|
||||
In this example, the `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` Kconfig option is used, which allows you to reduce the boot time of the bootloader during waking up from deep sleep. The bootloader stores in rtc memory the address of a running partition and uses it when it wakes up. This example allows you to skip all image checks and speed up the boot.
|
||||
|
||||
## Example output
|
||||
|
||||
```
|
||||
Not ULP wakeup, initializing ULP
|
||||
Entering deep sleep
|
||||
|
||||
ULP wakeup, saving pulse count
|
||||
Read pulse count from NVS: 384
|
||||
Pulse count from ULP: 5
|
||||
Wrote updated pulse count to NVS: 389
|
||||
Entering deep sleep
|
||||
|
||||
ULP wakeup, saving pulse count
|
||||
Read pulse count from NVS: 389
|
||||
Pulse count from ULP: 5
|
||||
Wrote updated pulse count to NVS: 394
|
||||
Entering deep sleep
|
||||
|
||||
ULP wakeup, saving pulse count
|
||||
Read pulse count from NVS: 394
|
||||
Pulse count from ULP: 6
|
||||
Wrote updated pulse count to NVS: 400
|
||||
Entering deep sleep
|
||||
|
||||
ULP wakeup, saving pulse count
|
||||
Read pulse count from NVS: 400
|
||||
Pulse count from ULP: 5
|
||||
Wrote updated pulse count to NVS: 405
|
||||
Entering deep sleep
|
||||
```
|
||||
|
||||
Note that in one case the pulse count captured by the ULP program is 6, even though the `edge_count_to_wake_up` variable is set to 10 by the main program. This shows that the ULP program keeps track of pulses while the main CPUs are starting up, so when pulses are sent rapidly it is possible to register more pulses between wake up and entry into app_main.
|
||||
|
||||
With the default configuration (20ms ULP wakeup period), average current consumption in deep sleep mode is 16uA.
|
||||
@@ -0,0 +1,43 @@
|
||||
from __future__ import unicode_literals
|
||||
from tiny_test_fw import Utility
|
||||
import re
|
||||
import time
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_ulp(env, extra_data):
|
||||
|
||||
dut = env.get_dut('ulp', 'examples/system/ulp')
|
||||
dut.start_app()
|
||||
|
||||
dut.expect_all('Not ULP wakeup, initializing ULP',
|
||||
'Entering deep sleep',
|
||||
timeout=30)
|
||||
|
||||
def generate_gpio0_events():
|
||||
for _ in range(5):
|
||||
dut.port_inst.setDTR(True) # Pulling GPIO0 low using DTR
|
||||
time.sleep(0.25)
|
||||
dut.port_inst.setDTR(False)
|
||||
time.sleep(0.25)
|
||||
|
||||
nvs_value = None
|
||||
for _ in range(5):
|
||||
generate_gpio0_events()
|
||||
dut.expect('ULP wakeup, saving pulse count', timeout=5)
|
||||
Utility.console_log('Woke up...')
|
||||
init_count = int(dut.expect(re.compile(r'Read pulse count from NVS:\s+(\d+)'), timeout=5)[0], 10)
|
||||
assert nvs_value in (init_count, None), ('Read count is {} and previously written value is {}'
|
||||
''.format(init_count, nvs_value))
|
||||
inc = int(dut.expect(re.compile(r'Pulse count from ULP:\s+(\d+)'), timeout=5)[0], 10)
|
||||
assert inc in (5, 6), 'pulse count is {}'.format(inc)
|
||||
new_count = int(dut.expect(re.compile(r'Wrote updated pulse count to NVS:\s+(\d+)'), timeout=5)[0], 10)
|
||||
assert init_count + inc == new_count, '{} + {} != {}'.format(init_count, inc, new_count)
|
||||
nvs_value = new_count
|
||||
Utility.console_log('Pulse count written to NVS: {}. Entering deep sleep...'.format(nvs_value))
|
||||
dut.expect('Entering deep sleep', timeout=5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_ulp()
|
||||
@@ -0,0 +1,21 @@
|
||||
idf_component_register(SRCS "ulp_example_main.c"
|
||||
INCLUDE_DIRS ""
|
||||
REQUIRES soc nvs_flash ulp)
|
||||
#
|
||||
# ULP support additions to component CMakeLists.txt.
|
||||
#
|
||||
# 1. The ULP app name must be unique (if multiple components use ULP).
|
||||
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||
#
|
||||
# 2. Specify all assembly source files.
|
||||
# Files should be placed into a separate directory (in this case, ulp/),
|
||||
# which should not be added to COMPONENT_SRCS.
|
||||
set(ulp_s_sources "ulp/pulse_cnt.S" "ulp/wake_up.S")
|
||||
#
|
||||
# 3. List all the component source files which include automatically
|
||||
# generated ULP export file, ${ulp_app_name}.h:
|
||||
set(ulp_exp_dep_srcs "ulp_example_main.c")
|
||||
#
|
||||
# 4. Call function to build ULP binary and embed in project using the argument
|
||||
# values above.
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}")
|
||||
@@ -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 wake_up.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,146 @@
|
||||
/* 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:
|
||||
/* Load io_number */
|
||||
move r3, io_number
|
||||
ld r3, r3, 0
|
||||
|
||||
/* Lower 16 IOs and higher need to be handled separately,
|
||||
* because r0-r3 registers are 16 bit wide.
|
||||
* Check which IO this is.
|
||||
*/
|
||||
move r0, r3
|
||||
jumpr read_io_high, 16, ge
|
||||
|
||||
/* Read the value of lower 16 RTC IOs into R0 */
|
||||
READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S, 16)
|
||||
rsh r0, r0, r3
|
||||
jump read_done
|
||||
|
||||
/* Read the value of RTC IOs 16-17, into R0 */
|
||||
read_io_high:
|
||||
READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 16, 2)
|
||||
sub r3, r3, 16
|
||||
rsh r0, r0, r3
|
||||
|
||||
read_done:
|
||||
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
|
||||
@@ -0,0 +1,16 @@
|
||||
/* 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"
|
||||
|
||||
.global wake_up
|
||||
wake_up:
|
||||
/* Check if the system can be woken up */
|
||||
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
|
||||
and r0, r0, 1
|
||||
jump wake_up, eq
|
||||
|
||||
/* Wake up the SoC, end program */
|
||||
wake
|
||||
halt
|
||||
@@ -0,0 +1,120 @@
|
||||
/* 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_sleep.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "soc/rtc_periph.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(void);
|
||||
static void update_pulse_count(void);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
||||
if (cause != ESP_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_sleep_enable_ulp_wakeup() );
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
static void init_ulp_program(void)
|
||||
{
|
||||
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);
|
||||
|
||||
/* GPIO used for pulse counting. */
|
||||
gpio_num_t gpio_num = GPIO_NUM_0;
|
||||
int rtcio_num = rtc_io_number_get(gpio_num);
|
||||
assert(rtc_gpio_is_valid_gpio(gpio_num) && "GPIO used for pulse counting must be an RTC IO");
|
||||
|
||||
/* 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 = rtcio_num; /* map from GPIO# to RTC_IO# */
|
||||
ulp_edge_count_to_wake_up = 10;
|
||||
|
||||
/* Initialize selected GPIO as RTC IO, enable input, disable pullup and pulldown */
|
||||
rtc_gpio_init(gpio_num);
|
||||
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);
|
||||
|
||||
/* Disconnect GPIO12 and GPIO15 to remove current drain through
|
||||
* pullup/pulldown resistors.
|
||||
* GPIO12 may be pulled high to select flash voltage.
|
||||
*/
|
||||
rtc_gpio_isolate(GPIO_NUM_12);
|
||||
rtc_gpio_isolate(GPIO_NUM_15);
|
||||
esp_deep_sleep_disable_rom_logging(); // suppress boot messages
|
||||
|
||||
/* Set ULP wake up period to T = 20ms.
|
||||
* Minimum pulse width has to be T * (ulp_debounce_counter + 1) = 80ms.
|
||||
*/
|
||||
ulp_set_wakeup_period(0, 20000);
|
||||
|
||||
/* Start the program */
|
||||
err = ulp_run(&ulp_entry - RTC_SLOW_MEM);
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
static void update_pulse_count(void)
|
||||
{
|
||||
const char* namespace = "plusecnt";
|
||||
const char* count_key = "count";
|
||||
|
||||
ESP_ERROR_CHECK( nvs_flash_init() );
|
||||
nvs_handle_t 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);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Enable ULP
|
||||
CONFIG_ESP32_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=1024
|
||||
# Set log level to Warning to produce clean output
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL=2
|
||||
CONFIG_LOG_DEFAULT_LEVEL_WARN=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL=2
|
||||
CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y
|
||||
Reference in New Issue
Block a user