ESP32_IDF学习1【基本内容】

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

ESP32_IDF学习1【基本内容】
学校⽼师留了个作业,让⽤剩下⼀半的寒假学学ESP32,做蓝⽛透传+STA&AP模式下⼯作的http服务器,但是不准⽤Arduino
当场就傻了:ESP32我刚刚好就会⼀⼿Arduino;乐鑫那套ESPIDF太难啃,之前点了个灯就去快乐stm32了;micropython......刷完固件发现蓝⽛⽀持跟【数据删除】⼀样,还不如⽤c写——⼀咬⽛⼀跺脚,回头肝ESPIDF吧
总体思路:资源少,跟着官⽅⾛准没错,硬啃就完事了
这个系列笔记可以供只接触过单⽚机开发(STM32、51基础)和硬件相关知识但没有接触过⽹络相关知识的同学翻阅学习
项⽬⽂件夹构建
ESP-IDF项⽬由各种“组件”构成,你需要什么功能就要往⾥扔进去什么组件
如果你的代码⾥⽤了⼀堆WiFi的库函数,但是没把WiFi组件加⼊进去,你是没办法⽤WiFi功能的
项⽬保存在项⽬⽂件夹下,它的根⽬录如下所⽰:
├── CMakeLists.txt Cmake使⽤的⽂件
├── other_documents 其他⽂件
├── main 存储主程序
│├── CMakeLists.txt
│├── component.mk 组件的make file
│└── main.c
└── Makefile 由传统的GNU make程序使⽤的Makefile
需要注意的是:ESP-IDF并不是项⽬⽂件夹的⼀部分,它更像是⼀个⾃助编译器,项⽬⽂件夹通过idf.py esptools等⼯具和{IDF_PATH}与ESP-IDF⽬录建⽴联系;同样,esp的开发⼯具链也独⽴于项⽬存在,通过{PATH}对项⽬进⾏作⽤
项⽬建⽴前,esp-idf会通过idf.py menuconfig配置出Makefile,这些配置保存在sdkconfig中。

sdkconfig会被保存在项⽬⽂件夹的根⽬录
CMakeLists.txt通过idf_component_register将项⽬⽂件夹下⾯的组件进⾏注册,如下所⽰
idf_component_register(SRCS "foo.c" "bar.c"
INCLUDE_DIRS "include"
REQUIRES mbedtls)
SRCS给出了源⽂件清单,能⽀持的源⽂件后缀名为.c .cpp .cc .S
INCLUDE_DIRS给出了组件中⽂件的搜索路径
REQUIRES不是必须的,它声明了其他需要加⼊的组件
通过这个txt⽂档,esp-idf就能知道你往⾥扔了什么组件,然后在编译的时候就会把这些组件编译链接进去(可以理解成操作系统的静态链接)
当编译完成后,⽂件根⽬录下会多出build⽂件夹和sdkconfig⽂件,build⽂件夹⽤来存放编译过程中的⽂件和⽣成的⽂件,sdkconfig⽂件是在menuconfig的过程中产⽣的,如果曾经多次重新设置过menuconfig,还会发现多出了以.old结尾的config⽂件
另外组件也可以⾃制,详细内容参考官⽅教程;⽽idf.py 的底层是⽤Cmake、make⼯具实现的,所以也可以直接⽤这些⼯具进⾏编译(不过应该没⼈这么⼲)
CMake与component组件
【摘⾃官⽅⽂档】⼀个ESP-IDF项⽬可以看作是多个不同组件(component)的集合,组件是模块化且独⽴的代码,会被编译成静态库并链接到应⽤程序。

ESP-IDF⾃带⼀些组件,也可以去找开源项⽬已有的组件来⽤
ESP-IDF的组件其实是对CMake的封装,如果使⽤纯CMake风格的构建⽅式也可⾏(说到底还是交叉编译的那套流程,只是乐鑫针对ESP32进⾏了优化),如下所⽰
cmake_minimum_required(VERSION 3.5)
project(my_custom_app C)
# 源⽂件 main.c 包含有 app_main() 函数的定义
add_executable(${CMAKE_PROJECT_NAME}.elf main.c)
# 提供 idf_import_components 及 idf_link_components 函数
include($ENV{IDF_PATH}/tools/cmake/idf_functions.cmake)
# 为 idf_import_components 做⼀些配置
# 使能创建构件(不是每个项⽬都必须)
set(IDF_BUILD_ARTIFACTS ON)
set(IDF_PROJECT_EXECUTABLE ${CMAKE_PROJECT_NAME}.elf)
set(IDF_BUILD_ARTIFACTS_DIR ${CMAKE_BINARY_DIR})
# idf_import_components 封装了 add_subdirectory(),为组件创建库⽬标,然后使⽤给定的变量接收“返回”的库⽬标。

