ESP32呼吸灯 PWM

这个实验的功能是使用 PWM 去控制板子上的彩灯实现渐变效果。 这个实验的代码为工程“3_8_pwm”目录。

3.8.1. 实验内容

(1) 学习如何控制 LED 灯及硬件原理
(2) 学习 ESP32 的 PWM(ledc)功能的配置
(3) 掌握 PWM(ledc)控制 LED 彩灯渐变程序

3.8.2. 硬件设计和原理

LED 彩灯硬件设计在 3.3 节已经讲解过了,我们知道通过 IO15、IO16 和 IO32 输出高低电平就可以控制 彩灯亮灭了。
在这个实验里,我们是通过 IO15、IO16 和 IO32 输出 PWM 信号去控制彩灯的亮度。要注意的是我们 的彩灯是共阳极的,输出 100%占空比的时候,彩灯是灭的,输出 0%占空比的时候,彩灯处于最亮状态。 ESP32 的占空比支持手动和自动两种模块,我们的这个实验里启动了两个任务,用于实现手动设置占
空比和自动设置占空比,通过板上的按键 key1 去控制切换。

3.8.3. 函数介绍

ESP32 的 ESP-IDF 编程指南可以从官网上查询:
https://docs.espressif.com/projects/esp-idf/zh_CN/latest/index.html

 PWM(LEDC)定时器配置
函数定义如下:
esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf); 参数说明:
ledc_timer_config_t* timer_conf:定时器的参数,这是一个结构体,具体的定义请往下看 返回值:ESP_OK(成功)和 ESP_ERR_INVALID_ARG(参数错误) 。

typedef struct {
ledc_mode_t speed_mode; union {

ledc_timer_bit_t duty_resolution;  //pwm 的分辨率,也就是最大的占空比
ledc_timer_bit_t bit_num   attribute  ((deprecated)); //
};
ledc_timer_t timer_num;	//定时器选择,支持 0 ̄3 

 	} ledc_timer_config_t;	

	

PWM(LEDC)配置函数
函数定义如下:
esp_err_t ledc_channel_config(const ledc_channel_config_t* ledc_conf); 参数说明:
ledc_channel_config_t* ledc_conf:pwm 配置参数,这是一个结构体,具体的定义请往下看 返回值:ESP_OK(成功)和 ESP_ERR_INVALID_ARG(参数错误) 。

typedef struct {
int gpio_num;	//具体的 IO 口定义 ledc_mode_t speed_mode;		//速度选择,高速或者低速 ledc_channel_t channel;		//通道选择,取值 0~7 ledc_intr_type_t intr_type;	//中断类型 ledc_timer_t timer_sel;			//定时器通道选择 uint32_t duty;		//占空比
int hpoint;	//LEDC channel hpoint value, the max value is 0xfffff
} ledc_channel_config_t;

 PWM(LEDC)渐变安装函数
这个函数在系统 PWM 占空比自己变化的时候会使用到,函数定义如下:
esp_err_t ledc_fade_func_install(int intr_alloc_flags); 参数说明:
int intr_alloc_flags:是否分配中断标志 返回值:ESP_OK(成功)和 ESP_ERR_INVALID_ARG(参数错误) 。

 PWM(LEDC)自动渐变
要实现 PWM 自动渐变,先要调用自动渐变设置函数 ledc_set_fade_with_time,接着调用自动渐变启 动函数 ledc_fade_start,分别说明。
函数 1:
esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t target_duty, int max_fade_time_ms);
功能:设置自动渐变参数 参数说明:
ledc_mode_t speed_mode://速度选择,高速或者低速 ledc_channel_t channel://PWM 通道
uint32_t target_duty://目标占空比
int max_fade_time_ms://变化到目标占空比使用的时间 返回值:ESP_OK(成功)和 ESP_ERR_INVALID_ARG(参数错误) 。

函数 2:
esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel,ledc_fade_mode_t fade_mode); 功能:启动自动渐变功能
参数说明:
ledc_mode_t speed_mode://速度选择,高速或者低速 ledc_channel_t channel://PWM 通道 ledc_fade_mode_t fade_mode://wait_done:是否等待
返回值:ESP_OK(成功)和 ESP_ERR_INVALID_ARG(参数错误) 。

 PWM(LEDC)手动修改占空比
手动修改占空比涉及到 2 个函数,使用函数 ledc_set_duty 设置占空比后,还要使用 ledc_update_duty函数让新占空比生效,分别说明。
函数 1:
esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty); 功能:设置新的占空比
参数说明:
ledc_mode_t speed_mode://速度选择,高速或者低速 ledc_channel_t channel://PWM 通道
uint32_t duty://要设置的占空比 返回值:ESP_OK(成功)和 ESP_ERR_INVALID_ARG(参数错误) 。

函数 2:
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); 功能:启用新的占空比
参数说明:
ledc_mode_t speed_mode://速度选择,高速或者低速 ledc_channel_t channel://PWM 通道
返回值:ESP_OK(成功)和 ESP_ERR_INVALID_ARG(参数错误) 。

