有关 ESP32 的所有资源
适用于 Espressif SoC
撰写本文时esp-idf-v5.3.1
启动模式设置电路
BOOT( ) 启动模式 GPIO0 GPIO46 功能 SPI Boot 1 任意 芯片正常读取程序并启动 Download Boot 0 0 下载代码至 Flash
通用输入输出( GPIO)
GPIO 概述
GPIO
在 ESP32 中

- IE
输入使能: 当 IE = 1 时, 输入有效, 。 - OE
输出使能: 当 OE = 1时, 输出有效, 。 - WPU
内部若上拉: weak pull-up( ) 。 - WPD
内部弱下拉: weak pull-down( ) 。
使用 GPIO 相关函数前driver/gpio.h 库#include "driver/gpio.h"
初始化 GPIO
在初始化 GPIO 前
要先创建一个结构体变量, 用于储存 GPIO 配置, : 1
2
3
4
5
6
7
8
9
gpio_config_t gpio_10_config = {
.pin_bit_mask = 1ull << GPIO_NUM_10, // 表示引脚掩码 用掩码移位方式控制 GPIO,
.mode = GPIO_MODE_INPUT_OUTPUT, // 设置为输入输出模式
.pull_up_en = GPIO_PULLUP_DISABLE, // 设置上拉电阻
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 设置下拉电阻
.intr_type = GPIO_INTR_DISABLE, // 设置 GPIO 中断
};1
2
3
4
5
6
7
8
9
typedef enum {
GPIO_MODE_DISABLE = GPIO_MODE_DEF_DISABLE, /*!< GPIO mode : disable input and output */
GPIO_MODE_INPUT = GPIO_MODE_DEF_INPUT, /*!< GPIO mode : input only */
GPIO_MODE_OUTPUT = GPIO_MODE_DEF_OUTPUT, /*!< GPIO mode : output only mode */
GPIO_MODE_OUTPUT_OD = ((GPIO_MODE_DEF_OUTPUT) | (GPIO_MODE_DEF_OD)), /*!< GPIO mode : output only with open-drain mode */
GPIO_MODE_INPUT_OUTPUT_OD = ((GPIO_MODE_DEF_INPUT) | (GPIO_MODE_DEF_OUTPUT) | (GPIO_MODE_DEF_OD)), /*!< GPIO mode : output and input with open-drain mode*/
GPIO_MODE_INPUT_OUTPUT = ((GPIO_MODE_DEF_INPUT) | (GPIO_MODE_DEF_OUTPUT)), /*!< GPIO mode : output and input mode */
} gpio_mode_t;接下来
使用, gpio_config函数初始化该 GPIO1
gpio_config(&gpio_10_config);
该函数的的原型为
: 1
esp_err_t gpio_config(const gpio_config_t *pGPIOConfig);
该函数的返回类型是
esp_err_t 若返回, ESP_OK时 代表初始化成功, 。 若要查看 ESP-IDF 所有错误类型
请参考, %IDF_PATH/components/esp_common/include/esp_err.h
控制 GPIO 输出电平
1
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level);
其中
参数, gpio_num代表的是 GPIO 号 如, GPIO_NUM_48 参数, level代表的是电平状态 1为高电平, 2为低电平, 。 例如
: gpio_set_level(GPIO_NUM_48, 1); 将 GPIO48 引脚设置为高电平, 。 读取 GPIO 电平
1
int gpio_get_level(gpio_num_t gpio_num);
其中
参数, gpio_num代表的是 GPIO号 如。 gpio_num(GPIO_NUM_48);该函数会返回 1 或 0 1高电平0低电平。 示例
按下按钮
切换, GPIO_NUM_47上 LED 灯的亮灭状态 同时, GPIO_NUM_48引脚上的 500 毫秒闪烁一次: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void app_main(void) {
gpio_config_t gpio_LED_config = {
.pin_bit_mask = (1ull << GPIO_NUM_47) | (1ull << GPIO_NUM_48),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config_t gpio_10_config = {
.pin_bit_mask = 1ull << GPIO_NUM_10,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&gpio_LED_config);
gpio_config(&gpio_10_config);
gpio_set_level(GPIO_NUM_48, 1);
while(1) {
if( gpio_get_level(GPIO_NUM_10) == 1 ) {
vTaskDelay( pdMS_TO_TICKS(20) );
if( gpio_get_level(GPIO_NUM_10) == 1 ) {
gpio_set_level(GPIO_NUM_48, !gpio_get_level(GPIO_NUM_48));
}
while( gpio_get_level(GPIO_NUM_10) == 1 );
}
vTaskDelay( pdMS_TO_TICKS(500) );
gpio_set_level(GPIO_NUM_48, !gpio_get_level(GPIO_NUM_48));
}
}这种方法有个致命的缺陷
要是按下按钮时: 整个程序就阻塞了, 。 我们可以用 FreeRTOS 的方法写
如下, : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void Task_Blink(void *param) {
gpio_set_level(GPIO_NUM_48, !gpio_get_level(GPIO_NUM_48));
vTaskDelay( pdMS_TO_TICKS(500) );
}
void Task_Switch(void *param) {
if( gpio_get_level(GPIO_NUM_10) == 1 ) {
vTaskDelay( pdMS_TO_TICKS(20) );
if( gpio_get_level(GPIO_NUM_10) == 1 ) {
gpio_set_level(GPIO_NUM_48, !gpio_get_level(GPIO_NUM_48));
}
while( gpio_get_level(GPIO_NUM_10) == 1 );
}
}
void app_main(void) {
gpio_config_t gpio_LED_config = {
.pin_bit_mask = (1ull << GPIO_NUM_47) | (1ull << GPIO_NUM_48),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config_t gpio_10_config = {
.pin_bit_mask = 1ull << GPIO_NUM_10,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&gpio_LED_config);
gpio_config(&gpio_10_config);
gpio_set_level(GPIO_NUM_47, 1);
gpio_set_level(GPIO_NUM_48, 1);
while(1) {
xTaskCreatePinnedToCore( Task_Blink, "Task_Blink", 1024, NULL, 1, NULL, 1 );
xTaskCreatePinnedToCore( Task_Switch, "Task_Switch", 1024, NULL, 1, NULL, 1 );
}
}有关 FreeRTOS 的详细教程
在后续章节我们可以学到, 。
1 | void Task_A(void *param) { |
##通用硬件定时器
1 | gptimer_handle_t gptimer = NULL; |
##中断
外部中断( EXTI)
定时器中断( )
##FreeRTOS
现有一个需求
我们有以下常规写法
周期任务调度
为节约篇幅( 忽略了初始化代码, 若想查看完整代码, 请参考附录 FreeRTOS 周期任务调度, 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
volatile uint8_t LED_1_Blink_Flag = 0;
volatile uint8_t LED_2_Blink_Flag = 0;
bool IRAM_ATTR timer_callback(void *args);
void setup() {
/* 初始化 GPIO 引脚 */
/* ...此部分略... */
/* 初始化定时器 */
/* ...此部分略... */
}
void app_main(void) {
while(1) {
if(LED_1_Blink_Flag) {
gpio_set_level(GPIO_NUM_1, !gpio_get_level(GPIO_NUM_1));
LED_1_Blink_Flag = 0;
}
if(LED_2_Blink_Flag) {
gpio_set_level(GPIO_NUM_2, !gpio_get_level(GPIO_NUM_2));
LED_2_Blink_Flag = 0;
}
}
}
bool IRAM_ATTR timer_callback(void *args) {
static uint16_t counter = 0;
counter ++;
if(counter % 500 == 0) LED_1_Blink_Flag = 1;
if(counter % 1000 == 0) LED_2_Blink_Flag = 1;
if(counter >= 60000) counter = 0;
return true;
}
附录
FreeRTOS 周期任务调度
1 |
|