# 在本例中,返回的库⽬标被保存在“component”变量中。

idf_import_components(components $ENV{IDF_PATH} esp-idf)
# idf_link_components 封装了 target_link_libraries(),将被 idf_import_components 处理过的组件链接到⽬标
idf_link_components(${CMAKE_PROJECT_NAME}.elf "${components}")
⽰例项⽬的⽬录树结构可能如下所⽰:
- myProject/ #主⽬录
- CMakeLists.txt #全局CMAke⽂档,⽤于配置项⽬CMake
- sdkconfig #项⽬配置⽂件,可⽤menuconfig⽣成
- components/ - component1/ - CMakeLists.txt #组件的CMake⽂档,⽤于配置组件CMake
- Kconfig #⽤于定义menuconfig时展⽰的组件配置选项
- src1.c
- component2/ - CMakeLists.txt
- Kconfig
- src1.c
- include/ - component2.h
- main/ #可以将main⽬录看作特殊的伪组件 - src1.c
- src2.c
- build/ #⽤于存放输出⽂件
main⽬录是⼀个特殊的“伪组件”,包含项⽬本⾝的源代码。

main是默认名称,CMake 变量COMPONENT_DIRS默认包含此组件,但您可以修改此变量。

或者,您也可以在顶层 CMakeLists.txt 中设置EXTRA_COMPONENT_DIRS变量以查找其他指定位置处的组件。

如果项⽬中源⽂件较多,建议将其归于组件中,⽽不是全部放在main中。

