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
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"

// 定义三个LED对应的GPIO引脚
#define LED_GPIO_1 GPIO_NUM_2 // 第一个LED连接到GPIO2

/​**​
* @brief 控制LED闪烁的任务函数(周期500ms)
*
* @param param 参数,传递LED对应的GPIO引脚号
*/
void led_run_task(void *param)
{
int gpio_level = 0; // 初始电平状态(0=低电平,1=高电平)
gpio_num_t gpio_num = (gpio_num_t)param; // 将参数转换为GPIO引脚号

while (1) // 无限循环
{
gpio_level = gpio_level ? 0 : 1; // 切换电平状态(取反)
gpio_set_level(gpio_num, gpio_level); // 设置GPIO电平
vTaskDelay(pdMS_TO_TICKS(500)); // 延时500ms(任务阻塞)
}
}

/​**​
* @brief 主函数,程序入口
*/
void app_main(void)
{
// 配置GPIO为输出模式
gpio_config_t led_cfg = {
.pin_bit_mask = (1 << LED_GPIO_1), // 位掩码设置GPIO
.pull_up_en = GPIO_PULLUP_DISABLE, // 禁用上拉电阻
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉电阻
.mode = GPIO_MODE_OUTPUT, // 设置为输出模式
.intr_type = GPIO_INTR_DISABLE, // 禁用中断
};
gpio_config(&led_cfg); // 应用GPIO配置

xTaskCreatePinnedToCore(led_run_task, "led1", 2048, (void *)LED_GPIO_1, 3, NULL, 1); // 任务1:500ms周期
}

PWM调光

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <stdio.h>
#include "freertos/FreeRTOS.h" // FreeRTOS 系统头文件
#include "freertos/task.h" // 任务管理函数
#include "driver/gpio.h" // GPIO 操作函数
#include "driver/ledc.h" // LED PWM(LEDC)控制函数

// 定义 LED 所连接的 GPIO 引脚编号
#define LED_GPIO GPIO_NUM_2

// 定义事件组中两个事件位,分别表示占空比非零(LED亮)和占空比为零(LED灭)
#define FULL_EV_BIT BIT0 // 事件位0:占空比非零
#define EMPTY_EV_BIT BIT1 // 事件位1:占空比为零

// 定义事件组句柄,用于任务间同步
static EventGroupHandle_t ledc_event_handle;

/**
* @brief LEDC 渐变完成中断回调函数(从中断中调用)
* @param param 包含占空比等信息的回调参数结构体
* @param user_arg 用户参数(此处未使用)
* @return 若唤醒高优先级任务,返回 true
*/
bool IRAM_ATTR ledc_finish_cb(const ledc_cb_param_t *param, void *user_arg)
{
BaseType_t taskWorken; // 记录是否需要切换上下文

if(param->duty) // 如果占空比非零,说明 LED 点亮
{
// 设置 FULL_EV_BIT 事件位,通知任务 LED 亮了
xEventGroupSetBitsFromISR(ledc_event_handle, FULL_EV_BIT, &taskWorken);
}
else // 占空比为 0,说明 LED 熄灭
{
// 设置 EMPTY_EV_BIT 事件位,通知任务 LED 灭了
xEventGroupSetBitsFromISR(ledc_event_handle, EMPTY_EV_BIT, &taskWorken);
}

return taskWorken; // 若有高优先级任务被唤醒,则返回 true
}

/**
* @brief 控制 LED 呼吸效果的任务
* @param param 参数(此处未使用)
*/
void led_run_task(void *param)
{
EventBits_t ev; // 保存等待到的事件位

while (1)
{
// 等待 FULL_EV_BIT 或 EMPTY_EV_BIT 事件,超时时间 5000ms
// pdTRUE 表示等待到事件后自动清除,pdFALSE 表示任一事件位即可返回
ev = xEventGroupWaitBits(ledc_event_handle, FULL_EV_BIT | EMPTY_EV_BIT,
pdTRUE, pdFALSE, pdMS_TO_TICKS(5000));

if(ev & FULL_EV_BIT)
{
// 如果 LED 处于亮的状态(占空比非0),开始执行灭灯过程
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0, 2000); // 渐变至占空比0(灭),耗时2000ms
ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); // 非阻塞启动渐变
}
if(ev & EMPTY_EV_BIT)
{
// 如果 LED 处于灭的状态,开始执行亮灯过程
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191, 2000); // 渐变至最大亮度
ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); // 非阻塞启动渐变
}

// 注册渐变完成的回调函数
// 注:理论上只需注册一次,可移到初始化阶段
ledc_cbs_t cbs = {
.fade_cb = ledc_finish_cb, // 设置渐变完成回调
};
ledc_cb_register(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, &cbs, NULL);
}
}

/**
* @brief 主程序入口,进行各项初始化并创建任务
*/
void app_main(void)
{
// GPIO 初始化,用于控制 LED
gpio_config_t led_cfg = {
.pin_bit_mask = 1 << LED_GPIO, // 要配置的引脚掩码
.pull_up_en = GPIO_PULLUP_DISABLE, // 禁用上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉
.mode = GPIO_MODE_OUTPUT, // 设置为输出模式
.intr_type = GPIO_INTR_DISABLE, // 禁用中断
};
gpio_config(&led_cfg); // 应用 GPIO 配置

// 初始化 LEDC 定时器
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE, // 设置为低速模式(对应较慢频率)
.timer_num = LEDC_TIMER_0, // 使用定时器0
.clk_cfg = LEDC_AUTO_CLK, // 自动选择时钟源
.freq_hz = 5000, // PWM 频率为 5kHz
.duty_resolution = LEDC_TIMER_13_BIT, // 占空比分辨率为13位,最大值8191
};
ledc_timer_config(&ledc_timer); // 应用定时器配置

// 初始化 LEDC 通道
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_LOW_SPEED_MODE, // 同样为低速模式
.channel = LEDC_CHANNEL_0, // 使用通道0
.timer_sel = LEDC_TIMER_0, // 选择上面配置的定时器
.gpio_num = LED_GPIO, // 指定输出的GPIO引脚
.duty = 0, // 初始占空比为0(LED熄灭)
.intr_type = LEDC_INTR_DISABLE, // 不使用通道中断
};
ledc_channel_config(&ledc_channel); // 应用通道配置

// 安装 LED 渐变功能,使能软件渐变
ledc_fade_func_install(0);

// 执行初始亮灯渐变:从0 → 8191,耗时2000ms
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191, 2000);
ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); // 非阻塞启动

// 创建一个事件组,用于回调函数与任务间同步通信
ledc_event_handle = xEventGroupCreate();

// 注册 LED 渐变完成后的回调函数
ledc_cbs_t cbs = {
.fade_cb = ledc_finish_cb,
};
ledc_cb_register(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, &cbs, NULL);

// 创建一个任务控制 LED 交替亮灭,绑定到 Core 1 执行
xTaskCreatePinnedToCore(led_run_task, // 任务函数名
"led1", // 任务名称
2048, // 任务堆栈大小(字节)
(void *)LED_GPIO, // 传入参数(未使用)
3, // 优先级
NULL, // 不保存任务句柄
1); // 绑定到 Core 1
}