3.8.4. 代码讲解

使用 vs code 展开本实验的工程目录,如下图:
在这里插入图片描述

我们的这个实验,只有一个源文件 app_main.c,按键、任务和 PWM 控制都在这个文件里。在讲解代 码的前,先把这个实验的框图发出来,如下图:
在这里插入图片描述

通过框图可以看出,一共有三个任务,功能分别如下:
 主任务,检测按键 key1 按下,然后修改 pwm_mode 值。,任务 1 和任务 2 根据 pwm_mode 的值去 决定是否执行渐变功能,
 任务 1,pwm_mode 等于 1 时,红绿蓝三种灯各渐变 2 秒,一个周期约是 9 秒时间。
 任务 2,pwm_mode 等于 0 时,根据 pwm_index 决定当前是设置红绿蓝三种的其中一种,每一个占 空比停留时间是 50ms。

对这个框图有了一定的认识之后,我们看程序的时候应该比较容易理解了。程序里关于按键初始化和 按键扫描部分不讲解了,如果不理解请往回看实验 3.5。下面开始讲解程序里的各个片段。
在源文件 app_main.c 的最前面,对灯的硬件 IO 口、占空比最大值、自动渐变的时间和 PWM 使用的
通道有定义,代码如下图:

//定义 LED 灯的 IO 
口                  32//对应红灯的 LED,绿灯为 15,
  #define             蓝灯为 16
   LED_RED_IO      15	//对应红灯的 LED,绿灯为 15,蓝灯为 16



#define LEDC_MAX_DUTY
#define        (8191)	//2 的 13 次方-1(13 位 PWM)
              (1000)	//渐变时间(ms)
   
#define 
#define  PWM_RED_CHANNEL	LEDC_CHANNEL_0	//定义红灯通道
#define  PWM_GREEN_CHANNEL	LEDC_CHANNEL_1	//定义绿灯通道
#define  PWM_BLUE_CHANNEL	LEDC_CHANNEL_2	//定义蓝灯通道
unsigned  char     //PWM 模块,如果为 1 表示通过库函数实现渐变

首先我们讲解 PWM 初始化,是对 PWM 使用的定时器配置,三路 PWM 参数配置,以及是否启动渐变功能,代码里都是注释,用到的每一个函数在前面都有讲解,代码如下:

void PWM_init(void)
{
//定时器配置结构体 ledc_timer_config_t
ledc_timer.duty_resolution  = LEDC_TIMER_13_BIT;
ledc_timer.freq_hz =  5000; ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; ledc_timer.timer_num = LEDC_TIMER_0; ledc_timer_config(&ledc_timer);
//PWM 通道 0 配置->IO32->红色灯
g_ledc_red.channel		=  PWM_RED_CHANNEL; g_ledc_red.duty			=  LEDC_MAX_DUTY; g_ledc_red.gpio_num		=  LED_RED_IO; g_ledc_red.speed_mode = LEDC_HIGH_SPEED_MODE; g_ledc_red.timer_sel	=  LEDC_TIMER_0; ledc_channel_config(&g_ledc_red);
//PWM 通道 0 配置->IO15->绿
g_ledc_green.channel
g_ledc_green.duty g_ledc_green.gpio_num g_ledc_green.speed_mode g_ledc_green.timer_sel      
//PWM 通道 0 配置->IO32->蓝
g_ledc_blue.channel
g_ledc_blue.duty g_ledc_blue.gpio_num g_ledc_blue.speed_mode g_ledc_blue.timer_sel
ledc_channel_config(&g_ledc_bl
//PWM 模式为 1 的时候,使能 ledc 渐变功能
if(pwm_mode==1)
{
ledc_fade_func_install(0);

接着我们看主任务,任务里选是初始化按键和 PWM,接着创建了任务 1 和任务 2,最后通过 while(1)
检测 key1 从而改变 pwm_mode,代码如下:

//用户函数入口,相当于 main 函数 void app_main()
{
initKey();//按键初始化 PWM_init();//PWM 初始化

//创建两个任务,用于执行不同的 PWM 模式
xTaskCreate(&task_pwm1,  "task_pwm1",  4096,  NULL,  9, NULL);
xTaskCreate(&task_pwm2,  "task_pwm2",  4096,  NULL,  9, NULL);

while(1)
{
//通过按键改变 PWM 模式
if(1==key_read_key1())
{
pwm_mode=(pwm_mode==1)?0:1; PWM_init();//重新初始化 ledc 初始化
}

vTaskDelay(10);//延时一下
}
}

任务 1 入口函数,如果 pwm_mode 等于 1 时,红绿蓝三种灯自动轮流渐变,代码里都是注释,用到的 每一个函数在前面都有讲解。代码如下,绿灯和蓝灯的代码和红灯的类似,固省略,完整的代码请看源文件:

//通过渐变功能演示 PWM 任务
void task_pwm1(void *pvParameter)
{
while(1)
{
if(1==pwm_mode)
{
printf("pwm mode1.\r\n");
//渐变功能演示 PWM

///
//红灯占空比 100%-->0%-->100%,时间 2*LEDC_FADE_TIME
//红灯:灭-->亮-->灭,的过程
///

//红灯占空比 100% 渐变至 0%,时间 LEDC_FADE_TIME ledc_set_fade_with_time(g_ledc_red.speed_mode,
g_ledc_red.channel, 0,  LEDC_FADE_TIME);
//渐变开始 ledc_fade_start(g_ledc_red.speed_mode,
g_ledc_red.channel, LEDC_FADE_NO_WAIT);
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);

//红灯占空比 0%渐变至 100%,时间 LEDC_FADE_TIME ledc_set_fade_with_time(g_ledc_red.speed_mode,
g_ledc_red.channel, LEDC_MAX_DUTY, LEDC_FADE_TIME);
//渐变开始 ledc_fade_start(g_ledc_red.speed_mode,
g_ledc_red.channel, LEDC_FADE_NO_WAIT);
vaskDelay(LEDCT_FADE_TIME / portTICK_PERIOD_MS); vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);