全局CMake编写
全局CMake⽂档应该⾄少包含如下三个部分:
cmake_minimum_required(VERSION 3.5) #必须放在第⼀⾏,设置构建该项⽬所需CMake的最⼩版本号
include($ENV{IDF_PATH}/tools/cmake/project.cmake) #⽤于导⼊CMake的其余功能来完成配置项⽬、检索组件等任务
project(myProject) #指定项⽬名称并创建项⽬,改名成蕙作为最终输出的bin⽂件或elf⽂件的名字
每个 CMakeLists ⽂件只能定义⼀个项⽬
还可以包含以下可选部分
COMPONENT_DIRS #组件搜索⽬录,默认为${IDF_PATH}/components、${PROJECT_PATH}/components和EXTRA_COMPONENT_DIRS
COMPONENTS #要构建进项⽬中的组件名称列表,默认为COMPONENT_DIRS⽬录下检索到的所有组件
EXTRA_COMPONENT_DIRS #⽤于搜索组件的其它可选⽬录列表,可以是绝对路径也可以是相对路径
COMPONENT_REQUIRES_COMMON #每个组件都需要的通⽤组件列表,这些通⽤组件会⾃动添加到每个组件的COMPONENT_PRIV_REQUIRES列表和项⽬的COMPONENTS列表中使⽤set命令来设置以上变量,如下所⽰
set(COMPONENTS "COMPONENTx")
注意:set命令需要放在include之前,cmake_minimum_required之后
特别地,可以重命名main组件,分为两种情况
1. main组件处于正常位置${PROJECT_PATH}/main,则会被⾃动添加到构建系统中,其他组件⾃动成为main的依赖项,⽅便处理依赖关系
2. main组件被重命名为xxx,需要在全局CMake设定中设置EXTRA_COMPONENT_DIRS=${PROJECT_PATH}/xxx,并在组件CMake⽬录中设置
COMPONENT_REQUIRES或COMPONENT_PRIV_REQUIRES以指定依赖项
组件CMake编写
每个项⽬都包含⼀个或多个组件,这些组件可以是 ESP-IDF 的⼀部分,可以是项⽬⾃⾝组件⽬录的⼀部分,也可以从⾃定义组件⽬录添加
组件是COMPONENT_DIRS列表中包含CMakeLists.txt⽂件的任何⽬录
ESP-IDF会搜索COMPONENT_DIRS中的⽬录列表来查找项⽬的组件此列表中的⽬录可以是组件⾃⾝(即包含CMakeLists.txt⽂件的⽬录),也可以是⼦⽬录为组件的顶级⽬录;搜索顺序:【ESP-IDF内部组件】-【项⽬组件】-【EXTRA_COMPONENT_DIRS】中的组件,如果这些⽬录中的两个或者多个包含具有相同名字的组件,则使⽤搜索到的最后⼀个位置的组件,允许将组件复制到项⽬⽬录中再修改以覆盖ESP-IDF组件
最⼩的组件CMakeLists如下
set(COMPONENT_SRCS "foo.c" "k.c") #⽤空格分隔的源⽂件列表
set(COMPONENT_ADD_INCLUDEDIRS "include") #⽤空格分隔的⽬录列表,⾥⾯的路径会被添加到所有需要该组件的组件(包括 main 组件)全局 include 搜索路径中
register_component() #构建⽣成与组件同名的库,并最终被链接到应⽤程序中
有以下预设变量,不建议修改
COMPONENT_PATH #组件⽬录,是包含CMakeLists.txt⽂件的绝对路径,注意路径中不能包含空格
COMPONENT_NAME #组件名,等同于组件⽬录名
COMPONENT_TARGET #库⽬标名,由CMake在内部⾃动创建
有以下项⽬级别的变量,不建议修改,但可以在组件CMake⽂档中使⽤
PROJECT_NAME #项⽬名,在全局CMake⽂档中设置
PROJECT_PATH #项⽬⽬录(包含项⽬ CMakeLists ⽂件)的绝对路径,与CMAKE_SOURCE_DIR相同
COMPONENTS #此次构建中包含的所有组件的名称
CONFIG_* #项⽬配置中的每个值在cmake中都对应⼀个以CONFIG_开头的变量
IDF_VER #ESP-IDF的git版本号,由git describe命令⽣成
IDF_TARGET #项⽬的硬件⽬标名称,⼀般是ESP32
PROJECT_VER #项⽬版本号
COMPONENT_ADD_INCLUDEDIRS #相对于组件⽬录的相对路径,会被添加到所有需要该组件的其他组件的全局include搜索路径中
COMPONENT_REQUIRES #⽤空格分隔的组件列表,列出了当前组件依赖的其他组件
【摘⾃官⽹】如果⼀个组件仅需要额外组件的头⽂件来编译其源⽂件(⽽不是全局引⼊它们的头⽂件),则这些被依赖的组件需要在COMPONENT_PRIV_REQUIRES中指出
有以下可选的组件特定变量,⽤于控制某组件的⾏为
COMPONENT_SRCS #要编译进当前组件的源⽂件的路径,推荐使⽤此⽅法向构建系统中添加源⽂件
COMPONENT_PRIV_INCLUDEDIRS #相对于组件⽬录的相对路径,仅会被添加到该组件的include搜索路径中
COMPONENT_PRIV_REQUIRES #以空格分隔的组件列表,⽤于编译或链接当前组件的源⽂件
COMPONENT_SRCDIRS #相对于组件⽬录的源⽂件⽬录路径,⽤于搜索源⽂件,匹配成功的源⽂件会替代COMPONENT_SRCS中指定的源⽂件
COMPONENT_SRCEXCLUDE #需要从组件中剔除的源⽂件路径
COMPONENT_ADD_LDFRAGMENTS #组件使⽤的链接⽚段⽂件的路径,⽤于⾃动⽣成链接器脚本⽂件
组件配置⽂件Kconfig
每个组件都可以包含⼀个Kconfig⽂件,和CMakeLists.txt放在同⼀⽬录下
Kconfig⽂件中包含要添加到该组件配置菜单中的⼀些配置设置信息,运⾏menuconfig时,可以在Component Settings菜单栏下找到这些设置有⼿就⾏的⼊门
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#include "esp_log.h"
//固定需要include的头⽂件
//⽤于freertos⽀持和输出调试信息
以下内容头⽂件包含部分会省略这些
⼀般程序的⼊⼝是app_main()函数
void app_main(void)
点灯
#include "driver/gpio.h"
#define BLINK_GPIO CONFIG_BLINK_GPIO
/*Kconfig.projbuild⽂件内容如下
menu "Example Configuration"
config BLINK_GPIO
int "Blink GPIO number"
range 0 34
default 5
help
GPIO number (IOxx) to blink on and off.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
GPIOs 35-39 are input-only so cannot be used as outputs.
endmenu
这个⽂件的内容是在c预编译器之前进⾏的替换,会把CONFIG_BLINK_GPIO变成default的值(5)
*/
void app_main(void)
{
gpio_pad_select_gpio(BLINK_GPIO);//选择的引脚
gpio_set_direction(BLINK_GPIO,GPIO_MODE_OUTPUT);//设置输⼊输出⽅向
while(1)
{
printf("Turning off the LED\n");//串⼝打印信息
gpio_set_level(BLINK_GPIO, 0);//GPIO寄存器清零
vTaskDelay(1000 / portTICK_PERIOD_MS);//vTaskDelay()⽤于任务中的延时,下⾯会提到这其实是将任务转⼊阻塞态
printf("Turning on the LED\n");
gpio_set_level(BLINK_GPIO, 1);//GPIO寄存器置位
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
UART
官⽅给出的配置步骤为:
1. 设置uart_config_t配置结构体
2. 通过ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));应⽤设置
3. 设置引脚
4. 安装驱动,设置buffer和事件处理函数等
5. 配置FSM并运⾏UART
#include "esp_system.h"
#include "driver/uart.h"
#include "driver/gpio.h"
//include uart库和gpio库来实现相应功能
void UART_init(void)//uart初始化函数
{
const uart_config_t uart_config =
{
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};//配置uart设置
ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config));//应⽤设置
//设置uart引脚
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
//使⽤buffer的情况,使⽤freertos提供的设备驱动
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2,0,0, NULL, 0));
}
int UART_send_data(const char* TAG, const char* data)//发送数据函数
{
const int length = uart_write_bytes(UART_NUM_1, data, strlen(data));
ESP_LOGI(TAG, "Wrote %d bytes", length);
return length;
}
int UART_read_data(const char* TAG, const char* buffer)//收取数据函数
{
int length = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(UART_NUM_1, (size_t*)&length));
length = uart_read_bytes(UART_NUM_1,buffer,length,100);
ESP_LOGI(TAG, "Read %d bytes", length);
return length;
}
void app_main(void)
{
UART_init();//初始化
//分别配置发送和接收串⼝信息的任务
xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
xTaskCreate(tx_task, "uart_tx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
}
console控制台
ESP提供了⼀个console⽤于串⼝调试,可以实现类似shell的操作
在固件中加⼊console相关组件后烧录,在串⼝中打出help就可以查看相关帮助
void register_system(void)//系统相关指令
{
register_free();
register_heap();
register_version();
register_restart();
register_deep_sleep();
register_light_sleep();
#if WITH_TASKS_INFO
register_tasks();
#endif
}
组件中两个⽬录:cmd_nvs⽤于指令的识别;cmd_system⽤于系统指令的实现(这部分功能需要与RTOS配合才⾏)
NVS FLASH
NVS即Non-volatile storage⾮易失性存储
它相当于把ESP32的关键数据以键值格式存储在FLASH⾥,NVS通过spi_flash_{read|write|erase}三个API进⾏操作,NVS使⽤主flash的⼀部分。

管理⽅式类似数据库的表,在NVS⾥⾯可以存储很多个不同的表,每个表下⾯有不同的键值,每个键值可以存储8位、16位、32位等等不同的数据类型,但不能是浮点数
1. 使⽤接⼝函数nvs_flash_init();进⾏初始化,如果失败可以使⽤nvs_flash_erase();先擦除再初始化
2. 应⽤程序可以使⽤nvs_open();选⽤NVS表中的分区或通过nvs_open_from_part()指定其名称后使⽤其他分区
注意:NVS分区被截断时,其内容应该被擦除
读写操作
nvs_get_i8(my_handle,//表的句柄
&nvs_i8);//对应变量的指针
//使⽤这个API来读取8位数据,同理还有i16、u32等版本的API可⽤
nvs_set_i8(my_handle,//表的句柄
"nvs_i8",//键值
nvs_i8);//对应的变量
//使⽤这个API来写⼊8位数据,同理还有i16、u32等版本的API可⽤
表操作
nvs_open("List",//表名
NVS_READWRITE,//读写模式,可选读写模式或只读模式
&my_handle);//表的句柄
//打开表
nvs_commit(my_handle);//提交表
nvs_close(my_handle);//关闭表
NVS初始化⽰例程序
官⽅给出的⽰例程序中⼀般以以下形式初始化NVS
//Initialize NVS
esp_err_t ret = nvs_flash_init();//初始化
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)//如果初始化未成功
{
ESP_ERROR_CHECK(nvs_flash_erase());//擦除NVS并查错
ret = nvs_flash_init();//再次初始化
}
ESP_ERROR_CHECK(ret);//查错
ESPIDF提供的常⽤库函数
ESP_LOG打印系统⽇志到串⼝
#include "esp_err.h"
//⽤于打印错误信息
ESP_LOGE - 错误⽇志 (最⾼优先级)
ESP_LOGW - 警告⽇志
ESP_LOGI - 信息级别的⽇志
ESP_LOGD - ⽤于调试的⽇志
ESP_LOGV - 仅仅⽤于提⽰的⽇志{最低优先级)
这些⽇志可以在menuconfig设置中打开或关闭,也可以在代码中⼿动设置关闭
RTOS操作
1. vTaskDelay将任务置为阻塞状态,期间CPU继续运⾏其它任务
持续时间由参数xTicksToDelay指定,单位是系统节拍时钟周期
void vTaskDelay(portTickTypexTicksToDelay)
常量portTickTypexTicksToDelay⽤来辅助计算真实时间,此值是系统节拍时钟中断的周期,单位是ms
在⽂件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelay 必须设置成1,此函数才能有效
2. xTaskCreate创建新的任务并添加到任务队列
注意:所有任务应当为死循环且永远不会返回,即嵌套在while(1)内
xTaskCreate(pdTASK_CODE pvTaskCode,//指向任务的⼊⼝函数
const portCHAR * const pcName,//任务名
unsigned portSHORT usStackDepth,//任务堆栈⼤⼩
void *pvParameters,//任务参数指针
unsigned portBASE_TYPE uxPriority,//任务优先级
xTaskHandle *pvCreatedTask)//任务句柄,⽤于引⽤创建的任务
注意,任务优先级0为最低,数字越⼤优先级越⾼
3. FreeRTOS的神奇之处
⼀句话概论:RTOS就是⼻亍,FreeRTOS可以实现任务之间的时间⽚轮转调度,两个任务可以你执⾏⼀会我执⾏⼀会,⾼优先级任务还能抢占低优先级任务,让它马上⽖巴,⾼优先级任务先运⾏
FreeRTOS的底层实现还没看明⽩,过⼀阵⼦再学,反正效果和RTThread差不多,先把作业肝完再说 =)
这种神奇的操作靠的就是上⾯两个API
需要注意的是:所有任务应当为死循环且永远不会返回(两次强调)
不过如果实在不想写死循环,可以在任务末尾加上
vTaskDelete();//⽤于删除执⾏结束的任务
不过只执⾏⼀次的任务⼤多是在初始化阶段完成的,⽤的时候尽量⼩⼼些
4. 事件event
事件是⼀种实现任务间通信的机制,主要⽤于实现多任务间的同步,但事件通信只能是事件类型的通信,⽆数据传输。

