update: 添加代码

This commit is contained in:
DuRuofu
2025-02-16 09:22:48 +08:00
parent 621725c132
commit a209d1b057
46 changed files with 2375 additions and 20 deletions

View File

@@ -18,9 +18,10 @@
### 目录:
![](attachments/20250102082222.png)
![](attachments/目录.png)
本教程共八个章节三个阶段基础1-3实践4进阶5-10
本教程共八个章节三个阶段基础1-3实践4进阶5-8
其中基础章节一定要确保自己掌握,实践和进阶部分推荐按照个人需求进行选学。
<div STYLE="page-break-after: always;"></div>
@@ -520,25 +521,16 @@ MCPWM 外设是一个多功能 PWM 生成器,集成多个子模块,在电力
- 图形界面优化技巧
---
## 九、其他实用内容
## 九、项目实战
> 补充一些综合性性案例,与实用功能,为前八章做补充。
## 十、项目实战
> 目标通过综合项目实践整合所学知识独立开发基于ESP32的完整应用提升实战能力。
### 9.1 实战项目:智能家庭控制中心
- 多外设联动
- 无线通信与协议实现
- 图形界面集成
## 十、补充内存
> 补充一些零碎的遗漏内容,总结一些开发经验
## 参考链接

Binary file not shown.

Before

Width:  |  Height:  |  Size: 854 KiB

BIN
attachments/目录.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 KiB

5
code/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
**/build/
**managed_components/
**sdkconfig
**sdkconfig.old
.vscode

View File

@@ -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.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(hello_world)

View File

