Compare commits
2 Commits
0e2d6cb68e
...
2feb8fb184
Author | SHA1 | Date | |
---|---|---|---|
|
2feb8fb184 | ||
|
69c2b3e731 |
@ -0,0 +1,6 @@
|
||||
# The following four 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.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(external_power_control_example)
|
366
examples/power_management/external_power_control/README.md
Normal file
@ -0,0 +1,366 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
Note: In this example, the ESP32 only supports the GPIO Control
|
||||
|
||||
# External Power Control Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to use the [power management](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/power_management.html) function, through the [GPIO](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/gpio.html#gpio-rtc-gpio), [UART](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/uart.html#uart) control [power management lock](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/power_management.html) has realized the function of controlling the power consumption of the system, and provides a complete set of control logic.
|
||||
|
||||
1. The example supports GPIO-controlled power management and [auto light sleep](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/sleep_modes.html#api-reference) functions with two control modes.
|
||||
|
||||
- Level control: The GPIO input level always corresponds to the chip state.
|
||||
|
||||
![image-20230831151232303](image/LevelMode.png)
|
||||
|
||||
- Pulse control: Trigger a complete pulse once and the chip state will change.
|
||||
|
||||
![image-20230831151413645](image/PulseMode.png)
|
||||
|
||||
2. This example supports both UART-controlled power management and auto light sleep.
|
||||
|
||||
Note 1: The example can be configured via `menuconfig`. (See [Configure the project](##Configure the project) section)
|
||||
|
||||
Note 2: For an explanation of the example, see the [Example Breakdown](#Example Breakdown) section.
|
||||
|
||||
WARNING: This project uses power management features, so the [CONFIG_PM_ENABLE](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html#config-pm-enable) option must be enabled. In addition, auto light sleep mode is based on the [FreeRTOS Tickless idle](https://freertos.org/low-power-tickless-rtos.html) feature, so the [CONFIG_FREERTOS_USE_TICKLESS_IDLE](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html#config-freertos-use-tickless-idle) option must be enabled when using the auto light sleep feature via `esp_pm_configure()`, otherwise `esp_pm_configure()` will return an ESP_ERR_NOT_SUPPORTED error.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example will typically run on any popular ESP32 series development board.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py set-target esp32xx
|
||||
```
|
||||
|
||||
(Use specific soc model instead of xx)
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* GPIO Pin Configuration: `(Top) -> Example Configuration -> GPIO configuration -> Pin for gpio control`
|
||||
* GPIO control mode configuration: `(Top) -> Example Configuration -> GPIO configuration -> Enable GPIO pulse control`
|
||||
* GPIO control level configuration: `(Top) -> Example Configuration -> GPIO configuration -> Enable GPIO high-level control`
|
||||
* GPIO pull-up and pull-down configurations: `(Top) -> Example Configuration -> GPIO configuration -> Use internal pull up and pull down`
|
||||
* UART Configuration: `(Top) -> Example Configuration -> UART configuration -> Select the UART to use`
|
||||
* UART's tx pin configuration: `(Top) -> Example Configuration -> UART configuration -> Select the UART tx io`
|
||||
* UART's rx pin configuration: `(Top) -> Example Configuration -> UART configuration -> Select the UART rx io`
|
||||
* UART's cts pin configuration: `(Top) -> Example Configuration -> UART configuration -> Select the UART cts io`
|
||||
* UART's rts pin configuration: `(Top) -> Example Configuration -> UART configuration -> Select the UART rts io`
|
||||
* UART wakeup threshold configuration: `(Top) -> Example Configuration -> UART configuration -> UART wakeup threshold`
|
||||
* Timer Configuration: `(Top) -> Example Configuration -> UART configuration -> idle wait time (us)`
|
||||
* Power management lock configuration: `(Top) -> Example Configuration -> power manager configuration -> Set pm lock`
|
||||
* Power management configuration: `(Top) -> Example Configuration -> power manager configuration -> Set max freq in pm config`
|
||||
* Power management configuration: `(Top) -> Example Configuration -> power manager configuration -> Set min freq in pm config`
|
||||
|
||||
An explanation of the above configuration options can be viewed via `manuconfig`.
|
||||
|
||||
Note: All of the above configuration options have default values, you can build the project directly after setting the target development board using `idf.py set-target esp32xx`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Since the example supports two types of GPIO control methods, level and pulse, and then combines the low level or high level wake-up, there are four types of control methods, but their outputs are similar, so only the output of the low level wake-up under the level control method is given here as an example.
|
||||
|
||||
Initialize Power Management, GPIO, and UART according to the set configuration:
|
||||
|
||||
```
|
||||
...
|
||||
I (315) app_start: Starting scheduler on CPU0
|
||||
I (320) main_task: Started on CPU0
|
||||
I (320) main_task: Calling app_main()
|
||||
I (320) pm: Frequency switching config: CPU_MAX: 160, APB_MAX: 80, API (340) uart: queue free spaces: 5
|
||||
I (340) uart_control: UART control initialization complete
|
||||
I (340) pm: Frequency switching config: CPU_MAX: 160, APB_MAX: 80, APB_MIN: 40, Light sleep: ENABLED
|
||||
I (350) power_config: Acquired lock at [0x4081303c], system remains active
|
||||
I (360) gpio: GPIO[6]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 1| Intr:0
|
||||
Waiting for [GPIO6] to go low...
|
||||
I (370) ext_power_ctrl_main: All init success
|
||||
I (380) main_task: Returned from app_main()
|
||||
...
|
||||
```
|
||||
|
||||
Based on the output, the example is waiting for the GPIO pin to go low, at which point the GPIO pin is set low:
|
||||
|
||||
```
|
||||
...
|
||||
I (4100) gpio_control: GPIO control initialization complete
|
||||
...
|
||||
```
|
||||
|
||||
### GPIO control
|
||||
|
||||
For this example, if Wake-up by high level is set, then a high input to the GPIO pin corresponds to the operation of acquiring the power management lock, and a low input corresponds to the operation of releasing the power management lock. On the other hand, if Wake-up low level is set, a low input to the GPIO pin corresponds to the operation of acquiring the lock, and a high input corresponds to the operation of releasing the lock.
|
||||
|
||||
In the level control mode, when the GPIO pin input is low during low level wake-up, the system will acquire the power management lock and keep it active; when the GPIO pin input is high, the system will release the power management lock, and if the system is idle at this time, it will enter auto light sleep.
|
||||
|
||||
The GPIO pin constantly inputs high and low levels and outputs as follows:
|
||||
|
||||
```
|
||||
...
|
||||
I (8370) gpio_control: Acquired lock at [0x4081303c], system remains active
|
||||
I (9180) gpio_control: Released lock at [0x4081303c], system may sleep
|
||||
I (10120) gpio_control: Acquired lock at [0x4081303c], system remains active
|
||||
I (12230) gpio_control: Released lock at [0x4081303c], system may sleep
|
||||
...
|
||||
```
|
||||
|
||||
### UART control
|
||||
|
||||
For this example, the default UART is bound to the default console port (UART_NUM_0), and the chip is woken up from light sleep by entering a character at the keyboard. In this example, the UART wakes up and starts a timer that guarantees to release the power management lock if no UART data is received for a certain period of time.
|
||||
|
||||
Entering the character 'u' from the keyboard wakes up the chip and acquires the power management lock, after which no further action is taken, and after a certain period of time the timer times out and releases the lock. The output is as follows:
|
||||
|
||||
```
|
||||
I(15260) uart_control: Acquired lock at [0x4087cd38], system remains active
|
||||
I (23270) uart_control: Released lock at [0x4087cd38], system may sleep
|
||||
I (24700) uart_control: Acquired lock at [0x4087cd38], system remains active
|
||||
```
|
||||
|
||||
Note 1: In this example, the UART wake-up threshold is set to 3 by default, which means the ascii code of the input character should have at least 3 edges, for example, the ascii code of the character "0" is "0011 0000" in binary, which contains only 2 edges, so the character "0" is not enough to wake up the chip.
|
||||
|
||||
Note 2: Only UART0 and UART1 are supported to be configured as wake-up source. For ESP32, although it can be woken up by uart, there is no uart wakeup interrupt, so it can't perform the lock acquisition operation.
|
||||
|
||||
Note 3: The data received during light sleep is only used for wakeup and will not be received by the UART peripheral or passed to the driver.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. GPIO wakeup supports level mode, GPIO wakeup and GPIO interrupt use the same set of configuration registers, so if you use the "GPIO wakeup, control power management lock through GPIO interrupt" method, then GPIO interrupt can only be configured as level mode. In addition, the configurations of GPIO wakeup and GPIO interrupt should not be conflicting, for example, if you configure the GPIO wake-up to be high level first, and then configure the GPIO interrupt to be low level, then you can't wake up the chip by high level.
|
||||
|
||||
2. Can't do block thing in a task in gpio control.
|
||||
|
||||
- Because the gpio interrupt has been disable in the GPIO interrupt service program, when the task is placed in the blocking queue before enabling the interrupt in the task because of blocking on an event, it is possible to execute the idle task, and at this time, it will cause the chip to enter the auto light sleep mode, but the GPIO interrupt has not yet been re-enabled, which will result in the wake-up again, it is not able to trigger the GPIO interrupt, and it will not be able to operate the power lock.
|
||||
|
||||
3. In order to ensure that the GPIO can successfully wake up the chip, it is necessary to configure the GPIO as a level-triggered mode and disable the GPIO interrupt source in the interrupt callback in time to prevent frequent interrupt triggering.
|
||||
|
||||
4. Leakage current problem
|
||||
|
||||
- After testing, all the socs have the same phenomenon: the light sleep power consumption with pulse control is **about 80uA lower** than the light sleep power consumption with level control (all other configurations are the same). This is due to the internal pull-up or pull-down resistor of the IO configuration forming a path with the input level of the IO pin during light sleep in level control mode, and **the leakage current caused by the pull-up or pull-down resistor**.
|
||||
|
||||
- Using the ESP32-S3 as an example, the four types of GPIO control are explained:
|
||||
|
||||
| Control mode | Wakeup level | Current during light sleep (uA) | Internal pull-up or pull-down | Level at sleep |
|
||||
| ------------ | ------------ | ------------------------------- | ----------------------------- | -------------- |
|
||||
| Pulse | LOW | 180 | pull-up resistor | HIGH |
|
||||
| Pulse | HIGH | 191 | pull-down resistor | LOW |
|
||||
| Level | LOW | 262 | pull-down resistor | HIGH |
|
||||
| Level | HIGH | 258 | pull-up resistor | LOW |
|
||||
|
||||
The leakage current is about 80 uA and the voltage 3.3 V. In turn, the resistance values of the pull-up and pull-down resistors can be calculated:
|
||||
|
||||
**3.3 * 1000000 / 80 = 41250 Ω,So the inferred pull-up and pull-down resistor resistance is 41.3 KΩ**
|
||||
|
||||
- The circuits during light sleep in the 4 GPIO control modes are as follows:
|
||||
|
||||
- Pulse-Low level wakeup
|
||||
|
||||
![image-Circuit-Pulse-LowLevel](image/Circuit-Pulse-LowLevel.png)
|
||||
|
||||
- Pulse-High level wakeup
|
||||
|
||||
![image-Circuit-Pulse-HighLevel](image/Circuit-Pulse-HighLevel.png)
|
||||
|
||||
- Level-Low level wakeup
|
||||
|
||||
![image-Circuit-Level-LowLevel](image/Circuit-Level-LowLevel.png)
|
||||
|
||||
- Level-High level wakeup
|
||||
|
||||
![image-Circuit-Level-HighLevel](image/Circuit-Level-HighLevel.png)
|
||||
|
||||
- To avoid leakage currents due to pull-up and pull-down resistors, try the following two methods:
|
||||
|
||||
1. No internal pull-up and pull-down resistors are used and the pin input levels are guaranteed by external circuitry.
|
||||
2. Use the [gpio_sleep_sel_dis()](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/gpio.html#_CPPv418gpio_sleep_sel_dis10gpio_num_t) function to isolate the GPIO pin during light sleep so that external levels do not affect the pin.
|
||||
|
||||
5. Flash power down is not recommended
|
||||
|
||||
- As a storage medium, flash has strict timing requirements for power-up and power-down. (You can check the details in the datasheet of the specific flash chip model.)
|
||||
- It takes some time for the flash to work properly after powering up, and accessing the flash immediately after powering up will result in an error.
|
||||
- In addition, the current of flash is not large enough to significantly affect the power consumption during light sleep, so the flash should not be powered down during the chip's light sleep.
|
||||
|
||||
6. Selection of UART clock source
|
||||
|
||||
- There are several different options for the UART clock source in the IDF. Taking ESP32-S3 as an example, `UART_SCLK_DEFAULT` is the APB clock, but the frequency of APB is directly related to the CPU frequency. **When the CPU frequency is PLL, the APB frequency is 80 MHZ, otherwise it is the same frequency as the CPU**. When dynamic frequency cutting is enabled with Power Manager function, the CPU frequency will change, and the corresponding APB frequency will also change.
|
||||
|
||||
At initialization, CPU runs at maximum frequency, APB frequency is 80 MHZ, then when UART is initialized, it configures the baud rate according to the APB frequency of 80 MHZ, at first, everything is normal, after releasing the lock, CPU may switch to the minimum frequency, at this time, the APB is the same as the CPU frequency (generally 40 MHZ, except for ESP32-H2), but UART does not know that the APB frequency has been changed, it still sets the baud rate according to the original 80 MHZ, but the actual frequency is 40 MHZ, so at this time, the baud rate is wrong, which leads to garbage. However, the UART does not know that the frequency of APB has been changed, it still sets the baud rate according to the original 80 MHZ, but the actual frequency is 40 MHZ, so the baud rate is wrong at this time, which leads to garbled code.
|
||||
|
||||
- When setting the UART clock source, should set the clock source that is not affected by the dynamic frequency cut function.
|
||||
|
||||
7. If the power consumption during light sleep is high, you can set some configuration options in `menuconfig` to reduce the power consumption.
|
||||
|
||||
- The CPU is powered down when light sleep is configured: `(Top) -> Component config -> Power Management -> Power down CPU in light sleep`
|
||||
- For the ESP32-C6 and ESP32-H2, digital peripheral power-down can be configured if the UART function is not used: `(Top) -> Component config -> Power Management -> Power down Digital Peripheral in light sleep (EXPERIMENTAL)`
|
||||
- Configure Wi-Fi and Bluetooth for MAC, baseband power down: `(Top) -> Component config -> PHY -> <ESP_PHY_MAC_BB_PD> Power down MAC and baseband of Wi-Fi and Bluetooth when PHY is disabled`
|
||||
- Configure PSRAM (if the SOC supports configuring PSRAM)
|
||||
1. `(Top) -> Component config -> ESP PSRAM -> Support for external, SPI-connected RAM`
|
||||
2. `(Top) -> Component config -> Hardware Settings -> Sleep Config -> Pull-up PSRAM CS pin in light sleep (NEW)`
|
||||
|
||||
8. Power consumption when the chip is active (need to check the real-time current with an ammeter)
|
||||
|
||||
- If small current spikes occur periodically, consider that some task is being performed.
|
||||
- If the chip is not able to sleep, consider whether there is a task delay time less than the parameter `Minimum number of ticks to enter sleep mode for` setting. In addition, you also need to check whether the power management locks are still not released, you can turn on the menuconfig `Enable profiling counters for PM locks` option, in the program periodically call `esp_pm_dump_locks(stdout)` to view the current occupation time of the various management locks and other information, debug end, you need to turn off this option to reduce overhead. After debugging, you need to turn off this option to reduce the overhead. In addition, you can also turn on the `Enable debug tracing of PM using GPIOs` option for GPIO debugging, see [pm_trace.c](https://github.com/espressif/esp-idf/blob/master/components/esp_pm/pm_trace.c) for details.
|
||||
|
||||
9. Intermittent serial output in light sleep mode
|
||||
|
||||
When the chip enters light sleep mode, the UART data buffer will not be flushed. Instead, the UART output will be paused and the remaining characters in the FIFO will be sent after the light sleep wakes up. You can add `uart_wait_tx_idle_polling()` after the position that needs to be sent to achieve the effect that it will sleep after sending is completed.
|
||||
|
||||
10. Abnormal application operation in low-power mode
|
||||
|
||||
When Power Management is enabled, the system will dynamically switch between `max_freq_mhz` and `min_freq_mhz`. If the task has system performance requirements, it needs to create and maintain a power management lock that meets the task's needs, such as `ESP_PM_CPU_FREQ_MAX`.
|
||||
|
||||
## Example Breakdown
|
||||
|
||||
This example implements a complete set of power management control logic using [GPIO](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/gpio.html#gpio-rtc-gpio) and [UART](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/uart.html#uart) to demonstrate how to control the power consumption of the system by controlling the power management locks. The two main parts are GPIO control and UART control.
|
||||
|
||||
### GPIO Control
|
||||
|
||||
The general idea of GPIO control is that an interrupt is triggered by the GPIO input level, the interrupt service program throws an event to a GPIO task, and the task completes the operation of the power management lock.
|
||||
|
||||
The example implements two GPIO control modes, level and pulse (see below for specific explanation), and there are also two modes of GPIO wakeup, high level wakeup and low level wakeup, so there are four GPIO control modes when they are combined, respectively:
|
||||
|
||||
1. Level Mode-Low Level
|
||||
2. Level Mode-High Level
|
||||
3. Pulse Mode-Low Level
|
||||
4. Pulse Mode-High Level
|
||||
|
||||
#### Level Mode
|
||||
|
||||
The level control mode is characterized by the fact that the GPIO input level always corresponds to the chip state.
|
||||
|
||||
The control flow of level mode mainly contains three processes. Firstly, initialization is carried out, and then when the GPIO input level changes at a certain moment, it triggers the release of the power management lock, and the chip enters the process of auto light sleep. When the GPIO input level changes again, it triggers to wake up the chip and get the power management lock, and the chip stays active.
|
||||
|
||||
![image-20230831105828075](image/LevelMode-FlowChart.png)
|
||||
|
||||
Note: The priority of the GPIO task is higher than level 0 because the priority of the idle task is level 0.
|
||||
|
||||
##### Low Level
|
||||
|
||||
![image-LevelMode-LowLevel](image/LevelMode-LowLevel.png)
|
||||
|
||||
1. The development board has just been powered up and is not yet initialized, so the power management locks and GPIO states are uncertain.
|
||||
2. Initialization
|
||||
1. Since the chip should remain active after power-up and the corresponding GPIO state is low, set the internal pull-down resistor (the example uses the internal pull-down resistor by default, or it can be configured not to use it) when the GPIO is initialized to ensure that the GPIO input is low.
|
||||
2. Set the GPIO wake-up level to low. Since the ESP-C6 and ESP-H2 support digital peripherals power-down, if digital peripherals power-down is configured, wake-up using lp io or ext1 should be used.
|
||||
3. Since a low level corresponds to the state of acquiring the lock, then a high level corresponds to the state of releasing the lock, so set the next GPIO interrupt to a high level interrupt.
|
||||
3. Release lock
|
||||
1. The GPIO input high level, triggering the GPIO interrupt, and since the GPIO should acquire a lock when it goes low again, the next interrupt trigger type is immediately modified to low.
|
||||
2. The interrupt service program throws a lock release event to the GPIO task.
|
||||
3. The GPIO task completes the lock release operation, after which the chip enters auto light sleep.
|
||||
4. Acquire lock
|
||||
1. The GPIO input goes low, the chip wakes up and triggers a GPIO interrupt. Since the lock should be released when the GPIO goes high again, the next interrupt trigger type is immediately modified to high.
|
||||
2. The interrupt service program throws a lock acquisition event to the GPIO task.
|
||||
3. The lock acquisition is done in the GPIO task and the chip stays active after that.
|
||||
|
||||
The software state machine for this mode, is as follows:
|
||||
|
||||
![image-20230831195547032](image/LevelMode-LowLevel-SoftwareStateMachine.png)
|
||||
|
||||
##### High Level
|
||||
|
||||
![image-LevelMode-HighLevel](image/LevelMode-HighLevel.png)
|
||||
|
||||
1. The development board has just been powered up and is not yet initialized, so the power management locks and GPIO states are uncertain.
|
||||
2. Initialization
|
||||
1. Since the chip should remain active after power-up and the corresponding GPIO state is high, set the internal pull-up resistor (the example uses the internal pull-up and down resistor by default, or it can be configured not to use it) when the GPIO is initialized to ensure that the GPIO input is high.
|
||||
2. Set the GPIO wake-up level to high. Since the ESP-C6 and ESP-H2 support digital peripherals power-down, if digital peripherals power-down is configured, wake-up using lp io or ext1 should be used.
|
||||
3. Since a high level corresponds to the state of acquiring the lock, then a low level corresponds to the state of releasing the lock, so set the next GPIO interrupt to a low level interrupt.
|
||||
3. Release lock
|
||||
1. The GPIO input goes low, triggering a GPIO interrupt. Since the lock should be acquired when the GPIO goes high again, the next interrupt trigger type is immediately modified to high.
|
||||
2. The interrupt service program throws a lock release event to the GPIO task.
|
||||
3. The GPIO task completes the lock release operation, after which the chip enters auto light sleep.
|
||||
4. Acquire lock
|
||||
1. GPIO input goes high, the chip wakes up and triggers a GPIO interrupt. Since the lock should be released when the GPIO goes low again, the next interrupt trigger type is immediately modified to low.
|
||||
2. The interrupt service program throws a lock acquisition event to the GPIO task.
|
||||
3. The lock acquisition is done in the GPIO task and the chip stays active after that.
|
||||
|
||||
The software state machine for this mode, is as follows:
|
||||
|
||||
![image-20230831195919546](image/LevelMode-HighLevel-SoftwareStateMachine.png)
|
||||
|
||||
#### Pulse Mode
|
||||
|
||||
Pulse control mode is characterized by triggering a complete pulse once and the chip state changes.
|
||||
|
||||
The pulse control mode differs in two ways:
|
||||
|
||||
1. After the interrupt is triggered, the complete pulse needs to be detected before operating the power management lock.
|
||||
2. The GPIO interrupt type remains unchanged at all times.
|
||||
|
||||
The control flow of pulse mode mainly contains three processes, firstly, initialization, and then at a certain moment when the GPIO inputs pulse, it triggers the release of the power management lock, and the chip enters the process of auto light sleep. When the GPIO inputs pulse again, it will trigger to wake up the chip, get the power management lock, and keep the chip active.
|
||||
|
||||
![image-20230831142417270](image/PulseMode-FlowChart.png)
|
||||
|
||||
Note 1: The priority of GPIO task should be higher than level 0 because the priority of idle task is level 0.
|
||||
|
||||
Note 2: When using pulse mode, pay attention to de-jittering to prevent the complete pulse from being detected due to jitter.
|
||||
|
||||
##### Low Level
|
||||
|
||||
![image-PulseMode-LowLevel](image/PulseMode-LowLevel.png)
|
||||
|
||||
1. The development board has just been powered up and is not yet initialized, so the power management locks and GPIO states are uncertain.
|
||||
2. Initialization
|
||||
1. Since the set low level triggers the interrupt, the GPIO is initialized with the internal pull-up resistor set (the example uses the internal pull-up and down resistors by default, or can be configured not to use them) to ensure that the GPIO input goes high.
|
||||
2. Set the GPIO wake-up level to low. Since the ESP-C6 and ESP-H2 support digital peripherals power-down, if digital peripherals power-down is configured, wake-up using lp io or ext1 should be used.
|
||||
3. Sets a low level interrupt.
|
||||
3. Release lock
|
||||
1. The GPIO input goes low, triggering the GPIO interrupt, and the interrupt service program detects the event of the pulse into the GPIO task.
|
||||
2. The GPIO task detects the pulse and completes the lock release operation, after which the chip enters auto light sleep.
|
||||
4. Acquire lock
|
||||
1. The GPIO input goes low, the chip wakes up and triggers the GPIO interrupt, and the interrupt service program detects the event of the pulse into the GPIO task.
|
||||
2. The GPIO task detects the pulse and completes the lock acquisition operation, after which the chip remains active.
|
||||
|
||||
The software state machine for this mode, is as follows:
|
||||
|
||||
![image-20230831200053226](image/PulseMode-LowLevel-SoftwareStateMachine.png)
|
||||
|
||||
##### High Level
|
||||
|
||||
![image-PulseMode-HighLevel](image/PulseMode-HighLevel.png)
|
||||
|
||||
1. The development board has just been powered up and is not yet initialized, so the power management locks and GPIO states are uncertain.
|
||||
2. Initialization
|
||||
1. Since the set high level triggers the interrupt, the GPIO is initialized with the internal pull-down resistor set (the example uses the internal pull-down resistor by default, or it can be configured not to use it) to ensure that the GPIO input is low.
|
||||
2. Set the GPIO wake-up level to high. Since the ESP-C6 and ESP-H2 support digital peripherals power-down, if digital peripherals power-down is configured, wake-up using lp io or ext1 should be used.
|
||||
3. Sets a high level interrupt.
|
||||
3. Release lock
|
||||
1. The GPIO input goes high, triggering the GPIO interrupt, and the interrupt service program detects the event of the pulse into the GPIO task.
|
||||
2. The GPIO task detects the pulse and completes the lock release operation, after which the chip enters auto light sleep.
|
||||
4. Acquire lock
|
||||
1. The GPIO input goes high, the chip wakes up and triggers the GPIO interrupt, and the interrupt service program detects the event of the pulse into the GPIO task.
|
||||
2. The GPIO task detects the pulse and completes the lock acquisition operation, after which the chip remains active.
|
||||
|
||||
The software state machine for this mode, is as follows:
|
||||
|
||||
![image-20230831200350793](image/PulseMode-HighLevel-SoftwareStateMachine.png)
|
||||
|
||||
### UART Control
|
||||
|
||||
The general idea of UART control: wake up the chip through UART, get the power management lock, the chip stays active, and after a period of time of idle (no UART data received), then release the lock, and the chip enters auto light sleep.
|
||||
|
||||
![image-20230831144945435](image/UARTControl-FlowChart.png)
|
||||
|
||||
The software state machine for UART control is as follows:
|
||||
|
||||
![image-20230831200849176](image/UARTControl-SoftwareStateMachine.png)
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 142 KiB |
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 163 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 124 KiB |
@ -0,0 +1,7 @@
|
||||
set(srcs "external_power_control_example_main.c"
|
||||
"pm_config.c"
|
||||
"gpio_control/gpio_control.c"
|
||||
"uart_control/uart_control.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,308 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
menu "GPIO configuration"
|
||||
config EXAMPLE_GPIO_CONTROL_NUM
|
||||
int "Pin for gpio control"
|
||||
default 6 if IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C6
|
||||
default 5 if IDF_TARGET_ESP32S2
|
||||
default 4 if IDF_TARGET_ESP32
|
||||
default 10 if IDF_TARGET_ESP32H2
|
||||
default 0
|
||||
range 0 7 if PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && IDF_TARGET_ESP32C6
|
||||
range 7 14 if PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && IDF_TARGET_ESP32H2
|
||||
range 0 39 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
range 0 48 if IDF_TARGET_ESP32S3
|
||||
range 0 20 if IDF_TARGET_ESP32C2
|
||||
range 0 21 if IDF_TARGET_ESP32C3
|
||||
range 0 30 if IDF_TARGET_ESP32C6
|
||||
range 0 27 if IDF_TARGET_ESP32H2
|
||||
help
|
||||
This option is used to set the pin for gpio control.
|
||||
|
||||
config EXAMPLE_USE_PULSE_CONTROL
|
||||
bool "Enable GPIO pulse control"
|
||||
default n
|
||||
help
|
||||
This option set the gpio control mode.
|
||||
If this option is enabled, it is a pulse control mode, otherwise it is a level control mode.
|
||||
|
||||
config EXAMPLE_GPIO_CONTROL_HIGH_LEVEL
|
||||
bool "Enable GPIO high-level control"
|
||||
default n
|
||||
help
|
||||
This option set the gpio control trigger signal, In light sleep, only high or low level wake-up is
|
||||
supported. If this option is enabled, it is a high level wakeup, otherwise it is a low level wakeup.
|
||||
|
||||
config EXAMPLE_USE_INTERNAL_PU_PD
|
||||
bool "Use internal pull up and pull down"
|
||||
default y
|
||||
help
|
||||
This option set the pull-down mode of the gpio. If this option is enabled,
|
||||
then the internal pull-down is used, otherwise the external access pull-down is used.
|
||||
endmenu
|
||||
|
||||
menu "UART configuration"
|
||||
config EXAMPLE_UART_NUM
|
||||
int "Select the UART to use"
|
||||
default 0
|
||||
range 0 1
|
||||
help
|
||||
This option sets the UART used for the UART wakeup.
|
||||
Although the esp32s3 have three uart, only uart0 and uart1 support uart wakeup.
|
||||
|
||||
config EXAMPLE_UART_TX_IO_NUM
|
||||
int "Select the UART tx io"
|
||||
default 1 if IDF_TARGET_ESP32 && (!EXAMPLE_UART_NUM)
|
||||
default 43 if IDF_TARGET_ESP32S2 && (!EXAMPLE_UART_NUM)
|
||||
default 43 if IDF_TARGET_ESP32S3 && (!EXAMPLE_UART_NUM)
|
||||
default 20 if IDF_TARGET_ESP32C2 && (!EXAMPLE_UART_NUM)
|
||||
default 21 if IDF_TARGET_ESP32C3 && (!EXAMPLE_UART_NUM)
|
||||
default 16 if IDF_TARGET_ESP32C6 && (!EXAMPLE_UART_NUM)
|
||||
default 24 if IDF_TARGET_ESP32H2 && (!EXAMPLE_UART_NUM)
|
||||
default 10 if IDF_TARGET_ESP32 && EXAMPLE_UART_NUM
|
||||
default 17 if IDF_TARGET_ESP32S2 && EXAMPLE_UART_NUM
|
||||
default 17 if IDF_TARGET_ESP32S3 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C2 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C3 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C6 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32H2 && EXAMPLE_UART_NUM
|
||||
range -1 39 if IDF_TARGET_ESP32
|
||||
range -1 46 if IDF_TARGET_ESP32S2
|
||||
range -1 48 if IDF_TARGET_ESP32S3
|
||||
range -1 20 if IDF_TARGET_ESP32C2
|
||||
range -1 21 if IDF_TARGET_ESP32C3
|
||||
range -1 30 if IDF_TARGET_ESP32C6
|
||||
range -1 27 if IDF_TARGET_ESP32H2
|
||||
help
|
||||
This option is used to set the tx pin of the uart.
|
||||
The default value is the tx default pin for uart0 or uart1.
|
||||
-1 means no pin is set.
|
||||
Notes:
|
||||
1.The ESP32 must configure uart as the wakeup source using the iomux input; there are no limitation to the other target.
|
||||
2.If you want to select other gpio pins, must check if the pins are available through the datasheet.
|
||||
|
||||
config EXAMPLE_UART_RX_IO_NUM
|
||||
int "Select the UART rx io"
|
||||
default 3 if IDF_TARGET_ESP32 && (!EXAMPLE_UART_NUM)
|
||||
default 44 if IDF_TARGET_ESP32S2 && (!EXAMPLE_UART_NUM)
|
||||
default 44 if IDF_TARGET_ESP32S3 && (!EXAMPLE_UART_NUM)
|
||||
default 19 if IDF_TARGET_ESP32C2 && (!EXAMPLE_UART_NUM)
|
||||
default 20 if IDF_TARGET_ESP32C3 && (!EXAMPLE_UART_NUM)
|
||||
default 17 if IDF_TARGET_ESP32C6 && (!EXAMPLE_UART_NUM)
|
||||
default 23 if IDF_TARGET_ESP32H2 && (!EXAMPLE_UART_NUM)
|
||||
default 9 if IDF_TARGET_ESP32 && EXAMPLE_UART_NUM
|
||||
default 18 if IDF_TARGET_ESP32S2 && EXAMPLE_UART_NUM
|
||||
default 18 if IDF_TARGET_ESP32S3 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C2 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C3 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C6 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32H2 && EXAMPLE_UART_NUM
|
||||
range -1 39 if IDF_TARGET_ESP32
|
||||
range -1 46 if IDF_TARGET_ESP32S2
|
||||
range -1 48 if IDF_TARGET_ESP32S3
|
||||
range -1 20 if IDF_TARGET_ESP32C2
|
||||
range -1 21 if IDF_TARGET_ESP32C3
|
||||
range -1 30 if IDF_TARGET_ESP32C6
|
||||
range -1 27 if IDF_TARGET_ESP32H2
|
||||
help
|
||||
This option is used to set the rx pin of the uart.
|
||||
The default value is the rx default pin for uart0 or uart1.
|
||||
-1 means no pin is set.
|
||||
Notes:
|
||||
1.The ESP32 must configure uart as the wakeup source using the iomux input; there are no limitation to the other target.
|
||||
2.If you want to select other gpio pins, must check if the pins are available through the datasheet.
|
||||
|
||||
config EXAMPLE_UART_CTS_IO_NUM
|
||||
int "Select the UART cts io"
|
||||
default 19 if IDF_TARGET_ESP32 && (!EXAMPLE_UART_NUM)
|
||||
default 16 if IDF_TARGET_ESP32S2 && (!EXAMPLE_UART_NUM)
|
||||
default 16 if IDF_TARGET_ESP32S3 && (!EXAMPLE_UART_NUM)
|
||||
default -1 if IDF_TARGET_ESP32C2 && (!EXAMPLE_UART_NUM)
|
||||
default -1 if IDF_TARGET_ESP32C3 && (!EXAMPLE_UART_NUM)
|
||||
default -1 if IDF_TARGET_ESP32C6 && (!EXAMPLE_UART_NUM)
|
||||
default -1 if IDF_TARGET_ESP32H2 && (!EXAMPLE_UART_NUM)
|
||||
default 6 if IDF_TARGET_ESP32 && EXAMPLE_UART_NUM
|
||||
default 20 if IDF_TARGET_ESP32S2 && EXAMPLE_UART_NUM
|
||||
default 20 if IDF_TARGET_ESP32S3 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C2 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C3 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C6 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32H2 && EXAMPLE_UART_NUM
|
||||
range -1 39 if IDF_TARGET_ESP32
|
||||
range -1 46 if IDF_TARGET_ESP32S2
|
||||
range -1 48 if IDF_TARGET_ESP32S3
|
||||
range -1 20 if IDF_TARGET_ESP32C2
|
||||
range -1 21 if IDF_TARGET_ESP32C3
|
||||
range -1 30 if IDF_TARGET_ESP32C6
|
||||
range -1 27 if IDF_TARGET_ESP32H2
|
||||
help
|
||||
This option is used to set the cts pin of the uart.
|
||||
The default value is the cts default pin for uart0 or uart1.
|
||||
-1 means no pin is set.
|
||||
Notes:
|
||||
1.The ESP32 must configure uart as the wakeup source using the iomux input; there are no limitation to the other target.
|
||||
2.If you want to select other gpio pins, must check if the pins are available through the datasheet.
|
||||
|
||||
config EXAMPLE_UART_RTS_IO_NUM
|
||||
int "Select the UART rts io"
|
||||
default 22 if IDF_TARGET_ESP32 && (!EXAMPLE_UART_NUM)
|
||||
default 15 if IDF_TARGET_ESP32S2 && (!EXAMPLE_UART_NUM)
|
||||
default 15 if IDF_TARGET_ESP32S3 && (!EXAMPLE_UART_NUM)
|
||||
default -1 if IDF_TARGET_ESP32C2 && (!EXAMPLE_UART_NUM)
|
||||
default -1 if IDF_TARGET_ESP32C3 && (!EXAMPLE_UART_NUM)
|
||||
default -1 if IDF_TARGET_ESP32C6 && (!EXAMPLE_UART_NUM)
|
||||
default -1 if IDF_TARGET_ESP32H2 && (!EXAMPLE_UART_NUM)
|
||||
default 11 if IDF_TARGET_ESP32 && EXAMPLE_UART_NUM
|
||||
default 19 if IDF_TARGET_ESP32S2 && EXAMPLE_UART_NUM
|
||||
default 19 if IDF_TARGET_ESP32S3 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C2 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C3 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32C6 && EXAMPLE_UART_NUM
|
||||
default -1 if IDF_TARGET_ESP32H2 && EXAMPLE_UART_NUM
|
||||
range -1 39 if IDF_TARGET_ESP32
|
||||
range -1 46 if IDF_TARGET_ESP32S2
|
||||
range -1 48 if IDF_TARGET_ESP32S3
|
||||
range -1 20 if IDF_TARGET_ESP32C2
|
||||
range -1 21 if IDF_TARGET_ESP32C3
|
||||
range -1 30 if IDF_TARGET_ESP32C6
|
||||
range -1 27 if IDF_TARGET_ESP32H2
|
||||
help
|
||||
This option is used to set the rts pin of the uart.
|
||||
The default value is the rts default pin for uart0 or uart1.
|
||||
-1 means no pin is set.
|
||||
Notes:
|
||||
1.The ESP32 must configure uart as the wakeup source using the iomux input; there are no limitation to the other target.
|
||||
2.If you want to select other gpio pins, must check if the pins are available through the datasheet.
|
||||
|
||||
config EXAMPLE_UART_WAKEUP_THRESHOLD
|
||||
int "UART wakeup threshold"
|
||||
default 3
|
||||
range 3 100
|
||||
help
|
||||
This option is used to set the number of pos-edge used for uart wakeup.
|
||||
Note: The minimum number of pos-edge is 3
|
||||
|
||||
config EXAMPLE_SLEEP_TIMEOUT_US
|
||||
int "idle wait time (us)"
|
||||
default 8000000
|
||||
range 1 15000000
|
||||
help
|
||||
This option sets the timeout for the sleep timer.
|
||||
endmenu
|
||||
|
||||
menu "event configuration"
|
||||
config EXAMPLE_EVENT_QUEUE_LEN
|
||||
int "Event queue length"
|
||||
default 5
|
||||
range 1 30
|
||||
help
|
||||
This option is used to set the length of the queue in which events are stored.
|
||||
It is recommended to set it to between 5 and 10.
|
||||
Because if it is too small, it is easy to cause the system to crash; too big is also useless.
|
||||
|
||||
config EXAMPLE_EVENT_TASK_STACK_SIZE
|
||||
int "Event task`s stack size"
|
||||
default 4096
|
||||
range 1 8192
|
||||
help
|
||||
This option is used to set the stack size of the task that handles the event.
|
||||
When there is extra heap space requested in the task, it should be set larger.
|
||||
|
||||
config EXAMPLE_EVENT_TASK_PRIORITY
|
||||
int "Event task`s priority"
|
||||
default 10
|
||||
range 1 15
|
||||
help
|
||||
This option is used to set the priority of the task.
|
||||
The higher the value, the higher the priority. A value of 10 is usually sufficient.
|
||||
endmenu
|
||||
|
||||
menu "power manager configuration"
|
||||
|
||||
choice EXAMPLE_PM_LOCK_TYPE
|
||||
prompt "Set pm lock"
|
||||
default EXAMPLE_PM_LOCK_TYPE_NO_LIGHT_SLEEP
|
||||
help
|
||||
set lock in pm config.
|
||||
|
||||
config EXAMPLE_PM_LOCK_TYPE_CPU_FREQ_MAX
|
||||
bool "CPU_FREQ_MAX lock"
|
||||
config EXAMPLE_PM_LOCK_TYPE_APB_FREQ_MAX
|
||||
bool "APB_FREQ_MAX lock"
|
||||
config EXAMPLE_PM_LOCK_TYPE_NO_LIGHT_SLEEP
|
||||
bool "NO_LIGHT_SLEEP lock"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_PM_LOCK_TYPE
|
||||
int
|
||||
default 0 if EXAMPLE_PM_LOCK_TYPE_CPU_FREQ_MAX
|
||||
default 1 if EXAMPLE_PM_LOCK_TYPE_APB_FREQ_MAX
|
||||
default 2 if EXAMPLE_PM_LOCK_TYPE_NO_LIGHT_SLEEP
|
||||
|
||||
choice EXAMPLE_PM_MAX_FREQ_MHZ
|
||||
prompt "Set max freq in pm config"
|
||||
default EXAMPLE_PM_MAX_FREQ_MHZ_96 if IDF_TARGET_ESP32H2
|
||||
default EXAMPLE_PM_MAX_FREQ_MHZ_120 if IDF_TARGET_ESP32C2
|
||||
default EXAMPLE_PM_MAX_FREQ_MHZ_160 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6
|
||||
default EXAMPLE_PM_MAX_FREQ_MHZ_240
|
||||
help
|
||||
set max freq in pm config.
|
||||
|
||||
config EXAMPLE_PM_MAX_FREQ_MHZ_80
|
||||
bool "80 MHz" if !IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_PM_MAX_FREQ_MHZ_120
|
||||
bool "120 MHz" if IDF_TARGET_ESP32C2
|
||||
config EXAMPLE_PM_MAX_FREQ_MHZ_160
|
||||
bool "160 MHz" if !IDF_TARGET_ESP32H2 && !IDF_TARGET_ESP32C2
|
||||
config EXAMPLE_PM_MAX_FREQ_MHZ_240
|
||||
bool "240 MHz" if !IDF_TARGET_ESP32H2 && !IDF_TARGET_ESP32C2 && !IDF_TARGET_ESP32C3 && !IDF_TARGET_ESP32C6
|
||||
|
||||
config EXAMPLE_PM_MAX_FREQ_MHZ_96
|
||||
bool "96 MHz" if IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_PM_MAX_FREQ_MHZ_64
|
||||
bool "64 MHz" if IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_PM_MAX_FREQ_MHZ_48
|
||||
bool "48 MHz" if IDF_TARGET_ESP32H2
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_PM_MAX_FREQ_MHZ
|
||||
int
|
||||
default 80 if EXAMPLE_PM_MAX_FREQ_MHZ_80
|
||||
default 120 if EXAMPLE_PM_MAX_FREQ_MHZ_120
|
||||
default 160 if EXAMPLE_PM_MAX_FREQ_MHZ_160
|
||||
default 240 if EXAMPLE_PM_MAX_FREQ_MHZ_240
|
||||
default 96 if EXAMPLE_PM_MAX_FREQ_MHZ_96
|
||||
default 64 if EXAMPLE_PM_MAX_FREQ_MHZ_64
|
||||
default 48 if EXAMPLE_PM_MAX_FREQ_MHZ_48
|
||||
|
||||
choice EXAMPLE_PM_MIN_FREQ_MHZ
|
||||
prompt "Set min freq in pm config"
|
||||
default EXAMPLE_PM_MIN_FREQ_MHZ_32 if IDF_TARGET_ESP32H2
|
||||
default EXAMPLE_PM_MIN_FREQ_MHZ_40
|
||||
help
|
||||
set min freq in pm config.
|
||||
|
||||
config EXAMPLE_PM_MIN_FREQ_MHZ_40
|
||||
bool "40 MHz" if !IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_PM_MIN_FREQ_MHZ_80
|
||||
bool "80 MHz" if !IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_PM_MIN_FREQ_MHZ_32
|
||||
bool "32 MHz" if IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_PM_MIN_FREQ_MHZ_48
|
||||
bool "48 MHz" if IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_PM_MIN_FREQ_MHZ_64
|
||||
bool "64 MHz" if IDF_TARGET_ESP32H2
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_PM_MIN_FREQ_MHZ
|
||||
int
|
||||
default 40 if EXAMPLE_PM_MIN_FREQ_MHZ_40
|
||||
default 80 if EXAMPLE_PM_MIN_FREQ_MHZ_80
|
||||
default 32 if EXAMPLE_PM_MIN_FREQ_MHZ_32
|
||||
default 48 if EXAMPLE_PM_MIN_FREQ_MHZ_48
|
||||
default 64 if EXAMPLE_PM_MIN_FREQ_MHZ_64
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
#include "esp_pm.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
/* Set the length of the event queue */
|
||||
#define DEFAULT_EVENT_QUEUE_LEN (CONFIG_EXAMPLE_EVENT_QUEUE_LEN)
|
||||
|
||||
/* Set the task's stack size */
|
||||
#define DEFAULT_EVENT_TASK_STACK_SIZE (CONFIG_EXAMPLE_EVENT_TASK_STACK_SIZE)
|
||||
|
||||
/* Set the priority of a task */
|
||||
#define DEFAULT_EVENT_TASK_PRIORITY (CONFIG_EXAMPLE_EVENT_TASK_PRIORITY)
|
||||
|
||||
/* Type of pm lock configured by menuconfig */
|
||||
#if (CONFIG_EXAMPLE_PM_LOCK_TYPE == 0)
|
||||
#define DEFAULT_PM_LOCK_TYPE (ESP_PM_CPU_FREQ_MAX)
|
||||
#elif (CONFIG_EXAMPLE_PM_LOCK_TYPE == 1)
|
||||
#define DEFAULT_PM_LOCK_TYPE (ESP_PM_APB_FREQ_MAX)
|
||||
#else
|
||||
#define DEFAULT_PM_LOCK_TYPE (ESP_PM_NO_LIGHT_SLEEP)
|
||||
#endif
|
||||
|
||||
/* System hold lock state */
|
||||
#define RELEASED_LOCK (false)
|
||||
#define ACQUIRED_LOCK (true)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
esp_err_t example_register_power_config(esp_pm_lock_handle_t *pm_lock, bool *lock_state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "gpio_control/gpio_control.h"
|
||||
#include "uart_control/uart_control.h"
|
||||
#include "external_power_control_example.h"
|
||||
|
||||
static const char *TAG = "ext_pw_ctrl_main";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
#if !CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP || !CONFIG_IDF_TARGET_ESP32
|
||||
static uart_ctrl_context_t s_uart_context;
|
||||
#endif
|
||||
static gpio_ctrl_context_t s_gpio_context;
|
||||
|
||||
#if !CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP || !CONFIG_IDF_TARGET_ESP32
|
||||
ESP_ERROR_CHECK( example_register_uart_control(&s_uart_context) );
|
||||
#endif
|
||||
ESP_ERROR_CHECK( example_register_gpio_control(&s_gpio_context) );
|
||||
|
||||
ESP_LOGI(TAG, "All init success");
|
||||
}
|
@ -0,0 +1,364 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "esp_check.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rtc_io.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/projdefs.h"
|
||||
#include "gpio_control.h"
|
||||
|
||||
/* IO pin for gpio control */
|
||||
#define DEFAULT_GPIO_CONTROL_NUM (CONFIG_EXAMPLE_GPIO_CONTROL_NUM)
|
||||
|
||||
/* Set control mode, 1 for control with pulse, 0 for control with level */
|
||||
#if CONFIG_EXAMPLE_USE_PULSE_CONTROL
|
||||
#define DEFAULT_USE_PULSE_CONTROL (1)
|
||||
#else
|
||||
#define DEFAULT_USE_PULSE_CONTROL (0)
|
||||
#endif
|
||||
|
||||
/* Setting the level for control, 1 for control with high level, 0 for control with low level */
|
||||
#if CONFIG_EXAMPLE_GPIO_CONTROL_HIGH_LEVEL
|
||||
#define DEFAULT_GPIO_CONTROL_LEVEL (1)
|
||||
#else
|
||||
#define DEFAULT_GPIO_CONTROL_LEVEL (0)
|
||||
#endif
|
||||
|
||||
/* Whether to use internal pull up and pull down, 1 means use internal pu pd, 0 means no internal pu pd */
|
||||
#if CONFIG_EXAMPLE_USE_INTERNAL_PU_PD
|
||||
#define DEFAULT_USE_INTERNAL_PU_PD (1)
|
||||
#else
|
||||
#define DEFAULT_USE_INTERNAL_PU_PD (0)
|
||||
#endif
|
||||
|
||||
/* Types of interrupt for lock acquisition and lock release in level control mode */
|
||||
#if DEFAULT_GPIO_CONTROL_LEVEL
|
||||
#define ACQUIRE_LOCK_INTR_TYPE (GPIO_INTR_HIGH_LEVEL)
|
||||
#define RELEASE_LOCK_INTR_TYPE (GPIO_INTR_LOW_LEVEL)
|
||||
#else
|
||||
#define ACQUIRE_LOCK_INTR_TYPE (GPIO_INTR_LOW_LEVEL)
|
||||
#define RELEASE_LOCK_INTR_TYPE (GPIO_INTR_HIGH_LEVEL)
|
||||
#endif
|
||||
|
||||
#define ESP_INTR_FLAG_DEFAULT (0)
|
||||
|
||||
/**
|
||||
* @brief Types of event that task can handle.
|
||||
*/
|
||||
typedef enum {
|
||||
GPIO_EVENT_INIT = 0, /*!< gpio initialization event */
|
||||
GPIO_EVENT_CHECK_PULSE, /*!< detect pulse event */
|
||||
GPIO_EVENT_ACQUIRE_PM_LOCK, /*!< acquire lock event */
|
||||
GPIO_EVENT_RELEASE_PM_LOCK /*!< release lock event */
|
||||
} example_gpio_event_t;
|
||||
|
||||
/**
|
||||
* @brief Message passed to task by interrupt function.
|
||||
*/
|
||||
typedef struct {
|
||||
example_gpio_event_t event;
|
||||
} example_gpio_msg_t;
|
||||
|
||||
static const char *TAG = "gpio_control";
|
||||
|
||||
/**
|
||||
* @brief The gpio interrupt service program.
|
||||
*
|
||||
* interrupt service program have three tasks:
|
||||
* 1. disable interrupt
|
||||
* 2. modify interrupt trigger type if it is a level wake-up method
|
||||
* 3. notify task to work
|
||||
*
|
||||
* @param args The parameter passed in when creating the task, in this case of type 'gpio_ctrl_context_t'.
|
||||
*/
|
||||
static void IRAM_ATTR gpio_isr_handler(void *args)
|
||||
{
|
||||
gpio_ctrl_context_t *object = (gpio_ctrl_context_t *)args;
|
||||
example_gpio_msg_t message;
|
||||
|
||||
// The first step is to disable intr
|
||||
ESP_ERROR_CHECK( gpio_intr_disable(object->gpio) );
|
||||
if (object->control_mode == GPIO_LEVEL_CONTROL) {
|
||||
// If it's level control mode, there's also changing the interrupt trigger method
|
||||
ESP_ERROR_CHECK( gpio_set_intr_type(object->gpio,
|
||||
object->lock_state ? ACQUIRE_LOCK_INTR_TYPE : RELEASE_LOCK_INTR_TYPE) );
|
||||
|
||||
// Direct acquire lock or release lock in level control mode
|
||||
message.event = object->lock_state ? GPIO_EVENT_RELEASE_PM_LOCK : GPIO_EVENT_ACQUIRE_PM_LOCK;
|
||||
} else {
|
||||
// In pulse mode, the pulse is detected first
|
||||
message.event = GPIO_EVENT_CHECK_PULSE;
|
||||
}
|
||||
xQueueSendFromISR(object->event_queue, (void *)&message, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief GPIO initialization.
|
||||
*
|
||||
* Initialize the gpio according to the control mode.
|
||||
*
|
||||
* @param gpio_context Important information to be used for gpio control
|
||||
*/
|
||||
static void example_gpio_initialization(gpio_ctrl_context_t *gpio_context)
|
||||
{
|
||||
gpio_config_t config = {
|
||||
.pin_bit_mask = BIT64(gpio_context->gpio),
|
||||
.mode = GPIO_MODE_INPUT
|
||||
};
|
||||
switch (gpio_context->control_mode) {
|
||||
case GPIO_LEVEL_CONTROL:
|
||||
config.pull_up_en = DEFAULT_USE_INTERNAL_PU_PD ? (gpio_context->control_level ? true : false) : false;
|
||||
config.pull_down_en = DEFAULT_USE_INTERNAL_PU_PD ? (gpio_context->control_level ? false : true) : false;
|
||||
break;
|
||||
|
||||
case GPIO_PULSE_CONTROL:
|
||||
config.pull_up_en = DEFAULT_USE_INTERNAL_PU_PD ? ( (!gpio_context->control_level) ? true : false ) : false;
|
||||
config.pull_down_en = DEFAULT_USE_INTERNAL_PU_PD ? ( (!gpio_context->control_level) ? false : true ) : false;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_ERROR_CHECK( gpio_config(&config) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for gpio state to stabilize.
|
||||
*
|
||||
* @param gpio_context Important information to be used for gpio control
|
||||
*/
|
||||
static void example_wait_gpio_inactive(gpio_ctrl_context_t *gpio_context)
|
||||
{
|
||||
bool wait_level = (gpio_context->control_mode == GPIO_LEVEL_CONTROL) ? gpio_context->control_level
|
||||
: !(gpio_context->control_level);
|
||||
|
||||
if (gpio_get_level(gpio_context->gpio) != wait_level) {
|
||||
printf("Waiting for [GPIO%d] to go %s...\n", gpio_context->gpio, wait_level ? "high" : "low");
|
||||
}
|
||||
while (gpio_get_level(gpio_context->gpio) != wait_level) {
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable rtc gpio wakeup.
|
||||
*
|
||||
* First initialize rtc gpio according to the control mode, then enable rtc gpio wakeup.
|
||||
*
|
||||
* @param gpio_context Important information to be used for gpio control
|
||||
*/
|
||||
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
|
||||
static void example_rtc_gpio_wakeup_enable(gpio_ctrl_context_t *gpio_context)
|
||||
{
|
||||
// Initialize LPIO
|
||||
ESP_ERROR_CHECK( rtc_gpio_init(gpio_context->gpio) );
|
||||
ESP_ERROR_CHECK( rtc_gpio_set_direction(gpio_context->gpio, RTC_GPIO_MODE_INPUT_ONLY) );
|
||||
#if DEFAULT_USE_INTERNAL_PU_PD
|
||||
switch (gpio_context->control_mode) {
|
||||
case GPIO_LEVEL_CONTROL:
|
||||
if (gpio_context->control_level) {
|
||||
ESP_ERROR_CHECK( rtc_gpio_pullup_en(gpio_context->gpio) );
|
||||
ESP_ERROR_CHECK( rtc_gpio_pulldown_dis(gpio_context->gpio) );
|
||||
} else {
|
||||
ESP_ERROR_CHECK( rtc_gpio_pullup_dis(gpio_context->gpio) );
|
||||
ESP_ERROR_CHECK( rtc_gpio_pulldown_en(gpio_context->gpio) );
|
||||
}
|
||||
break;
|
||||
|
||||
case GPIO_PULSE_CONTROL:
|
||||
if (gpio_context->control_level) {
|
||||
ESP_ERROR_CHECK( rtc_gpio_pullup_dis(gpio_context->gpio) );
|
||||
ESP_ERROR_CHECK( rtc_gpio_pulldown_en(gpio_context->gpio) );
|
||||
} else {
|
||||
ESP_ERROR_CHECK( rtc_gpio_pullup_en(gpio_context->gpio) );
|
||||
ESP_ERROR_CHECK( rtc_gpio_pulldown_dis(gpio_context->gpio) );
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
#else
|
||||
ESP_ERROR_CHECK( rtc_gpio_pullup_dis(gpio_context->gpio) );
|
||||
ESP_ERROR_CHECK( rtc_gpio_pulldown_dis(gpio_context->gpio) );
|
||||
#endif
|
||||
|
||||
// Enable wake up from LPIO wakeup
|
||||
ESP_ERROR_CHECK( rtc_gpio_wakeup_enable(gpio_context->gpio, gpio_context->control_level == 0 ?
|
||||
GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL) );
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enable wakeup.
|
||||
*
|
||||
* Enable rtc gpio wakeup if the digital peripheral is powered down
|
||||
* and the target supports rtc input and output; enable ext1 wakeup
|
||||
* if the target does not support rtc inputs and outputs. If the
|
||||
* digital peripheral is not powered down, enable gpio wakeup.
|
||||
*
|
||||
* @param gpio_context Important information to be used for gpio control
|
||||
*/
|
||||
static void example_gpio_wakeup_enable(gpio_ctrl_context_t *gpio_context)
|
||||
{
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
|
||||
example_rtc_gpio_wakeup_enable(gpio_context);
|
||||
#else
|
||||
ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(BIT64(gpio_context->gpio), gpio_context->control_level == 0 ?
|
||||
ESP_EXT1_WAKEUP_ANY_LOW : ESP_EXT1_WAKEUP_ANY_HIGH) );
|
||||
#endif
|
||||
#else
|
||||
ESP_ERROR_CHECK( gpio_wakeup_enable(gpio_context->gpio, gpio_context->control_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL) );
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the gpio interrupt type.
|
||||
*
|
||||
* Different interrupt types should be set for different
|
||||
* control modes. For level control mode, it should be set
|
||||
* to non-control level; for pulse control mode, it should
|
||||
* be set to control level.
|
||||
*
|
||||
* @param gpio_context Important information to be used for gpio control
|
||||
*/
|
||||
static void example_gpio_set_intr_type(gpio_ctrl_context_t *gpio_context)
|
||||
{
|
||||
switch (gpio_context->control_mode) {
|
||||
case GPIO_LEVEL_CONTROL:
|
||||
ESP_ERROR_CHECK( gpio_set_intr_type(gpio_context->gpio, RELEASE_LOCK_INTR_TYPE) );
|
||||
break;
|
||||
|
||||
case GPIO_PULSE_CONTROL:
|
||||
ESP_ERROR_CHECK( gpio_set_intr_type(gpio_context->gpio, gpio_context->control_level == 0 ?
|
||||
GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL) );
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable gpio interrupt.
|
||||
*
|
||||
* @param gpio_context Important information to be used for gpio control
|
||||
*/
|
||||
static void example_gpio_intr_enable(gpio_ctrl_context_t *gpio_context)
|
||||
{
|
||||
ESP_ERROR_CHECK( gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT) );
|
||||
ESP_ERROR_CHECK( gpio_isr_handler_add(gpio_context->gpio, gpio_isr_handler, (void *)gpio_context) );
|
||||
ESP_ERROR_CHECK( gpio_intr_enable(gpio_context->gpio) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Task to handle gpio events.
|
||||
*
|
||||
* The gpio initialization section also uses a publish-subscribe mechanism.
|
||||
* After creating an event queue in task, put a gpio initialization event
|
||||
* directly into the queue.
|
||||
* The gpio initialization is divided into 5 steps:
|
||||
* 1. gpio initialization
|
||||
* 2. wait for gpio state to stabilize
|
||||
* 3. enable wakeup
|
||||
* 4. set gpio interrupt
|
||||
* 5. gpio interrupt enable
|
||||
*
|
||||
* @param args The parameter passed in when creating the task, in this case of type 'gpio_ctrl_context_t'.
|
||||
*/
|
||||
static void gpio_event_task(void *args)
|
||||
{
|
||||
gpio_ctrl_context_t *object = (gpio_ctrl_context_t *)args;
|
||||
example_gpio_msg_t recv_msg, send_msg;
|
||||
|
||||
object->event_queue = xQueueCreate(DEFAULT_EVENT_QUEUE_LEN, sizeof(example_gpio_msg_t));
|
||||
if (!(object->event_queue)) {
|
||||
ESP_LOGE(TAG, "create gpio event queue failed!");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* When the task first starts running, it sends a gpio init event to itself to complete initialization */
|
||||
send_msg.event = GPIO_EVENT_INIT;
|
||||
if (pdFALSE == xQueueSend(object->event_queue, (void *)&send_msg, 0)) {
|
||||
ESP_LOGE(TAG, "send gpio init event failed!");
|
||||
abort();
|
||||
}
|
||||
|
||||
while (xQueueReceive(object->event_queue, (void *)&recv_msg, (TickType_t)portMAX_DELAY)) {
|
||||
switch (recv_msg.event) {
|
||||
case GPIO_EVENT_INIT:
|
||||
example_gpio_initialization(object);
|
||||
example_wait_gpio_inactive(object);
|
||||
example_gpio_wakeup_enable(object);
|
||||
example_gpio_set_intr_type(object);
|
||||
example_gpio_intr_enable(object);
|
||||
ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
|
||||
ESP_LOGI(TAG, "GPIO control initialization complete");
|
||||
break;
|
||||
|
||||
case GPIO_EVENT_CHECK_PULSE:
|
||||
if (gpio_get_level(object->gpio) != object->control_level) {
|
||||
send_msg.event = object->lock_state ? GPIO_EVENT_RELEASE_PM_LOCK : GPIO_EVENT_ACQUIRE_PM_LOCK;
|
||||
} else {
|
||||
send_msg.event = GPIO_EVENT_CHECK_PULSE;
|
||||
}
|
||||
|
||||
if (pdFALSE == xQueueSend(object->event_queue, (void *)&send_msg, 0)) {
|
||||
ESP_LOGE(TAG, "send event failed!");
|
||||
}
|
||||
break;
|
||||
|
||||
case GPIO_EVENT_RELEASE_PM_LOCK:
|
||||
ESP_ERROR_CHECK( esp_pm_lock_release(object->pm_lock) );
|
||||
object->lock_state = RELEASED_LOCK;
|
||||
ESP_ERROR_CHECK( gpio_intr_enable(object->gpio) );
|
||||
ESP_LOGI(TAG, "Released lock at [%p], system may sleep", object->pm_lock);
|
||||
break;
|
||||
|
||||
case GPIO_EVENT_ACQUIRE_PM_LOCK:
|
||||
ESP_ERROR_CHECK( esp_pm_lock_acquire(object->pm_lock) );
|
||||
object->lock_state = ACQUIRED_LOCK;
|
||||
ESP_ERROR_CHECK( gpio_intr_enable(object->gpio) );
|
||||
ESP_LOGI(TAG, "Acquired lock at [%p], system remains active", object->pm_lock);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register external power control - gpio control.
|
||||
*
|
||||
* To avoid problems caused by configuring wakeups when
|
||||
* locks have not yet been created, power management
|
||||
* configure should be done first. Then set some parameters.
|
||||
* Finally create a task for handling gpio events.
|
||||
*
|
||||
* @param gpio_context Important information to be used for gpio control
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t example_register_gpio_control(gpio_ctrl_context_t *gpio_context)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(example_register_power_config(&(gpio_context->pm_lock), &(gpio_context->lock_state)), TAG, "PM lock initialization failed!");
|
||||
|
||||
gpio_context->gpio = DEFAULT_GPIO_CONTROL_NUM;
|
||||
#if DEFAULT_USE_PULSE_CONTROL
|
||||
gpio_context->control_mode = GPIO_PULSE_CONTROL;
|
||||
#else
|
||||
gpio_context->control_mode = GPIO_LEVEL_CONTROL;
|
||||
#endif
|
||||
gpio_context->control_level = DEFAULT_GPIO_CONTROL_LEVEL;
|
||||
|
||||
if(pdPASS != xTaskCreate(gpio_event_task, "gpio_event_task", DEFAULT_EVENT_TASK_STACK_SIZE,
|
||||
(void *)gpio_context, DEFAULT_EVENT_TASK_PRIORITY, &(gpio_context->task_handle))) {
|
||||
ESP_LOGE(TAG, "create event task failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
#include "esp_pm.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "external_power_control_example.h"
|
||||
|
||||
/**
|
||||
* @brief GPIO control mode
|
||||
*/
|
||||
typedef enum {
|
||||
GPIO_LEVEL_CONTROL = 0, /*!< level control mode */
|
||||
GPIO_PULSE_CONTROL /*!< pulse control mode */
|
||||
} gpio_control_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Important information to be used for gpio control
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_control_mode_t control_mode; /*!< gpio control mode */
|
||||
int control_level; /*!< level of trigger control */
|
||||
gpio_num_t gpio; /*!< pin for control */
|
||||
TaskHandle_t task_handle; /*!< a task to handle gpio events */
|
||||
QueueHandle_t event_queue; /*!< event queue */
|
||||
bool lock_state; /*!< record the status of the current operating lock, acquired lock or released lock */
|
||||
esp_pm_lock_handle_t pm_lock; /*!< power management lock */
|
||||
} gpio_ctrl_context_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
esp_err_t example_register_gpio_control(gpio_ctrl_context_t *gpio_context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "esp_pm.h"
|
||||
#include "esp_check.h"
|
||||
#include "gpio_control/gpio_control.h"
|
||||
#include "uart_control/uart_control.h"
|
||||
#include "external_power_control_example.h"
|
||||
|
||||
/* Max and min frequencies set by menuconfig */
|
||||
#define DEFAULT_PM_MAX_FREQ_MHZ (CONFIG_EXAMPLE_PM_MAX_FREQ_MHZ)
|
||||
#define DEFAULT_PM_MIN_FREQ_MHZ (CONFIG_EXAMPLE_PM_MIN_FREQ_MHZ)
|
||||
|
||||
static const char *TAG = "power_config";
|
||||
|
||||
/**
|
||||
* @brief Initialize power management unit.
|
||||
*
|
||||
* The system should remain active after initialization.
|
||||
* The function first creates a lock based on the user-selected
|
||||
* lock type, stored in the parameter `pm_lock`. The power management
|
||||
* unit is then configured according to the set maximum and minimum
|
||||
* frequencies. Immediately after that the lock should be acquired,
|
||||
* thus ensuring that the system is active. Finally the current lock
|
||||
* status is then recorded: acquired.
|
||||
*
|
||||
* @param pm_lock the power management lock
|
||||
* @param lock_state record whether the power management lock was acquired or released
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t example_register_power_config(esp_pm_lock_handle_t *pm_lock, bool *lock_state)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR( esp_pm_lock_create(DEFAULT_PM_LOCK_TYPE, 0, "ctrl_auto_light_sleep", pm_lock), TAG, "create lock failed" );
|
||||
|
||||
esp_pm_config_t config = {
|
||||
.max_freq_mhz = DEFAULT_PM_MAX_FREQ_MHZ,
|
||||
.min_freq_mhz = DEFAULT_PM_MIN_FREQ_MHZ,
|
||||
.light_sleep_enable = true
|
||||
};
|
||||
ESP_RETURN_ON_ERROR( esp_pm_configure(&config), TAG, "auto light sleep config failed" );
|
||||
|
||||
// after power on, it should remain active, so it should acquire lock
|
||||
ESP_RETURN_ON_ERROR( esp_pm_lock_acquire(*pm_lock), TAG, "acquire lock failed" );
|
||||
ESP_LOGI(TAG, "Acquired lock at [%p], system remains active", *pm_lock);
|
||||
*lock_state = ACQUIRED_LOCK;
|
||||
return ESP_OK;
|
||||
}
|
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_check.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_timer.h"
|
||||
#include "soc/uart_pins.h"
|
||||
#include "driver/uart.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "uart_control.h"
|
||||
|
||||
/* Set the UART used for the UART control */
|
||||
#define DEFAULT_UART_NUM (CONFIG_EXAMPLE_UART_NUM)
|
||||
|
||||
/* Set the tx pin of the uart */
|
||||
#define DEFAULT_UART_TX_IO_NUM (CONFIG_EXAMPLE_UART_TX_IO_NUM)
|
||||
|
||||
/* Set the rx pin of the uart */
|
||||
#define DEFAULT_UART_RX_IO_NUM (CONFIG_EXAMPLE_UART_RX_IO_NUM)
|
||||
|
||||
/* Set the cts pin of the uart */
|
||||
#define DEFAULT_UART_CTS_IO_NUM (CONFIG_EXAMPLE_UART_CTS_IO_NUM)
|
||||
|
||||
/* Set the rts pin of the uart */
|
||||
#define DEFAULT_UART_RTS_IO_NUM (CONFIG_EXAMPLE_UART_RTS_IO_NUM)
|
||||
|
||||
/* Set the number of rising edges used for uart wakeup */
|
||||
#define DEFAULT_UART_WAKEUP_THRESHOLD (CONFIG_EXAMPLE_UART_WAKEUP_THRESHOLD)
|
||||
|
||||
/* Set the timeout for the sleep timer */
|
||||
#define DEFAULT_SLEEP_TIMEOUT_US (CONFIG_EXAMPLE_SLEEP_TIMEOUT_US)
|
||||
|
||||
#define EXAMPLE_READ_BUF_SIZE 1024
|
||||
#define EXAMPLE_UART_BUF_SIZE (EXAMPLE_READ_BUF_SIZE * 2)
|
||||
|
||||
static const char *TAG = "uart_control";
|
||||
|
||||
/**
|
||||
* @brief sleep_timer callback.
|
||||
*
|
||||
* The callback's responsibility is to release the lock and record that the lock has been released.
|
||||
*
|
||||
* @param args The parameter passed in when creating the timer, in this case a parameter of type 'uart_ctrl_context_t'.
|
||||
*/
|
||||
static void sleep_timer_handler(void *args)
|
||||
{
|
||||
uart_ctrl_context_t *object = (uart_ctrl_context_t *)args;
|
||||
ESP_ERROR_CHECK( esp_pm_lock_release(object->pm_lock) );
|
||||
object->lock_state = RELEASED_LOCK;
|
||||
ESP_LOGI(TAG, "Released lock at [%p], system may sleep", object->pm_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the sleep timer.
|
||||
*
|
||||
* Create a timer and turn on one-time timing.
|
||||
*
|
||||
* @param uart_context Important information to be used for uart control
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
static esp_err_t example_sleep_timer_config(uart_ctrl_context_t *uart_context)
|
||||
{
|
||||
/* Initialize sleep timer */
|
||||
const esp_timer_create_args_t config = {
|
||||
.callback = &sleep_timer_handler,
|
||||
.arg = (void *)uart_context,
|
||||
.name = "sleep_timer"
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_timer_create(&config, &(uart_context->sleep_timer)), TAG, "Configure sleep timer failed!");
|
||||
|
||||
// Once the timer is configured, it should be turn on
|
||||
ESP_RETURN_ON_ERROR(esp_timer_start_once(uart_context->sleep_timer, uart_context->sleep_timeout), TAG, "Failed to turn on sleep timer");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The task that handles the uart event.
|
||||
*
|
||||
* Since the example uses a timer to realize the
|
||||
* function of releasing the lock, the task configures
|
||||
* the timer first, and then handles the uart event.
|
||||
*
|
||||
* @param args The parameter passed in when creating the task, in this case of type 'uart_ctrl_context_t'.
|
||||
*/
|
||||
static void uart_event_task(void *args)
|
||||
{
|
||||
uart_ctrl_context_t *object = (uart_ctrl_context_t *)args;
|
||||
uart_event_t event;
|
||||
|
||||
if (object->event_queue == NULL) {
|
||||
ESP_LOGE(TAG, "uart event queue is NULL!");
|
||||
abort();
|
||||
}
|
||||
uint8_t *dtmp = (uint8_t *)malloc(EXAMPLE_READ_BUF_SIZE);
|
||||
|
||||
/* Start sleep timer */
|
||||
ESP_ERROR_CHECK( example_sleep_timer_config(object) );
|
||||
|
||||
while (xQueueReceive(object->event_queue, (void *)&event, (TickType_t)portMAX_DELAY)) {
|
||||
switch (event.type) {
|
||||
case UART_DATA:
|
||||
/* When the power management lock of the uart_control has been released, the uart should pause its work,
|
||||
and then come uart data, which is incorrect data. (See README.md for details) */
|
||||
if (!(object->lock_state)) {
|
||||
ESP_LOGE(TAG, "uart receives untimely data!");
|
||||
abort();
|
||||
}
|
||||
ESP_ERROR_CHECK( esp_timer_restart(object->sleep_timer, object->sleep_timeout) );
|
||||
|
||||
// receive uart data
|
||||
ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
|
||||
uart_read_bytes(object->uart_num, dtmp, event.size, (TickType_t)portMAX_DELAY);
|
||||
|
||||
// Retransmit the received data
|
||||
ESP_LOGI(TAG, "[DATA EVT]:");
|
||||
uart_write_bytes(object->uart_num, (const char *)dtmp, event.size);
|
||||
break;
|
||||
// Event of HW FIFO overflow detected
|
||||
case UART_FIFO_OVF:
|
||||
ESP_LOGI(TAG, "hw fifo overflow");
|
||||
// If fifo overflow happened, you should consider adding flow control for your application.
|
||||
// The ISR has already reset the rx FIFO,
|
||||
// As an example, we directly flush the rx buffer here in order to read more data.
|
||||
uart_flush_input(object->uart_num);
|
||||
xQueueReset(object->event_queue);
|
||||
break;
|
||||
// Event of UART ring buffer full
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGI(TAG, "ring buffer full");
|
||||
// If buffer full happened, you should consider encreasing your buffer size
|
||||
// As an example, we directly flush the rx buffer here in order to read more data.
|
||||
uart_flush_input(object->uart_num);
|
||||
xQueueReset(object->event_queue);
|
||||
break;
|
||||
// Event of UART RX break detected
|
||||
case UART_BREAK:
|
||||
ESP_LOGI(TAG, "uart rx break");
|
||||
break;
|
||||
// Event of UART parity check error
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGI(TAG, "uart parity error");
|
||||
break;
|
||||
// Event of UART frame error
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGI(TAG, "uart frame error");
|
||||
break;
|
||||
// ESP32 can wakeup by uart but there is no wake up interrupt
|
||||
#if SOC_UART_SUPPORT_WAKEUP_INT
|
||||
// Event of wakeup by UART
|
||||
case UART_WAKEUP:
|
||||
ESP_ERROR_CHECK( esp_pm_lock_acquire(object->pm_lock) );
|
||||
object->lock_state = ACQUIRED_LOCK;
|
||||
ESP_LOGI(TAG, "Acquired lock at [%p], system remains active", object->pm_lock);
|
||||
ESP_ERROR_CHECK( esp_timer_start_once(object->sleep_timer, object->sleep_timeout) );
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ESP_LOGI(TAG, "uart event type: %d", event.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(dtmp);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize uart.
|
||||
*
|
||||
* Take special care when setting the uart clock source!
|
||||
* Because the DFS function will change the frequency,
|
||||
* which will cause the uart baud rate to be wrong,
|
||||
* resulting in garbled output.
|
||||
* Configure the basic flow of uart:
|
||||
* 1.Set communication parameters
|
||||
* 2.Set communication pins
|
||||
* 3.Install Drivers
|
||||
*
|
||||
* @param uart_context Important information to be used for uart control
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
static esp_err_t example_uart_initialization(uart_ctrl_context_t *uart_context)
|
||||
{
|
||||
/* Set communication parameters */
|
||||
uart_config_t config = {
|
||||
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
/* Note: there is a conflict between the uart clock source and the DFS function,
|
||||
so it is very important to choose the right clock source (detailed description in the README.md) */
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
.source_clk = UART_SCLK_REF_TICK,
|
||||
#else
|
||||
.source_clk = UART_SCLK_XTAL,
|
||||
#endif
|
||||
};
|
||||
ESP_RETURN_ON_ERROR( uart_param_config(uart_context->uart_num, &config), TAG, "Configure uart param failed" );
|
||||
|
||||
/* Set communication pins */
|
||||
ESP_RETURN_ON_ERROR( uart_set_pin(uart_context->uart_num, uart_context->tx_io_num, uart_context->rx_io_num,
|
||||
uart_context->rts_io_num, uart_context->cts_io_num), TAG, "Configure uart gpio pins failed" );
|
||||
|
||||
/* Install uart driver, and get the queue */
|
||||
ESP_RETURN_ON_ERROR( uart_driver_install(uart_context->uart_num, EXAMPLE_UART_BUF_SIZE, EXAMPLE_UART_BUF_SIZE,
|
||||
DEFAULT_EVENT_QUEUE_LEN, &(uart_context->event_queue), 0), TAG, "Install uart failed" );
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure uart wakeup.
|
||||
*
|
||||
* UART will wakeup the chip up from light sleep
|
||||
* if the edges that RX pin received has reached
|
||||
* the threshold. Besides, the Rx pin need extra
|
||||
* configuration to enable it can work during light
|
||||
* sleep (The rx pin should be held high).
|
||||
*
|
||||
* @param uart_num The uart to be configured, typically uart0 or uart1.
|
||||
* @param rx_io_num The rx pin number of uart.
|
||||
* @param wakeup_threshold Threshold for the pos-edge used for wakeup.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
static esp_err_t example_uart_wakeup_config(int uart_num, int rx_io_num, int wakeup_threshold)
|
||||
{
|
||||
/* UART will wakeup the chip up from light sleep if the edges that RX pin received has reached the threshold
|
||||
* Besides, the Rx pin need extra configuration to enable it can work during light sleep */
|
||||
|
||||
// Configure the rx pin to work during light sleep
|
||||
ESP_RETURN_ON_ERROR( gpio_sleep_set_direction(rx_io_num, GPIO_MODE_INPUT), TAG, "Set uart sleep gpio failed" );
|
||||
// Set internal pull-up to prevent pin from floating during light sleep. The rx pin should be held high.
|
||||
ESP_RETURN_ON_ERROR( gpio_sleep_set_pull_mode(rx_io_num, GPIO_PULLUP_ONLY), TAG, "Set uart sleep gpio failed" );
|
||||
ESP_RETURN_ON_ERROR( uart_set_wakeup_threshold(uart_num, wakeup_threshold), TAG, "Set uart wakeup threshold failed" );
|
||||
// Enable uart wakeup: Only uart0 and uart1 support to be configured as wakeup source
|
||||
ESP_RETURN_ON_ERROR( esp_sleep_enable_uart_wakeup(uart_num), TAG, "Configure uart as wakeup source failed" );
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register external power control - uart control.
|
||||
*
|
||||
* To avoid problems caused by configuring wakeups when
|
||||
* locks have not yet been created, power management
|
||||
* configure should be done first. Then initialize the
|
||||
* uart according to the set parameters. when the uart
|
||||
* is successfully initialized, then configure the uart
|
||||
* to wake up. Once the initial setup is complete, a task
|
||||
* is created to handle uart events.
|
||||
*
|
||||
* @param uart_context Important information to be used for uart control
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t example_register_uart_control(uart_ctrl_context_t *uart_context)
|
||||
{
|
||||
// power management config
|
||||
ESP_RETURN_ON_ERROR( example_register_power_config(&(uart_context->pm_lock), &(uart_context->lock_state)), TAG, "pm lock initialization failed!" );
|
||||
|
||||
/* Set Parameters */
|
||||
uart_context->event_queue = NULL;
|
||||
uart_context->uart_num = DEFAULT_UART_NUM;
|
||||
uart_context->tx_io_num = DEFAULT_UART_TX_IO_NUM;
|
||||
uart_context->rx_io_num = DEFAULT_UART_RX_IO_NUM;
|
||||
uart_context->cts_io_num = DEFAULT_UART_CTS_IO_NUM;
|
||||
uart_context->rts_io_num = DEFAULT_UART_RTS_IO_NUM;
|
||||
uart_context->wakeup_threshold = DEFAULT_UART_WAKEUP_THRESHOLD;
|
||||
uart_context->sleep_timeout = DEFAULT_SLEEP_TIMEOUT_US;
|
||||
|
||||
ESP_RETURN_ON_ERROR( example_uart_initialization(uart_context), TAG, "Initialize uart%d failed", uart_context->uart_num );
|
||||
ESP_RETURN_ON_ERROR( example_uart_wakeup_config(uart_context->uart_num, uart_context->rx_io_num, uart_context->wakeup_threshold),
|
||||
TAG, "Configure uart as wakeup source failed" );
|
||||
|
||||
xTaskCreate(uart_event_task, "uart_event_task", DEFAULT_EVENT_TASK_STACK_SIZE,
|
||||
(void *)uart_context, DEFAULT_EVENT_TASK_PRIORITY, &(uart_context->task_handle));
|
||||
ESP_LOGI(TAG, "UART control initialization complete");
|
||||
return ESP_OK;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
#include "esp_pm.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "external_power_control_example.h"
|
||||
|
||||
/**
|
||||
* @brief Important information to be used for uart control
|
||||
*/
|
||||
typedef struct {
|
||||
int uart_num; /*!< uart0 or uart1 */
|
||||
int tx_io_num; /*!< io pin used by tx */
|
||||
int rx_io_num; /*!< io pin used by rx */
|
||||
int rts_io_num; /*!< io pin used by rts */
|
||||
int cts_io_num; /*!< io pin used by cts */
|
||||
int wakeup_threshold; /*!< uart wakeup threshold */
|
||||
TaskHandle_t task_handle; /*!< a task to handle uart events */
|
||||
QueueHandle_t event_queue; /*!< event queue */
|
||||
esp_timer_handle_t sleep_timer; /*!< timer for release power management lock */
|
||||
int sleep_timeout; /*!< wait for some time, if no data is transferred from uart, then ralease lock */
|
||||
bool lock_state; /*!< record the status of the current operating lock, acquired lock or released lock */
|
||||
esp_pm_lock_handle_t pm_lock; /*!< power management lock */
|
||||
} uart_ctrl_context_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
esp_err_t example_register_uart_control(uart_ctrl_context_t *uart_context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|