事件可以实现⼀对多和多对多的传输:⼀个任务可以等待多个事件的发⽣:可以是任意⼀个事件发⽣时唤醒任务进⾏事件处理;也可以是⼏个事件都发⽣后才唤醒任务进⾏事件处理
#include "esp_event.h"
//include 这个⽂件才能使⽤event
事件使⽤事件循环来管理,事件循环分别为默认事件循环和⾃定义事件循环
默认事件循环不需要传⼊事件循环句柄;但⾃定义循环需要
esp_event_loop_create(const esp_event_loop_args_t *event_loop_args,//事件循环参数
esp_event_loop_handle_t *event_loop)//事件循环句柄
//⽤于创建⼀个事件循环
esp_event_loop_delete(esp_event_loop_handle_t event_loop)//删除事件循环
事件需要注册到事件循环
/* 注册事件到事件循环 */
esp_event_handler_instance_register(esp_event_base_t event_base,//事件基本ID
int32_t event_id,//事件ID
esp_event_handler_t event_handler,//事件回调函数指针(句柄)
void *event_handler_arg,//事件回调函数参数
esp_event_handler_instance_t *instance)
//如果事件回调函数在事件删除之前还没有被注册,需要在这⾥注册来进⾏调⽤
esp_event_handler_instance_register_with(esp_event_loop_handle_t event_loop,//事件循环句柄
esp_event_base_t event_base,//事件基本ID
int32_t event_id,//事件ID
esp_event_handler_t event_handler,//事件回调函数指针(句柄)
void *event_handler_arg,//事件回调函数参数
esp_event_handler_instance_t *instance)
//如果事件回调函数在事件删除之前还没有被注册,需要在这⾥注册来进⾏调⽤
esp_event_handler_register(esp_event_base_t event_base,//事件基本ID
int32_t event_id,//事件ID
esp_event_handler_t event_handler,//事件句柄
void *event_handler_arg)//事件参数
esp_event_handler_register_with(esp_event_loop_handle_t event_loop,//事件循环句柄
esp_event_base_t event_base,//事件基本ID
int32_t event_id,//事件ID
esp_event_handler_t event_handler,//事件回调函数指针(句柄)
void *event_handler_arg)//事件回调函数的参数
/* 取消注册 */
esp_event_handler_unregister(esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler)
esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler)
默认事件循环default event loop是系统的基础事件循环,⽤于传递系统事件(如WiFi等),但是也可以注册⽤户事件,⼀般的蓝⽛+WiFi⽤这⼀个循环就⾜够了
esp_event_loop_create_default(void)//创建默认事件循环
esp_event_loop_delete_default(void)//删除默认事件循环
esp_event_loop_run(esp_event_loop_handle_t event_loop,//事件循环句柄
TickType_t ticks_to_run)//运⾏时间
默认事件和⾃定义事件之间可以进⾏发送操作
esp_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait)
使⽤宏
ESP_EVENT_DECLARE_BASE()
ESP_EVENT_DEFINE_BASE()
来声明和定义事件,同时事件的ID应该⽤enum枚举变量来指出,如下所⽰
/* 头⽂件 */
// Declarations for event source 1: periodic timer
#define TIMER_EXPIRIES_COUNT// number of times the periodic timer expires before being stopped
#define TIMER_PERIOD 1000000 // period of the timer event source in microseconds
extern esp_timer_handle_t g_timer; // the periodic timer object
// Declare an event base
ESP_EVENT_DECLARE_BASE(TIMER_EVENTS); // declaration of the timer events family
enum {// declaration of the specific events under the timer event family
TIMER_EVENT_STARTED, // raised when the timer is first started
TIMER_EVENT_EXPIRY, // raised when a period of the timer has elapsed
TIMER_EVENT_STOPPED // raised when the timer has been stopped
};
// Declarations for event source 2: task
#define TASK_ITERATIONS_COUNT 5 // number of times the task iterates
#define TASK_ITERATIONS_UNREGISTER 3 // count at which the task event handler is unregistered #define TASK_PERIOD 500 // period of the task loop in milliseconds
ESP_EVENT_DECLARE_BASE(TASK_EVENTS); // declaration of the task events family
enum {
TASK_ITERATION_EVENT, // raised during an iteration of the loop within the task
};
/* 头⽂件 */
/* 源⽂件 */
ESP_EVENT_DEFINE_BASE(TIMER_EVENTS);
/* 源⽂件 */
/* 枚举定义的事件名应该放在头⽂件,宏函数应该放在源⽂件 */
可使⽤API esp_event_loop_create_default()来创建事件
esp_event_loop_create_default()
Processing math: 0%。

相关文档
最新文档