当前位置: 首页 > news >正文

ESP32开发之LED闪烁和呼吸的实现

  • 硬件电路介绍
  • GPIO输出模式
  • GPIO配置过程
  • 闪烁灯的源码
  • LED PWM的控制器(LEDC)概述
  • LEDC配置过程及现象
  • 整体流程

硬件电路介绍

电路图如下:

在这里插入图片描述

只要有硬件基础的应该都知道上图中,当GPIO4的输出电平为高时,LED灯亮,反之则熄灭。如果每间隔一段时间进行一次电平的反转,则将使LED产生闪烁的效果。

GPIO模式

在进行GPIO控制之前,需要熟悉一下ESP32的GPIO几种模式:

GPIO模式模式宏定义说明
输入模式GPIO_MODE_INPUT可以通过配置项pull_up_en或pull_down_en配置上拉或者下拉
推挽输出模式GPIO_MODE_OUTPUT高低电平输出
开漏输出模式GPIO_MODE_OUTPUT_OD通常用于I2C
中断可通过intr_type配置项配置触发方式:上升沿/下降沿/双沿/电平触发等
禁用GPIO_MODE_DISABLE禁用GPIO,不作为输入也不作为输出
输入输出模式GPIO_MODE_INPUT_OUTPUT
输入及开漏输出GPIO_MODE_INPUT_OUTPUT_OD

注意:

  • 使用中断时,将GPIO模式设置为输入模式
  • 如果GPIO用于I2C的SDA,设置模式为GPIO_MODE_INPUT_OUTPUT_OD,且需要配置上拉,也可在芯片相关引脚增加上拉电路

GPIO配置过程

  • 配置GPIO

    使用结构体gpio_config_t对GPIO相关参数进行配置

  • 注册GPIO

​ 通过函数gpio_config函数将以上配置注册进系统

  • 通过GPIO相关API函数对GPIO进行控制

​ 比如此次实验是控制LED闪烁,那么则是使用gpio_set_level函数进行输出电平控制

闪烁灯的源码

/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file 	 Filename without the absolute path* @brief 	 Brief description* @author 	 HalfMoon2* @date 	 2025-05-20* @version	 v0.1* * @revision history:* 	 2025-05-20 - Initial version.*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"#define LED_GPIO GPIO_NUM_4 //根据实际的连接方式更改void ledCtlTask(void *pvParam)
{while(1){gpio_set_level(LED_GPIO, 1);  // 设置为高电平(点亮 LED)vTaskDelay(pdMS_TO_TICKS(500));  // 延时 0.5 秒gpio_set_level(LED_GPIO, 0);  // 设置为低电平(熄灭 LED)vTaskDelay(pdMS_TO_TICKS(500));  // 延时 0.5 秒}
}void app_main(void)
{// 配置 GPIOgpio_config_t io_conf = {.pin_bit_mask = (1ULL << LED_GPIO),    // 选择 GPIO.mode = GPIO_MODE_OUTPUT,              // 设置为输出模式.pull_up_en = GPIO_PULLUP_DISABLE,     // 不启用上拉.pull_down_en = GPIO_PULLDOWN_DISABLE, // 不启用下拉.intr_type = GPIO_INTR_DISABLE         // 不启用中断};gpio_config(&io_conf);xTaskCreatePinnedToCore(ledCtlTask,"ledCtlTask",2048,NULL,3,NULL,1);
}

LED PWM的控制器(LEDC)概述

从以上案例可以看出,对于通用GPIO的控制要么是高电平,要么是低电平。所以只能控制LED的闪烁现象。而对于ESP32-S3却有专用控制LED的控制器,称之LED PWM。它有8路低速通道。专用于控制LED。当然也可以产生PWM控制电机等。ESP32有两组LED PWM控制器,一组为8路高速通道,另一组为8路低速通道。

LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实现亮度渐变,如果是RGB LED,还能实现颜色的渐变。

LEDC的配置过程及现象

  1. 定时器的配置过程
  • 创建定时器配置结构体

    typedef struct {ledc_mode_t speed_mode;                /* LEDC速度模式, high-speed mode (only exists on esp32) or low-speed mode */ledc_timer_bit_t duty_resolution;      /* LEDC占空比分辨率 */ledc_timer_t  timer_num;                  /* The timer source of channel (0 - LEDC_TIMER_MAX-1) */uint32_t freq_hz;                               /* LEDC 的时钟频率 */ledc_clk_cfg_t clk_cfg;                       /*配置LEDC的时钟源. */bool deconfigure;                             /*是否取消此配置之前的配置,取消之前先要关闭定时器 */
    } ledc_timer_config_t
    
  • 使用相关函数将结构体完成配置

esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);//参数为以上定义的结构体
/*返回值:
* ESP_OK  成功
* ESP_ERR_INVALID_ARG 参数错误
* ESP_FAIL   无法根据给定频率和当前占空比分辨率找到合适的预分频器编号
*ESP_ERR_INVALID_STATE  定时器未配置或未暂停
*/
  1. 配置通道以及指定GPIO
  • 创建配置通道结构体