///

///
//蓝灯占空比 100%-->0%-->100%,时间 2*LEDC_FADE_TIME
//蓝灯:灭-->亮-->灭,的过程
//
代码相似///略/
}
else{
vTaskDelay(5);//延时一下
}
}
}

任务 2 入口函数,如果 pwm_mode 等于 0 时,每 50ms 通过程序修改一次占空比,红绿蓝三种灯轮变
化:

//用户修改占空比,改变 LED 灯亮度任务 void  task_pwm2(void *pvParameter)
{
int pwm_level=0;//0~255 占空比 int pwm_index=1;//红 1 绿 2 蓝 3

while(1)
{
if(0==pwm_mode)
{
printf("pwm mode2.\r\n"); if(pwm_index==1)
{
//修改红灯占空比 CtrRBG_R(pwm_level); pwm_level+=10;//占空比递增

if(pwm_level>255)
{
pwm_level=0; pwm_index=2; CtrRBG_R(255);
}
}
else if(pwm_index==2)
{
//修改绿灯占空比 CtrRBG_G(pwm_level); pwm_level+=10;//占空比递增

if(pwm_level>255)
{
pwm_level=0; pwm_index=3; CtrRBG_G(255);
}
}
else

if(pwm_level>255)
{
pwm_level=0; pwm_index=1; CtrRBG_B(255);
}
}
}
vTaskDelay(5);//延时 50ms
}

}

在上面的代码里,修改红绿蓝占空比是通过三个函数 CtrRBG_R()、CtrRBG_G()和 CtrRBG_B()进行的,
这三个函数类似。
我们选其中一个讲解,这类函数先是把最大的占空比 LEDC_MAX_DUTY 平均分为 255 等分,根据参数计 算出当前的占空比,然后通过 ledc_set_duty()和 ledc_update_duty()函数更新占空比。为什么我们使 用 255 等分呢?因为我们说的真彩色是 24 位色,他是由红绿蓝三种色混合起来的,每一种颜色就是 8 位 色,2 的 8 次方就是 256,而 0~255 就是 256 级颜色,关于这方面的知识,可以百度查询理解一下。

//设置红灯的 PWM 级别
//输入 level 取值 0~255
void  CtrRBG_R(unsigned  char level)
{
int duty=0;

if(level==255)
{
duty=LEDC_MAX_DUTY;
}
else if(level==0)
{
duty=0;
}
else
{
//计算占空比 duty=(level*LEDC_MAX_DUTY)/255;
}

ledc_set_duty(LEDC_HIGH_SPEED_MODE, PWM_RED_CHANNEL, duty);//修改占空比 ledc_update_duty(LEDC_HIGH_SPEED_MODE, PWM_RED_CHANNEL);//新的占空比生效
}

3.8.5. 实验过程

配置下载串口、波特率、编绎和程序下载的详细过程请往回看 3.1.4,在这个实验里都是一笔带过。 (1) 把开发板通过 USB 线接到电脑上,通过设备管理器查看生成的串口。开发板在我们演示电脑上生成的是 COM3。
(2) 在 menuconfig 菜单里配置下载程序串口。提供的例程配置的串口是 COM3,波特率为 921600。
(3) 通过 make all 编绎工程。
(4) 当编绎通过之后,使用命令 make flash 把程序下载到开发板上。或者参考 2.3.2 节,使用工具下载。
(5) 按下开发板的复位键,让程序运行起来,观察彩灯的渐变过程。

(6) 按下 key1 键,观察彩灯的渐变过程,然后和按键前进行对比,也就是彩灯的两种模式变化区别。 (7) 我们认为自动变化过程比较适合做呼吸灯。

最后推荐一款开发套件,可以手淘扫码查看。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bytechip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值