@@ -0,0 +1,53 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | Linux |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | ----- |
# Hello World Example
Starts a FreeRTOS task to print "Hello World".
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## How to use example
Follow detailed instructions provided specifically for this example.
Select the instructions depending on Espressif chip installed on your development board:
- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html)
- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
## Example folder contents
The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main).
ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both).
Below is short explanation of remaining files in the project folder.
```
├── CMakeLists.txt
├── pytest_hello_world.py Python script used for automated testing
├── main
│ ├── CMakeLists.txt
│ └── hello_world_main.c
└── README.md This is the file you are currently reading
```
For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide.
## Troubleshooting
* Program upload failure
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
## Technical support and feedback
Please use the following feedback channels:
* For technical queries, go to the [esp32.com](https://esp32.com/) forum
* For a feature request or bug report, create a [GitHub issue](https://github.com/espressif/esp-idf/issues)
We will get back to you as soon as possible.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "hello_world_main.c"
INCLUDE_DIRS "")

View File

@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
void app_main(void)
{
printf("Hello world!\n");
/* Print chip information */
esp_chip_info_t chip_info;
uint32_t flash_size;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
(chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
(chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");
unsigned major_rev = chip_info.revision / 100;
unsigned minor_rev = chip_info.revision % 100;
printf("silicon revision v%d.%d, ", major_rev, minor_rev);
if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
printf("Get flash size failed");
return;
}
printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}

View File

@@ -0,0 +1,53 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import hashlib
import logging
from typing import Callable
import pytest
from pytest_embedded_idf.dut import IdfDut
from pytest_embedded_qemu.app import QemuApp
from pytest_embedded_qemu.dut import QemuDut
@pytest.mark.supported_targets
@pytest.mark.preview_targets
@pytest.mark.generic
def test_hello_world(
dut: IdfDut, log_minimum_free_heap_size: Callable[..., None]
) -> None:
dut.expect('Hello world!')
log_minimum_free_heap_size()
@pytest.mark.linux
@pytest.mark.host_test
def test_hello_world_linux(dut: IdfDut) -> None:
dut.expect('Hello world!')
def verify_elf_sha256_embedding(app: QemuApp, sha256_reported: str) -> None:
sha256 = hashlib.sha256()
with open(app.elf_file, 'rb') as f:
sha256.update(f.read())
sha256_expected = sha256.hexdigest()
logging.info(f'ELF file SHA256: {sha256_expected}')
logging.info(f'ELF file SHA256 (reported by the app): {sha256_reported}')
# the app reports only the first several hex characters of the SHA256, check that they match
if not sha256_expected.startswith(sha256_reported):
raise ValueError('ELF file SHA256 mismatch')
@pytest.mark.esp32 # we only support qemu on esp32 for now
@pytest.mark.host_test
@pytest.mark.qemu
def test_hello_world_host(app: QemuApp, dut: QemuDut) -> None:
sha256_reported = (
dut.expect(r'ELF file SHA256:\s+([a-f0-9]+)').group(1).decode('utf-8')
)
verify_elf_sha256_embedding(app, sha256_reported)
dut.expect('Hello world!')

View File

View File

@@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five 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(main)

View File

@@ -0,0 +1,35 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
# _Sample project_
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This is the simplest buildable example. The example is used by command `idf.py create-project`
that copies the project to user specified path and set it's name. For more information follow the [docs page](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#start-a-new-project)
## How to use example
We encourage the users to use the example as a template for the new projects.
A recommended way is to follow the instructions on a [docs page](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#start-a-new-project).
## Example folder contents
The project **sample_project** contains one source file in C language [main.c](main/main.c). The file is located in folder [main](main).
ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt`
files that provide set of directives and instructions describing the project's source files and targets
(executable, library, or both).
Below is short explanation of remaining files in the project folder.
```
├── CMakeLists.txt
├── main
│   ├── CMakeLists.txt
│   └── main.c
└── README.md This is the file you are currently reading
```
Additionally, the sample project contains Makefile and component.mk files, used for the legacy Make based build system.
They are not used or needed when building with CMake and idf.py.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,6 @@
#include <stdio.h>
void app_main(void)
{
}

View File

@@ -0,0 +1,6 @@
# The following five 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(blink)

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "blink_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,49 @@
menu "Example Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
choice BLINK_LED
prompt "Blink LED type"
default BLINK_LED_GPIO
help
Select the LED type. A normal level controlled LED or an addressable LED strip.
The default selection is based on the Espressif DevKit boards.
You can change the default selection according to your board.
config BLINK_LED_GPIO
bool "GPIO"
config BLINK_LED_STRIP
bool "LED strip"
endchoice
choice BLINK_LED_STRIP_BACKEND
depends on BLINK_LED_STRIP
prompt "LED strip backend peripheral"
default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
default BLINK_LED_STRIP_BACKEND_SPI
help
Select the backend peripheral to drive the LED strip.
config BLINK_LED_STRIP_BACKEND_RMT
depends on SOC_RMT_SUPPORTED
bool "RMT"
config BLINK_LED_STRIP_BACKEND_SPI
bool "SPI"
endchoice
config BLINK_GPIO
int "Blink GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 8
help
GPIO number (IOxx) to blink on and off the LED.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
config BLINK_PERIOD
int "Blink period in ms"
range 10 3600000
default 1000
help
Define the blinking period in milliseconds.
endmenu

View File

@@ -0,0 +1,104 @@
/* Blink 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 "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "led_strip.h"
#include "sdkconfig.h"
static const char *TAG = "example";
/* Use project configuration menu (idf.py menuconfig) to choose the GPIO to blink,
or you can edit the following line and set a number here.
*/
#define BLINK_GPIO CONFIG_BLINK_GPIO
static uint8_t s_led_state = 0;
#ifdef CONFIG_BLINK_LED_STRIP
static led_strip_handle_t led_strip;
static void blink_led(void)
{
/* If the addressable LED is enabled */
if (s_led_state) {
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
led_strip_set_pixel(led_strip, 0, 16, 16, 16);
/* Refresh the strip to send data */
led_strip_refresh(led_strip);
} else {
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
}
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink addressable LED!");
/* LED strip initialization with the GPIO and pixels number*/
led_strip_config_t strip_config = {
.strip_gpio_num = BLINK_GPIO,
.max_leds = 1, // at least one LED on board
};
#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT
led_strip_rmt_config_t rmt_config = {
.resolution_hz = 10 * 1000 * 1000, // 10MHz
.flags.with_dma = false,
};
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI
led_strip_spi_config_t spi_config = {
.spi_bus = SPI2_HOST,
.flags.with_dma = true,
};
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
#else
#error "unsupported LED strip backend"
#endif
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
}
#elif CONFIG_BLINK_LED_GPIO
static void blink_led(void)
{
/* Set the GPIO level according to the state (LOW or HIGH)*/
gpio_set_level(BLINK_GPIO, s_led_state);
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
gpio_reset_pin(BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}
#else
#error "unsupported LED type"
#endif
void app_main(void)
{
/* Configure the peripheral according to the LED type */
configure_led();
while (1) {
ESP_LOGI(TAG, "Turning the LED %s!", s_led_state == true ? "ON" : "OFF");
blink_led();
/* Toggle the LED state */
s_led_state = !s_led_state;
vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS);
}
}

View File

@@ -0,0 +1,2 @@
dependencies:
espressif/led_strip: "^2.4.1"

View File

@@ -0,0 +1,2 @@
CONFIG_BLINK_LED_GPIO=y
CONFIG_BLINK_GPIO=8

View File

@@ -0,0 +1,6 @@
# The following five 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(blink)

View File

@@ -0,0 +1,69 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
# Blink Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to blink a LED by using the GPIO driver or using the [led_strip](https://components.espressif.com/component/espressif/led_strip) library if the LED is addressable e.g. [WS2812](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf). The `led_strip` library is installed via [component manager](main/idf_component.yml).
## How to Use Example
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
### Hardware Required
* A development board with normal LED or addressable LED on-board (e.g., ESP32-S3-DevKitC, ESP32-C6-DevKitC etc.)
* A USB cable for Power supply and programming
See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it.
### Configure the Project
Open the project configuration menu (`idf.py menuconfig`).
In the `Example Configuration` menu:
* Select the LED type in the `Blink LED type` option.
* Use `GPIO` for regular LED
* Use `LED strip` for addressable LED
* If the LED type is `LED strip`, select the backend peripheral
* `RMT` is only available for ESP targets with RMT peripheral supported
* `SPI` is available for all ESP targets
* Set the GPIO number used for the signal in the `Blink GPIO number` option.
* Set the blinking period in the `Blink period in ms` option.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(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
As you run the example, you will see the LED blinking, according to the previously defined period. For the addressable LED, you can also change the LED color by setting the `led_strip_set_pixel(led_strip, 0, 16, 16, 16);` (LED Strip, Pixel Number, Red, Green, Blue) with values from 0 to 255 in the [source file](main/blink_example_main.c).
```text
I (315) example: Example configured to blink addressable LED!
I (325) example: Turning the LED OFF!
I (1325) example: Turning the LED ON!
I (2325) example: Turning the LED OFF!
I (3325) example: Turning the LED ON!
I (4325) example: Turning the LED OFF!
I (5325) example: Turning the LED ON!
I (6325) example: Turning the LED OFF!
I (7325) example: Turning the LED ON!
I (8325) example: Turning the LED OFF!
```
Note: The color order could be different according to the LED model.
The pixel number indicates the pixel position in the LED strip. For a single LED, use 0.
## Troubleshooting
* If the LED isn't blinking, check the GPIO or the LED type selection in the `Example Configuration` menu.
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@@ -0,0 +1,20 @@
dependencies:
espressif/led_strip:
component_hash: 28c6509a727ef74925b372ed404772aeedf11cce10b78c3f69b3c66799095e2d
dependencies:
- name: idf
require: private
version: '>=4.4'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.5.5
idf:
source:
type: idf
version: 5.3.2
direct_dependencies:
- espressif/led_strip
manifest_hash: a9af7824fb34850fbe175d5384052634b3c00880abb2d3a7937e666d07603998
target: esp32
version: 2.0.0

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "blink_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,49 @@
menu "Example Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
choice BLINK_LED
prompt "Blink LED type"
default BLINK_LED_GPIO
help
Select the LED type. A normal level controlled LED or an addressable LED strip.
The default selection is based on the Espressif DevKit boards.
You can change the default selection according to your board.
config BLINK_LED_GPIO
bool "GPIO"
config BLINK_LED_STRIP
bool "LED strip"
endchoice
choice BLINK_LED_STRIP_BACKEND
depends on BLINK_LED_STRIP
prompt "LED strip backend peripheral"
default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
default BLINK_LED_STRIP_BACKEND_SPI
help
Select the backend peripheral to drive the LED strip.
config BLINK_LED_STRIP_BACKEND_RMT
depends on SOC_RMT_SUPPORTED
bool "RMT"
config BLINK_LED_STRIP_BACKEND_SPI
bool "SPI"
endchoice
config BLINK_GPIO
int "Blink GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 8
help
GPIO number (IOxx) to blink on and off the LED.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
config BLINK_PERIOD
int "Blink period in ms"
range 10 3600000
default 1000
help
Define the blinking period in milliseconds.
endmenu

View File

@@ -0,0 +1,104 @@
/* Blink 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 "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "led_strip.h"
#include "sdkconfig.h"
static const char *TAG = "example";
/* Use project configuration menu (idf.py menuconfig) to choose the GPIO to blink,
or you can edit the following line and set a number here.
*/
#define BLINK_GPIO CONFIG_BLINK_GPIO
static uint8_t s_led_state = 0;
#ifdef CONFIG_BLINK_LED_STRIP
static led_strip_handle_t led_strip;
static void blink_led(void)
{
/* If the addressable LED is enabled */
if (s_led_state) {
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
led_strip_set_pixel(led_strip, 0, 16, 16, 16);
/* Refresh the strip to send data */
led_strip_refresh(led_strip);
} else {
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
}
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink addressable LED!");
/* LED strip initialization with the GPIO and pixels number*/
led_strip_config_t strip_config = {
.strip_gpio_num = BLINK_GPIO,
.max_leds = 1, // at least one LED on board
};
#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT
led_strip_rmt_config_t rmt_config = {
.resolution_hz = 10 * 1000 * 1000, // 10MHz
.flags.with_dma = false,
};
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI
led_strip_spi_config_t spi_config = {
.spi_bus = SPI2_HOST,
.flags.with_dma = true,
};
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
#else
#error "unsupported LED strip backend"
#endif
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
}
#elif CONFIG_BLINK_LED_GPIO
static void blink_led(void)
{
/* Set the GPIO level according to the state (LOW or HIGH)*/
gpio_set_level(BLINK_GPIO, s_led_state);
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
gpio_reset_pin(BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}
#else
#error "unsupported LED type"
#endif
void app_main(void)
{
/* Configure the peripheral according to the LED type */
configure_led();
while (1) {
ESP_LOGI(TAG, "Turning the LED %s!", s_led_state == true ? "ON" : "OFF");
blink_led();
/* Toggle the LED state */
s_led_state = !s_led_state;
vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS);
}
}

View File

@@ -0,0 +1,2 @@
dependencies:
espressif/led_strip: "^2.4.1"

View File

@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import logging
import os
import pytest
from pytest_embedded_idf.dut import IdfDut
@pytest.mark.supported_targets
@pytest.mark.generic
def test_blink(dut: IdfDut) -> None:
# check and log bin size
binary_file = os.path.join(dut.app.binary_path, 'blink.bin')
bin_size = os.path.getsize(binary_file)
logging.info('blink_bin_size : {}KB'.format(bin_size // 1024))

View File

@@ -0,0 +1,2 @@
CONFIG_BLINK_LED_GPIO=y
CONFIG_BLINK_GPIO=8

View File

@@ -0,0 +1 @@
CONFIG_BLINK_GPIO=5

View File

@@ -0,0 +1 @@
CONFIG_BLINK_LED_STRIP=y

View File

@@ -0,0 +1,2 @@
CONFIG_BLINK_GPIO=6
CONFIG_BLINK_LED_STRIP=y

View File

@@ -0,0 +1 @@
CONFIG_BLINK_LED_STRIP=y

View File

@@ -0,0 +1 @@
CONFIG_BLINK_LED_STRIP=y

View File

@@ -0,0 +1,2 @@
CONFIG_BLINK_LED_STRIP=y
CONFIG_BLINK_GPIO=18

View File

@@ -0,0 +1,2 @@
CONFIG_BLINK_LED_STRIP=y
CONFIG_BLINK_GPIO=48

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

View File

@@ -1,4 +1,8 @@
# 常见问题与解决
## 索引
[[toc]]
## 1. 头文件无法跳转,没有代码提示
**问题**
@@ -43,7 +47,7 @@
![](attachments/20240129232230.png)
## 2.开发板在Windows下可以显示端口但在Linux下找不到
## 2. 开发板在Windows下可以显示端口但在Linux下找不到
**问题**
@@ -56,7 +60,7 @@
安装对应型号的串口驱动程序,上网搜索安装教程一般都能找到。
## 3.Linux下烧录代码报错没有串口权限
## 3. Linux下烧录代码报错没有串口权限
**问题**
@@ -78,4 +82,53 @@ sudo chmod a+rw /dev/ttyUSB0
但是注意`sudo chmod a+rw /dev/ttyUSB0` 的权限更改是暂时的,只会在当前设备被使用的会话期间生效。一旦系统重启或者设备断开并重新连接,设备节点(例如 /dev/ttyUSB0可能会重新被创建且权限会被重置为默认状态。
可以使用下面的命令,永久为用户添加串口权限:`sudo usermod -aG dialout username`,设置后重启方能生效。
可以使用下面的命令,永久为用户添加串口权限:`sudo usermod -aG dialout username`,设置后重启方能生效。
## 4. 开发板烧录代码时出现MD5校验错误
![](attachments/Pasted%20image%2020250210094506.png)
解决办法如下:
![](attachments/Pasted%20image%2020250210094818.png)
需要在IDF环境下使用命令]
```bash
esptool.py --port PORT write_flash_status --non-volatile 0
```
其中的PORT需要修改为实际连接的开发板端口。
## 5. 烧录报错:无法验证闪存芯片连接(未收到串行数据。)
报错信息如下:
```bash
Connecting....
Chip is ESP32-D0WD-V3 (revision v3.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
WARNING: Detected crystal freq 15.32MHz is quite different to normalized freq 26MHz. Unsupported crystal in use?
Crystal is 26MHz
MAC: fc:e8:c0:7c:b4:24
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
A fatal error occurred: Unable to verify flash chip connection (No serial data received.).
CMake Error at run_serial_tool.cmake:66 (message):
/home/duruofu/.espressif/python_env/idf5.3_py3.10_env/bin/python;;/home/duruofu/esp32/esp-idf-v5.3/components/esptool_py/esptool/esptool.py;--chip;esp32
failed.
FAILED: CMakeFiles/flash /home/duruofu/project/Mock-IoT-Device-ESP32/build/CMakeFiles/flash
```
解决办法:
重新烧录引导程序长按BOOT键然后按复位键再次烧录代码即可成功烧录。

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

View File

@@ -0,0 +1,142 @@
# 信号量
FreeRTOS 提供了信号量和互斥锁,用于任务间的同步和资源共享管理。信号量更偏向于任务同步,而互斥锁用于保护共享资源。
## 1. 二进制信号量
二进制信号量是最基本的信号量,仅有两个状态:可用和不可用(或 1 和 0
通常用于任务之间或中断与任务之间的同步,当一个事件发生时,由中断或任务释放信号量,等待信号量的任务就会被唤醒。
二进制信号量适用于简单的事件通知场景,比如通知某个任务处理外部输入或完成某项任务。
### 1.1 API说明
### 1.2 示例代码:
```c
// 二进制信号量
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "main";
// 二进制信号量
SemaphoreHandle_t semaphoreHandle;
// 公共变量
int shareVariable = 0;
void task1(void *pvParameters)
{
for (;;)
{
// 获取信号量,信号量变为0
xSemaphoreTake(semaphoreHandle, portMAX_DELAY);
for (int i = 0; i < 10; i++)
{
shareVariable++;
ESP_LOGI(TAG, "task1 shareVariable:%d", shareVariable);
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 释放信号量,信号量变为1
xSemaphoreGive(semaphoreHandle);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void task2(void *pvParameters)
{
for (;;)
{
// 获取信号量
xSemaphoreTake(semaphoreHandle, portMAX_DELAY);
for (int i = 0; i < 10; i++)
{
shareVariable++;
ESP_LOGI(TAG, "task2 shareVariable:%d", shareVariable);
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 释放信号量,信号量变为1
xSemaphoreGive(semaphoreHandle);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
semaphoreHandle = xSemaphoreCreateBinary();
xSemaphoreGive(semaphoreHandle);
// 创建任务
xTaskCreate(task1, "task1", 1024 * 2, NULL, 10, NULL);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 10, NULL);
}
```
## 2. 计数信号量
### 2.1 示例代码:
```c
// 计数型信号量(占座)
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "main";
// 信号量
SemaphoreHandle_t semaphoreHandle;
// 占座任务
void task1(void *pvParameters)
{
// 定义空位
int seat = 0;
for (;;)
{
// 获取信号量
seat = uxSemaphoreGetCount(semaphoreHandle);
// 输出空位
ESP_LOGI(TAG, "当前空位:%d", seat);
// 获取信号量(占座)
if (xSemaphoreTake(semaphoreHandle, portMAX_DELAY) == pdPASS)
{
ESP_LOGI(TAG, "占座成功");
}
else
{
ESP_LOGI(TAG, "占座失败");
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// 离开座位任务
void task2(void *pvParameters)
{
for (;;)
{
vTaskDelay(pdMS_TO_TICKS(6000));
// 释放信号量
xSemaphoreGive(semaphoreHandle);
ESP_LOGI(TAG, "释放座位");
}
}
void app_main(void)
{
semaphoreHandle = xSemaphoreCreateCounting(5, 5);
// 创建占座任务
xTaskCreate(task1, "task1", 1024 * 2, NULL, 10, NULL);
// 创建离开座位任务
xTaskCreate(task2, "task2", 1024 * 2, NULL, 10, NULL);
}
```

View File

@@ -0,0 +1,175 @@
# 互斥锁
## 1. 互斥量
互斥量(互斥锁Mutex):互斥锁和二进制信号量极为相似,但 有一些细微差异:互斥锁具有优先级继承机制, 但二进制信号量没有。
### 1.1 示例代码:
```c
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "main";
SemaphoreHandle_t mutexHandle;
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "task1启动!");
while (1)
{
if (xSemaphoreTake(mutexHandle, portMAX_DELAY) == pdTRUE)
{
ESP_LOGI(TAG, "task1获取到互斥量!");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task1执行中...%d", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGive(mutexHandle);
ESP_LOGI(TAG, "task1释放互斥量!");
vTaskDelay(pdMS_TO_TICKS(1000));
}
else
{
ESP_LOGI(TAG, "task1获取互斥量失败!");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "task2启动!");
vTaskDelay(pdMS_TO_TICKS(1000));
while (1)
{
}
}
void task3(void *pvParameters)
{
ESP_LOGI(TAG, "task3启动!");
vTaskDelay(pdMS_TO_TICKS(1000));
while (1)
{
if (xSemaphoreTake(mutexHandle, 1000) == pdPASS)
{
ESP_LOGI(TAG, "task3获取到互斥量!");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task3执行中...%d",i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGive(mutexHandle);
ESP_LOGI(TAG, "task3释放互斥量!");
vTaskDelay(pdMS_TO_TICKS(5000));
}
else
{
ESP_LOGI(TAG, "task3未获取到互斥量!");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
void app_main(void)
{
mutexHandle = xSemaphoreCreateMutex();
// mutexHandle = xSemaphoreCreateBinary();
xTaskCreate(task1, "task1", 1024 * 2, NULL, 1, NULL);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 2, NULL);
xTaskCreate(task3, "task3", 1024 * 2, NULL, 3, NULL);
}
```
## 2. 递归互斥量
非递归互斥锁只能被一个任务 获取一次,如果同一个任务想再次获取则会失败, 因为当任务第一次释放互斥锁时,互斥锁就一直处于释放状态。与非递归互斥锁相反,递归互斥锁可以被同一个任务获取很多次, 获取多少次就需要释放多少次, 此时才会返回递归互斥锁。
### 2.1 示例代码:
```c
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "main";
SemaphoreHandle_t mutexHandle;
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task1启动!");
while (1)
{
xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY);
ESP_LOGI(TAG, "task1获取到互斥量-使用资源A");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task1执行中...%d -使用资源A", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY);
ESP_LOGI(TAG, "task1获取到互斥量-使用资源B");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task1执行中...%d -使用资源B", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGiveRecursive(mutexHandle);
ESP_LOGI(TAG, "task1释放互斥量-使用资源B");
vTaskDelay(pdMS_TO_TICKS(3000));
xSemaphoreGiveRecursive(mutexHandle);
ESP_LOGI(TAG, "task1释放互斥量-使用资源A");
}
vTaskDelete(NULL);
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "task2启动!");
vTaskDelay(pdMS_TO_TICKS(1000));
while (1)
{
// 获取递归互斥锁
if (xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY) == pdTRUE)
{
ESP_LOGI(TAG, "task2获取到互斥量");
for (int i = 0; i < 10; i++)
{
ESP_LOGI(TAG, "task2执行中...%d", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGiveRecursive(mutexHandle);
ESP_LOGI(TAG, "task2释放互斥量");
}
else
{
ESP_LOGI(TAG, "task2获取互斥量失败");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
void app_main(void)
{
// 创建递归互斥量
mutexHandle = xSemaphoreCreateRecursiveMutex();
xTaskCreate(task1, "task1", 1024 * 2, NULL, 1, NULL);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 2, NULL);
}
```

View File

@@ -0,0 +1,344 @@
# 事件组
事件组是一种实现任务间通信和同步的机制,主要用于协调多个任务或中断之间的执行。
**事件位(事件标志)**
事件位用于指示事件 是否发生。事件位通常称为事件标志。例如, 应用程序可以:
定义一个位(或标志), 设置为 1 时表示“已收到消息并准备好处理”, 设置为 0 时表示“没有消息等待处理”。
定义一个位(或标志), 设置为 1 时表示“应用程序已将准备发送到网络的消息排队”, 设置为 0 时表示 “没有消息需要排队准备发送到网络”。
定义一个位(或标志), 设置为 1 时表示“需要向网络发送心跳消息”, 设置为 0 时表示“不需要向网络发送心跳消息”。
**事件组**
事件组就是一组事件位。事件组中的事件位 通过位编号来引用。同样,以上面列出的三个例子为例:
事件标志组位编号 为 0 表示“已收到消息并准备好处理”。
事件标志组位编号 为 1 表示“应用程序已将准备发送到网络的消息排队”。
事件标志组位编号 为 2 表示“需要向网络发送心跳消息”。
**事件组和事件位数据类型**
事件组由 EventGroupHandle_t 类型的变量引用。
如果 configUSE_16_BIT_TICKS 设为 1则事件组中存储的位或标志数为 8 如果 configUSE_16_BIT_TICKS 设为 0则为 24。 configUSE_16_BIT_TICKS 的值取决于 任务内部实现中用于线程本地存储的数据类型。
> (ESP-IDF中默认为24位)
事件组中的所有事件位都 存储在 EventBits_t 类型的单个无符号整数变量中。事件位 0 存储在位 0 中, 事件位 1 存储在位1 中,依此类推。
下图表示一个 24 位事件组, 使用 3 个位来保存前面描述的 3 个示例事件。在图片中,仅设置了 事件位 2。包含 24 个事件位的事件组,其中只有三个在使用中)
![](attachments/20250111145800.png)
**事件组 RTOS API 函数**
提供的事件组 API 函数 允许任务在事件组中设置一个或多个事件位, 清除事件组中的一个或多个事件位, 并挂起(进入阻塞状态, 因此任务不会消耗任何处理时间)以等待 事件组中一个或多个事件位固定下来。
事件组也可用于同步任务, 创建通常称为“集合”的任务。任务同步点是 应用程序代码中的一个位置,在该位置任务将在 阻塞状态(不消耗任何 CPU 时间)下等待,直到参与同步的所有其他任务 也到达其同步点。
### 1. API说明
事件组操作主要涉及以下几个 API
| 函数名 | 功能 | 备注 |
| -------------------- | ---------------- | ------------------------- |
| xEventGroupCreate | 创建一个事件组 | 返回一个事件组句柄,供后续操作使用 |
| xEventGroupSetBits | 设置一个或多个事件标志 | 用于通知其他任务某些事件已发生 |
| xEventGroupClearBits | 清除一个或多个事件标志 | 用于复位事件标志,防止重复触发 |
| xEventGroupWaitBits | 等待一个或多个事件标志的设置状态 | 任务可以选择阻塞,直到指定事件发生 |
| xEventGroupGetBits | 查询当前事件组的状态 | 返回事件组中所有事件标志的当前状态 |
| xEventGroupSync | 同步多个任务 | 用于实现多个任务在同一时刻达到某一同步点后继续执行 |
#### xEventGroupCreate创建事件组
**原型:**
```c
EventGroupHandle_t xEventGroupCreate(void);
```
**返回值**:成功时返回事件组句柄;失败时返回 NULL。
**示例:**
```c
EventGroupHandle_t xEventGroup;
xEventGroup = xEventGroupCreate();
if (xEventGroup == NULL) {
// 创建事件组失败,处理错误
}
```
#### xEventGroupSetBits设置事件标志
**原型:**
```c
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet);
```
**参数说明**
- xEventGroup事件组句柄。
- uxBitsToSet需要设置的事件标志位按位表示例如 0x01 设置第 0 位)。
**返回值**:返回事件组在调用前的状态。
#### xEventGroupWaitBits等待事件标志
读取 RTOS 事件组中的位,选择性地进入“阻塞”状态(已设置 超时值)以等待设置单个位或一组位。无法从中断调用此函数。
**原型:**
```c
EventBits_t xEventGroupWaitBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait
);
```
**参数说明:**
- xEventGroup事件组句柄。
- uxBitsToWaitFor需要等待的事件标志位按位表示
- xClearOnExit是否在退出等待时清除指定的事件标志。
- xWaitForAllBits是否等待所有指定事件标志都被设置还是任意一个即可。
- xTicksToWait等待的最大时间以 Tick 为单位portMAX_DELAY 表示无限等待)。
**返回值**:返回当前满足条件的事件标志状态。
**示例**
```c
EventBits_t uxBits;
uxBits = xEventGroupWaitBits(
xEventGroup, // 事件组句柄
0x03, // 等待第 0 位和第 1 位
pdTRUE, // 退出等待时清除事件标志
pdFALSE, // 等待任意一个事件
portMAX_DELAY // 无限等待
);
if (uxBits & 0x01) {
// 第 0 位事件发生
}
if (uxBits & 0x02) {
// 第 1 位事件发生
}
```
#### xEventGroupSync同步任务
以原子方式设置 RTOS 事件组中的位(标志),然后等待在同一事件组中设置位的组合。此功能通常用于同步多个任务(通常称为任务集合),其中每个任务必须等待其他任务到达同步点后才能继续。
**原型**
```c
EventBits_t xEventGroupSync(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait
);
```
**参数说明**
- xEventGroup事件组句柄。
- uxBitsToSet当前任务设置的事件标志位。
- uxBitsToWaitFor需要等待的其他任务设置的事件标志位。
- xTicksToWait最大等待时间。
**返回值**
返回事件组的当前状态。
**示例**
```c
xEventGroupSync(
xEventGroup, // 事件组句柄
0x01, // 当前任务设置第 0 位
0x03, // 等待第 0 位和第 1 位都被设置
portMAX_DELAY // 无限等待
);
```
### 2 示例程序:
#### 1. 事件组等待
task1等待task2设置事件位然后执行程序
```c
// 事件组
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
static const char *TAG = "main";
EventGroupHandle_t xCreatedEventGroup;
#define BIT_0 (1 << 0)
#define BIT_4 (1 << 4)
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task1启动!");
while (1)
{
EventBits_t uxBits;
uxBits = xEventGroupWaitBits(
xCreatedEventGroup, /* The event group being tested. */
BIT_0 | BIT_4, /* The bits within the event group to wait for. */
pdTRUE, /* BIT_0 & BIT_4 should be cleared before returning. */
pdFALSE, /* Don't wait for both bits, either bit will do. */
portMAX_DELAY); /* Wait a maximum of 100ms for either bit to be set. */
if ((uxBits & (BIT_0 | BIT_4)) == (BIT_0 | BIT_4))
{
ESP_LOGI(TAG, "BIT_0 和 BIT_4 都被设置了");
}
else
{
ESP_LOGI(TAG, "BIT_0 和 BIT_4 有一个被设置了");
}
}
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "task2启动!");
vTaskDelay(pdMS_TO_TICKS(1000));
while (1)
{
xEventGroupSetBits(xCreatedEventGroup, BIT_0);
ESP_LOGI(TAG, "BIT_0 被设置");
vTaskDelay(pdMS_TO_TICKS(3000));
xEventGroupSetBits(xCreatedEventGroup, BIT_4);
ESP_LOGI(TAG, "BIT_4 被设置");
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
void app_main(void)
{
// 创建事件组
xCreatedEventGroup = xEventGroupCreate();
if (xCreatedEventGroup == NULL)
{
ESP_LOGE(TAG, "创建事件组失败");
}
else
{
xTaskCreate(task1, "task1", 1024 * 2, NULL, 1, NULL);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 1, NULL);
}
}
```
#### 2. 事件组同步
每个任务在启动后等待一段时间然后调用xEventGroupSync函数进行事件同步等待所有任务的事件位都被设置。
```c
// 事件组
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
/* Bits used by the three tasks. */
#define TASK_0_BIT (1 << 0)
#define TASK_1_BIT (1 << 1)
#define TASK_2_BIT (1 << 2)
#define ALL_SYNC_BITS (TASK_0_BIT | TASK_1_BIT | TASK_2_BIT)
static const char *TAG = "main";
EventGroupHandle_t xEventBits;
void task0(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task0启动!");
while (1)
{
vTaskDelay(pdMS_TO_TICKS(3000));
ESP_LOGI(TAG, "task0: 任务同步开始");
// 事件同步
xEventGroupSync(
xEventBits, /* The event group being tested. */
TASK_0_BIT, /* The bits within the event group to wait for. */
ALL_SYNC_BITS, /* The bits within the event group to wait for. */
portMAX_DELAY); /* Wait a maximum of 100ms for either bit to be set. */
ESP_LOGI(TAG, "task0: 任务同步完成");
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task1启动!");
while (1)
{
vTaskDelay(pdMS_TO_TICKS(4000));
ESP_LOGI(TAG, "task1: 任务同步开始");
// 事件同步
xEventGroupSync(
xEventBits, /* The event group being tested. */
TASK_1_BIT, /* The bits within the event group to wait for. */
ALL_SYNC_BITS, /* The bits within the event group to wait for. */
portMAX_DELAY); /* Wait a maximum of 100ms for either bit to be set. */
ESP_LOGI(TAG, "task1: 任务同步完成");
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task2启动!");
while (1)
{
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 任务同步开始");
// 事件同步
xEventGroupSync(
xEventBits, /* The event group being tested. */
TASK_2_BIT, /* The bits within the event group to wait for. */
ALL_SYNC_BITS, /* The bits within the event group to wait for. */
portMAX_DELAY); /* Wait a maximum of 100ms for either bit to be set. */
ESP_LOGI(TAG, "task2: 任务同步完成");
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
void app_main(void)
{
// 创建事件组
xEventBits = xEventGroupCreate();
if (xEventBits == NULL)
{
ESP_LOGE(TAG, "创建事件组失败");
}
else
{
xTaskCreate(task0, "task0", 1024 * 2, NULL, 1, NULL);
xTaskCreate(task1, "task1", 1024 * 2, NULL, 1, NULL);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 1, NULL);
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,258 @@
# 任务通知
任务通知Task Notifications是一种轻量级的任务间通信和同步机制它比队列或事件组更加高效因为它不需要动态分配内存。每个任务都内置了一个任务通知值其他任务或中断服务例程ISR可以用它来通知该任务事件的发生。
**特点**
- 每个任务都有一个任务通知值32 位整数),可以用于存储信息。
- 任务通知值默认初始化为 0。
- 一个任务可以通过 通知函数 来操作另一个任务的通知值。
- 可以将任务通知值用作二值信号量、计数信号量或简单的 32 位变量。
- 当一个任务收到通知时,它可以选择阻塞(等待通知)或处理该通知。
## 1. API说明
任务通知主要涉及以下几个 API
| 函数名 | 功能 |
| ------------------------------- | --------------------------------------------- |
| xTaskNotify | 向指定任务发送通知,通知值可以被覆盖 |
| xTaskNotifyGive | 简化版通知函数,用于发送信号量通知 |
| xTaskNotifyWait | 等待通知值更新,并选择是否清除通知值 |
| ulTaskNotifyTake | 等待任务通知并自动减少通知值(通常用于计数信号量) |
| xTaskNotifyStateClear | 清除任务的通知状态 |
### xTaskNotify
**功能** 向指定任务发送通知,并修改该任务的通知值。
**原型**
```c
BaseType_t xTaskNotify(
TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction
);
```
**参数**
- xTaskToNotify接收通知的任务句柄。
- ulValue要发送的通知值。
- eAction通知值的操作类型可以是以下值
- eSetBits设置通知值的指定位。
- eIncrement通知值递增。
- eSetValueWithOverwrite覆盖通知值。
- eSetValueWithoutOverwrite如果通知值未处理则不覆盖当前值。
**返回值**
- pdPASS操作成功。
- pdFAIL操作失败通常在 eSetValueWithoutOverwrite 时发生)。
**示例**
```c
xTaskNotify(xTaskHandle, 0x01, eSetBits); // 设置任务通知值的第 0 位
```
### xTaskNotifyGive
**功能** 向任务发送一个 "信号量风格" 的通知,等效于 xTaskNotify() 的 eIncrement 模式。
**原型**
```c
void xTaskNotifyGive(TaskHandle_t xTaskToNotify);
```
**参数**
- xTaskToNotify接收通知的任务句柄。
示例:
```c
xTaskNotifyGive(xTaskHandle); // 发送通知,通知值递增 1
```
### ulTaskNotifyTake
**功能** 任务等待通知,并在接收到通知时自动减少通知值(通常用于实现计数信号量)。
**原型**
```c
uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait);
```
**参数**
- xClearCountOnExit
pdTRUE退出等待时将通知值清零。
pdFALSE退出等待时保留剩余通知值。
- xTicksToWait等待通知的时间Tick 数portMAX_DELAY 表示无限等待)。
**返回值**:返回通知值(如果是计数信号量,表示剩余信号量计数)。
**示例**
```c
if (ulTaskNotifyTake(pdTRUE, portMAX_DELAY) > 0) {
// 收到通知,处理任务
}
```
### xTaskNotifyWait
**功能** 任务等待通知,可以选择获取通知值的内容,并决定是否清除通知值。
**原型**
```c
BaseType_t xTaskNotifyWait(
uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait
);
```
**参数**
- ulBitsToClearOnEntry进入等待时清除的通知值位。
- ulBitsToClearOnExit退出等待时清除的通知值位。
- pulNotificationValue保存通知值的指针。
- xTicksToWait等待时间Tick 数)。
**返回值**
- pdTRUE收到通知。
- pdFALSE超时未收到通知。
**示例**
```c
uint32_t ulNotificationValue;
if (xTaskNotifyWait(0x00, 0xFFFFFFFF, &ulNotificationValue, portMAX_DELAY) == pdTRUE) {
// 收到通知ulNotificationValue 保存通知值
}
```
### xTaskNotifyStateClear
**功能** 清除任务的通知状态(即标记任务为“未通知”)。
**原型**
```c
void vTaskNotifyStateClear(TaskHandle_t xTask);
```
**参数**xTask需要清除通知状态的任务句柄。
**示例**
```c
vTaskNotifyStateClear(xTaskHandle); // 清除通知状态
```
## 2. 示例代码:
### 1.直接任务通知
由task2控制task1的运行
```c
// 事件组
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "main";
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task1启动!");
while (1)
{
ESP_LOGI(TAG, "task1: 等待task通知");
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
ESP_LOGI(TAG, "task1: 收到task通知");
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task2启动!");
while (1)
{
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 发送task通知");
xTaskNotifyGive(xTask1);
}
}
void app_main(void)
{
xTaskCreate(task1, "task1", 1024 * 2, NULL, 1, &xTask1);
xTaskCreate(task2, "task2", 1024 * 2, NULL, 1, &xTask2);
}
```
### 2.任务通知值
任务通知值按位判断-代替队列邮箱或者事件组
```c
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "main";
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;
void task1(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task1启动!");
uint32_t ulNotifiedValue;
while (1)
{
ESP_LOGI(TAG, "task1: 等待task通知");
xTaskNotifyWait(0x00, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY);
// 通过不同的bit位来判断通知的来源
if ((ulNotifiedValue & 0x01) != 0)
{
ESP_LOGI(TAG, "task1: 收到task通知-bit0");
}
if ((ulNotifiedValue & 0x02) != 0)
{
ESP_LOGI(TAG, "task1: 收到task通知-bit1");
}
if ((ulNotifiedValue & 0x04) != 0)
{
ESP_LOGI(TAG, "task1: 收到task通知-bit2");
}
}
}
void task2(void *pvParameters)
{
ESP_LOGI(TAG, "-------------------------------");
ESP_LOGI(TAG, "task2启动!");
while (1)
{
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 发送task通知-bit0");
xTaskNotify(xTask1, 0x01, eSetValueWithOverwrite);
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 发送task通知-bit1");
xTaskNotify(xTask1, 0x02, eSetValueWithOverwrite);
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "task2: 发送task通知-bit2");
xTaskNotify(xTask1, 0x04, eSetValueWithOverwrite);
}
}
void app_main(void)
{
xTaskCreate(task1, "task1", 1024 * 4, NULL, 1, &xTask1);
xTaskCreate(task2, "task2", 1024 * 4, NULL, 1, &xTask2);
}
```