typedef struct {int gpio_num;                   /* LEDC的输出GPIO*/ledc_mode_t speed_mode;         /* LEDC 速度模式,ESP32S3只能配置为低速 */ledc_channel_t channel;         /*LED PWM的控制器(LEDC) LEDC的通道 */ledc_intr_type_t intr_type;     /*是否开启渐变中断 */ledc_timer_t timer_sel;         /*选择定时器l (0 - LEDC_TIMER_MAX-1) */uint32_t duty;                  /*!< LEDC 通道占空比*/int hpoint;                     /*!< LEDC channel hpoint value, the range is [0, (2**duty_resolution)-1] */struct {unsigned int output_invert: 1;/*!< Enable (1) or disable (0) gpio output invert */} flags;                        /*!< LEDC 标志 */} ledc_channel_config_t;
  • 使用函数完成配置
esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf);
  1. 配置占空比改变PWM信号
  • 使能硬件PWM
//参数intr_alloc_flags为分配的中断优先级
esp_err_t ledc_fade_func_install(int intr_alloc_flags)
  • 配置渐变参数
/*
参数:speed_mode:LEDC的速度模式,只有ESP32有高速模式channel:通道,0-7target_duty:占空比,取值范围 [0, (2**duty_resolution)]max_fade_time_ms:最大的渐变时间
*/
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)
  • 开启渐变
/*
参数:speed_mode:LEDC的速度模式channel:通道,0-7
fade_mode:是否阻塞直到渐变完成,如果设置成LEDC_FADE_WAIT_DONE模式,则不渐变到预定值则不返回
*/
esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_fade_mode_t fade_mode)
  1. 第一阶段实例:实现LED缓慢亮灯
/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file 	 Filename without the absolute path* @brief 	 Brief description* @author 	 HalfMoon2* @date 	 2025-05-27* @version	 v0.1* * @revision history:* 	 2025-05-27 - Initial version.*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include <driver/ledc.h>#define LEDC_MODE 		LEDC_LOW_SPEED_MODE
#define LEDC_DUTY_RES 	LEDC_TIMER_13_BIT
#define LEDC_TIMER_NUM 	LEDC_TIMER_0
#define LEDC_FREQ 		5000
#define LEDC_CHANNEL 	LEDC_CHANNEL_0
#define LEDC_GPIO		GPIO_NUM_4
#define LEDC_DUTY		4095    //2^13-1void ledc_init(void)
{ledc_timer_config_t timer_config={.speed_mode= LEDC_MODE,.duty_resolution= LEDC_DUTY_RES,.timer_num= LEDC_TIMER_NUM,.clk_cfg=LEDC_AUTO_CLK,.freq_hz=LEDC_FREQ};ledc_timer_config(&timer_config);ledc_channel_config_t ledc_channel={.speed_mode = 	LEDC_MODE,.channel 	=	LEDC_CHANNEL,.gpio_num	=	LEDC_GPIO,.intr_type	= 	LEDC_INTR_DISABLE,.duty		=	0,.hpoint		=	0};ledc_channel_config(&ledc_channel);
}void app_main(void)
{ledc_init();ledc_fade_func_install(0);ledc_set_fade_with_time(LEDC_MODE,LEDC_CHANNEL,4095,10000);ledc_fade_start(LEDC_MODE,LEDC_CHANNEL,LEDC_FADE_NO_WAIT);
}

在这里插入图片描述

波形说明:可以明显的看到PWM的占空比的变化,LED也缓慢的亮起。

那么接下来就是实现从亮起再缓慢的熄灭,以此循环则实现了LED呼吸的效果。

  1. 渐变回调函数

LEDC控制器在使能渐变后,每个通道都可以有一个回调函数,通过ledc_cb_register()进行注册

esp_err_t ledc_cb_register(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_cbs_t *cbs, void *user_arg)/*参数:speed_mode:速度模式,只有ESP32有高速模式channel: LEDC通道,低速模式有8个通道cbs:回调函数原型定义在 ledc_cbs_t 结构体中user_arg:用户注册时的数据,用于给回调函数传参 */
  1. 通过事件组的方式将此时LED的状态发送出去,即设置事件值

在中断中避免处理复杂的内容,所以在渐变回调函数中只使用事件组方式发送相关事件。不了解这块的知识可以参考我之前的文章

《ESP32开发之freeRTOS的事件组》

bool IRAM_ATTR ledc_fade_cb(const ledc_cb_param_t *param, void *user_arg)
{BaseType_t  pxHigherPriorityTaskWoken;//如果当前LEDC占空比最大,说明此时LED为开灯状态,反之为关灯状态if(param->duty){xEventGroupSetBitsFromISR(s_ledc_ev,LED_ON_EV,&pxHigherPriorityTaskWoken);}else{xEventGroupSetBitsFromISR(s_ledc_ev,LED_OFF_EV,&pxHigherPriorityTaskWoken);}return pxHigherPriorityTaskWoken;
}
  1. 创建一个任务来接收事件并做渐变过程的改变
void ledc_fade_task(void* param)
{EventBits_t ev;while(1){ev=xEventGroupWaitBits(s_ledc_ev,LED_OFF_EV|LED_ON_EV,pdTRUE,pdFALSE,portMAX_DELAY);if(ev){if(ev&LED_OFF_EV){ledc_set_fade_with_time(LEDC_MODE,LEDC_CHANNEL,LEDC_DUTY,1000);ledc_fade_start(LEDC_MODE,LEDC_CHANNEL,LEDC_FADE_NO_WAIT);}if(ev&LED_ON_EV){ledc_set_fade_with_time(LEDC_MODE,LEDC_CHANNEL,0,1000);ledc_fade_start(LEDC_MODE,LEDC_CHANNEL,LEDC_FADE_NO_WAIT);}}//处理完成需要再次注册回调函数,产生循环ledc_cbs_t cbs={.fade_cb=ledc_fade_cb};ledc_cb_register(LEDC_MODE,LEDC_CHANNEL,&cbs,NULL);}
}
  1. 第二阶段实例:完整实现渐变的循环
/*** Copyright (C) 2024-2034 HalfMoon2.* All rights reserved.* * @file 	 Filename without the absolute path* @brief 	 Brief description* @author 	 HalfMoon2* @date 	 2025-05-27* @version	 v0.1* * @revision history:* 	 2025-05-27 - Initial version.*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include <driver/ledc.h>#define LEDC_MODE 		LEDC_LOW_SPEED_MODE
#define LEDC_DUTY_RES 	LEDC_TIMER_13_BIT
#define LEDC_TIMER_NUM 	LEDC_TIMER_0
#define LEDC_FREQ 		5000
#define LEDC_CHANNEL 	LEDC_CHANNEL_0
#define LEDC_GPIO		GPIO_NUM_4
#define LEDC_DUTY		4095    //2^13-1//通知渐变完成
static EventGroupHandle_t   s_ledc_ev = NULL;//此时为关灯状态
#define LED_OFF_EV (1<<0)//事件组bit0设置为关灯事件//此时为开灯状态
#define LED_ON_EV (1<<1)//事件组bit1设置为开灯事件/*** @brief 	 渐变结束回调函数* @param 	 *param:LEDC callback parameter* @param 	 *user_arg:User registered data* @return 	 返回是否唤醒高优先级任务* @note	 此函数为中断服务函数,所以不应处理过多的操作,那么在此函数中通过发送事件的方式,由渐变任务函数处理事件*/
bool IRAM_ATTR ledc_fade_cb(const ledc_cb_param_t *param, void *user_arg)
{BaseType_t  pxHigherPriorityTaskWoken;//如果当前LEDC占空比最大,说明此时LED为开灯状态,反之为关灯状态if(param->duty){xEventGroupSetBitsFromISR(s_ledc_ev,LED_ON_EV,&pxHigherPriorityTaskWoken);}else{xEventGroupSetBitsFromISR(s_ledc_ev,LED_OFF_EV,&pxHigherPriorityTaskWoken);}return pxHigherPriorityTaskWoken;
}/*** @brief 	 led渐变任务* @param 	 任务参数* @note	 接收事件并做LED操作*/
void ledc_fade_task(void* param)
{EventBits_t ev;while(1){ev=xEventGroupWaitBits(s_ledc_ev,LED_OFF_EV|LED_ON_EV,pdTRUE,pdFALSE,portMAX_DELAY);if(ev){if(ev&LED_OFF_EV){ledc_set_fade_with_time(LEDC_MODE,LEDC_CHANNEL,LEDC_DUTY,1000);ledc_fade_start(LEDC_MODE,LEDC_CHANNEL,LEDC_FADE_NO_WAIT);}if(ev&LED_ON_EV){ledc_set_fade_with_time(LEDC_MODE,LEDC_CHANNEL,0,1000);ledc_fade_start(LEDC_MODE,LEDC_CHANNEL,LEDC_FADE_NO_WAIT);}}//处理完成需要再次注册回调函数,产生循环ledc_cbs_t cbs={.fade_cb=ledc_fade_cb};ledc_cb_register(LEDC_MODE,LEDC_CHANNEL,&cbs,NULL);}
}void ledc_init(void)
{ledc_timer_config_t timer_config={.speed_mode= LEDC_MODE,.duty_resolution= LEDC_DUTY_RES,.timer_num= LEDC_TIMER_NUM,.clk_cfg=LEDC_AUTO_CLK,.freq_hz=LEDC_FREQ};ledc_timer_config(&timer_config);ledc_channel_config_t ledc_channel={.speed_mode = 	LEDC_MODE,.channel 	=	LEDC_CHANNEL,.gpio_num	=	LEDC_GPIO,.intr_type	= 	LEDC_INTR_DISABLE,.duty		=	0,.hpoint		=	0};ledc_channel_config(&ledc_channel);//创建事件组,用于接收和发送渐变事件s_ledc_ev = xEventGroupCreate();//开启硬件PWMledc_fade_func_install(0);//设置渐变参数ledc_set_fade_with_time(LEDC_MODE,LEDC_CHANNEL,LEDC_DUTY,1000);//启动渐变ledc_fade_start(LEDC_MODE,LEDC_CHANNEL,LEDC_FADE_NO_WAIT);//注册渐变回调函数ledc_cbs_t cbs={.fade_cb=ledc_fade_cb,};ledc_cb_register(LEDC_MODE,LEDC_CHANNEL,&cbs,NULL);xTaskCreatePinnedToCore(ledc_fade_task,"ledc_fade_task",2048,NULL,3,NULL,1);
}void app_main(void)
{ledc_init();
}

在这里插入图片描述

整体流程

在这里插入图片描述

http://www.lqws.cn/news/106939.html

相关文章:

  • Tiktok App 登录账号、密码、验证码 XOR 加密算法
  • 道可云人工智能每日资讯|北京农业人工智能与机器人研究院揭牌
  • 【leetcode】15.三数之和
  • day20 奇异值SVD分解
  • 重新审视自回归语言模型的知识蒸馏
  • el-select 实现分页加载,切换也数滚回到顶部,自定义高度
  • Java求职者面试:Spring、Spring Boot、Spring MVC与MyBatis技术深度解析
  • 【C/C++】初步了解享元模式
  • 20250603在荣品的PRO-RK3566开发板的Android13下的使用命令行来查看RK3566的温度【显示优化版本】
  • 机器学习——使用多个决策树
  • Agent智能体应用教程系列(四):仅需几步,拥有自己专属的多agent智能体!
  • 计算A图片所有颜色占B图片红色区域的百分比
  • 关于物联网的基础知识(二)——物联网体系结构分层
  • 用Python训练自动驾驶神经网络:从零开始驾驭未来之路
  • Docker 部署前后端分离项目
  • 第六章 缓存一致性协议 A Primer on Memory Consistency and Cache Coherence - 2nd Edition
  • 阿里云无影云桌面深度测评
  • C++ Learning string类模拟实现
  • 使用glide 同步获取图片
  • 数据解析:一文掌握Python库 lxml 的详细使用(处理XML和HTML的高性能库)
  • Dockerfile使用与最佳实践
  • 微软PowerBI考试 PL300-Power BI 入门
  • centos安装locate(快速查找linux文件)
  • Ubuntu安装Docker命令清单(以20.04为例)
  • Linux入门(十四)rpmyum
  • Go高并发API服务器架构设计:从入门到精通
  • 线程池RejectedExecutionException异常
  • Excel表格批量下载 CyberWin Excel Doenlaoder 智能编程-——玄武芯辰
  • 【办公类-48-04】202506每月电子屏台账汇总成docx-5(问卷星下载5月范围内容,自动获取excel文件名,并转移处理)
  • 2025 Java面试大全技术文章(面试题2)