本帖最后由 正点原子运营 于 2025-12-15 09:25 编辑
第十一章 LED实验
1)实验平台:正点原子DNESP32P4开发板
2)章节摘自【正点原子】ESP32-P4开发指南— V1.0
3)购买链接:https://detail.tmall.com/item.htm?id=873309579825
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子DNESP32S3开发板技术交流群:132780729
本章教程介绍了ESP32-P4的GPIO输出应用,通过点灯例程帮助大家理解其基本功能。点灯作为经典的测试案例,能够让读者对ESP32-P4的应用有一个简单而全面的认识,为后续更复杂的项目奠定基础。
本章分为如下几个小节:
11.1 GPIO及LED介绍
11.2 硬件设计
11.3 程序设计
11.4 下载验证
11.1 GPIO及LED介绍
11.1.1 GPIO简介
ESP32-P4芯片提供了55个通用输入输出(GPIO)功能,使其在各种应用中具备灵活性和适应性。这些GPIO的主要特点包括:
1,多功能性:每个GPIO引脚不仅可以作为输入或输出,还可以通过IO MUX配置为不同的功能(详细内容可回溯到第二章),例如PWM、ADC、I2C、SPI等,这使得ESP32-P4能够适应多种外设连接。
2,高电流输出:ESP32-P4的GPIO引脚支持高达40mA的电流输出,可以直接驱动LED等低功耗负载,降低了外部驱动电路的复杂性。
3,可编程性:通过ESP-IDF(SDK)开发框架,用户可以灵活地配置每个GPIO的输入输出模式、上拉下拉等参数,以满足特定应用需求。
4,中断支持:GPIO引脚支持中断功能,能够在信号变化时触发中断,适用于实时响应的应用,如按键检测、传感器触发等。
5,状态指示:GPIO可以用作LED指示灯,通过简单的高低电平切换,实现状态指示,方便用户调试和监控系统运行状态。
ESP32-P4的GPIO功能为开发者提供了强大的硬件支持。在本章中,我们将通过点灯的例程深入了解GPIO的应用与配置。
11.1.2 LED简介
LED(发光二极管)是一种高效、长寿命的小型半导体器件,通过电流发光,具有能量转换效率高、热量少和环境友好等优点。常用于指示灯、显示屏和照明设备,LED能够快速响应并提供多种颜色,使其在电子产品中应用广泛。在ESP32-P4的点灯例程中,GPIO的控制使得LED的开关变得简单而直观,帮助用户理解其应用。
1,LED灯发光原理
LED器件是一种以固态半导体为核心的发光器件。当在具有PN结的半导体材料两端加上正向电流时,半导体内部的载流子产生复合,使能量以光子的形式释放从而发光。因此,LED是冷光源,且不存在灯丝等发光引起的发热,易烧等缺陷。下图为LED器件的工作原理示意图。
图11.1.2.1 LED器件的工作原理
上图中,半导体PN结具有正向导通、反向截止和击穿特性。在外加偏压为零且处于热平衡时,PN结内部没有载流子复合,因此不发光。而当施加正向偏压时,PN结的发光过程可以分为三步:首先,载流子在正向偏压下被注入;其次,电子和空穴在P区复合,释放能量;最后,复合产生的能量以光的形式向外辐射。简单来讲,电流通过PN结时,电子在电场力作用下向P区移动,与空穴复合,释放出多余能量并产生光子,实现PN结的发光功能。
注意:LED的光颜色由所用半导体材料的能带宽度决定,不同材料产生不同波长的光,从而实现多样的颜色输出。这种高效的发光机制使LED在照明和指示应用中广泛使用。
2,LED灯驱动原理
LED驱动是指通过稳定的电源为LED提供适宜的电流和电压,确保其正常发光。LED驱动方式主要有恒流和恒压两种,其中,恒流驱动因其能限定电流而备受青睐。由于LED灯对电流变化极为敏感,一旦电流超过其额定值,可能导致损坏。因此,恒流驱动通过确保电流的稳定行,进而保障LED的安全运行。接下来,我们看一下LED两种驱动方式。
1)灌入电流接法。指的是LED的供电电流是由外部提供电流,将电流灌入我们的MCU,其风险是当外部电源出现变化时,会容易导致MCU的管脚烧坏。下图为灌入电流接法。
图11.1.2.2 灌入电流接法
2)输出流接法。指的是MCU提供电压电流,将电流输出给LED,若使用MCU的GPIO直接驱动LED,则驱动能力较弱,可能无法提供足够的电流驱动LED。下图为输出电流接法。
图11.1.2.3 输出电流接法
DNESP32P4开发板上的LED电路采用灌入电流接法,这种方式避免了MCU直接提供电压电流来驱动LED,从而有效减轻了MCU的负载。这使得MCU能够更加专注于执行其他核心任务,进而提升了整体系统的性能和稳定性。
3,LED的压降和驱动电流
正点原子DNESP32P4开发板的LED电路采用了如图11.1.2.22所示的电路来驱动LED灯。那么,这种电路流经LED的电流是多少呢?在讨论之前,我们需要先了解一个基础知识,即LED灯的压降参考值。以下是贴片LED的压降参考值:
1)红色的压降为1.82~1.88V,电流5~8mA。
2)绿色的压降为1.75~1.82V,电流3~5mA。
3)蓝色的压降为3.1~3.3V,电流8~10mA。
根据上述的贴片LED压降参考值,我们可以利用基尔霍夫电压定律计算图11.1.2.22中的LED电流。计算过程如下:
(3.3 – 1.8)/ 510R = 2.9mA 在不考虑二极管本身电阻的情况下,流过LED的电流为2.9 mA。尽管这个电流值不在贴片LED的标准电流参考范围内,但2.9 mA的电流仍足以让红色LED发光。
在许多电路中,无论是板载哪种颜色的LED,通常都会使用相同数值的限流电阻。这主要是出于统一物料和简化设计的考虑。使用相同的电阻值可降低生产和维护的复杂性,方便库存管理。此外,统一的电阻值还可以简化电路设计流程,使得设计人员在设计和调试时更加高效。
11.2 硬件设计
11.2.1 例程功能
在500 ms的周期内,LED0的电平状态会发生翻转。
11.2.2 硬件资源
1)LED灯
LED 0 - IO51
11.2.3 原理图
LED灯相关原理图,如下图所示。
图11.2.3.1 LED硬件原理图
上图中,LED0由ESP32-P4的GPIO51号管脚控制亮灭,而LED1则由IO扩展芯片的第13位管脚控制。在后续章节中,笔者将重点讲解如何通过IIC接口控制IO扩展芯片,以实现对各个扩展管脚的灵活控制。同时,PWR指示灯表示电源状态,插上电源时该灯会亮起。
11.3 程序设计
11.3.1 GPIO的IDF驱动
GPIO外设驱动位于ESP-IDF的components\esp_driver_gpio目录。该目录中的include文件夹存放GPIO相关的头文件,声明了GPIO函数和结构体等;而src文件夹则存放实际的GPIO操作函数。要使用GPIO功能,必须先导入以下头文件。
接下来,作者将介绍一些常用的GPIO函数,这些函数的描述及其作用如下:
1,GPIO配置函数gpio_config
该函数用于配置GPIO的模式、上下拉等功能,其函数原型如下:
- esp_err_t gpio_config(const gpio_config_t *pGPIOConfig);
复制代码 函数形参:
表11.3.1.1 gpio_config函数形参描述
返回值:
ESP_OK表示操作成功。
ESP_ERR_INVALID_ARG表示参数错误。可能是由于提供了无效的GPIO配置,或者pGPIOConfig为空。
pGPIOConfig为指向GPIO配置结构体的指针。接下来,笔者将详细介绍gpio_config_t结构体中的各个成员变量,如下代码所示:
- typedef struct {
- uint64_t pin_bit_mask; /* GPIO引脚:位掩码设置,每个位映射到一个GPIO */
- gpio_mode_t mode; /* GPIO模式:设置输入/输出模式 */
- gpio_pullup_t pull_up_en; /* GPIO上拉配置 */
- gpio_pulldown_t pull_down_en; /* GPIO下拉配置 */
- gpio_int_type_t intr_type; /* GPIO中断类型 */
- #if SOC_GPIO_SUPPORT_PIN_HYS_FILTER /* 不支持GPIO滞后控制 */
- gpio_hys_ctrl_mode_t hys_ctrl_mode; /* GPIO滞后控制:对斜率输入的滞后滤波 */
- #endif
- } gpio_config_t;
复制代码 上述结构体用于配置GPIO引脚的各种参数,以下对各个成员做简单介绍。
1)pin_bit_mask:
设置管脚位。使用方法:1 << x(x:0~55)。
2)mode:
设置管脚模式。此字段可配置为GPIO_MODE_DISABLE禁用模式、GPIO_MODE_INPUT输入模式、GPIO_MODE_OUTPUT输出模式、GPIO_MODE_OUTPUT_OD开漏输出模式、GPIO_MODE_INPUT_OUTPUT_OD开漏输入和输出模式和GPIO_MODE_INPUT_OUTPUT输入和输出模式。
3)pull_up_en:
配置管脚是否需要上拉。此字段可配置为GPIO_PULLUP_DISABLE禁用上拉和GPIO_PULLUP_ENABLE使能上拉。
4)pull_down_en:
配置管脚是否需要下拉。此字段可配置为GPIO_PULLDOWN_DISABLE禁用下拉和GPIO_PULLDOWN_ENABLE使能下拉。
5)intr_type
配置管脚中断类型。此字段可配置为GPIO_INTR_DISABLE禁用中断、GPIO_INTR_POSEDGE上升沿中断类型、GPIO_INTR_NEGEDGE下降沿中断类型、GPIO_INTR_ANYEDGE任意沿中断类型、GPIO_INTR_LOW_LEVEL低电平触发中断类型和GPIO_INTR_HIGH_LEVEL高电平触发类型。
2,设置管脚输出电平gpio_set_level
该函数用于配置某个GPIO引脚的输出电平,其函数原型如下所示:
- esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level);
复制代码 函数形参:
表11.3.1.2 gpio_set_level函数形参描述
返回值:
ESP_OK表示操作成功。
ESP_ERR_INVALID_ARG表示参数错误,例如指定的GPIO引脚编号无效。
3,获取管脚电平gpio_get_level
该函数用于获取某个GPIO引脚的电平,其函数原型如下所示:
- esp_err_t gpio_get_level(gpio_num_t gpio_num);
复制代码 函数形参:
表11.3.1.3 gpio_get_level函数形参描述
返回值:
0表示GPIO输入电平为低。
1表示GPIO输入电平为高。
上述函数是本实验所需的核心GPIO函数。对于其他未提及的GPIO函数,用户计划在需要时再进行了解。
11.3.2 程序流程图
图11.3.2 LED实验程序流程图
11.3.3 程序解析
在01_led例程中,作者在01_led\components\BSP路径下新建了一个LED文件夹和CMakeLists.txt文件。其中,LED文件夹用于存放LED驱动,而CMakeLists.txt文件则用于将驱动添加至构建系统,以便项目工程能够使用LED驱动功能。
1,LED驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。LED驱动源码包括两个文件:led.c和led.h。
下面先解析led.h的程序。对LED引脚做了相关定义以及功能实现。
- <font size="3">/* 引脚定义 */</font>
- <font size="3">#define LED0_GPIO_PIN GPIO_NUM_51 /* LED0连接的GPIO端口 */</font>
- <font size="3">/* LED0端口定义 */</font>
- <font size="3">#define LED0(x) do { x ? \</font>
- <font size="3"> gpio_set_level(LED0_GPIO_PIN, 1): \</font>
- <font size="3"> gpio_set_level(LED0_GPIO_PIN, 0); \</font>
- <font size="3"> } while(0) /* LED0翻转 */</font>
- <font size="3">/* LED取反定义 */</font>
- <font size="3">#define LED0_TOGGLE() do { </font>
- <font size="3">gpio_set_level(LED0_GPIO_PIN, !gpio_get_level(LED0_GPIO_PIN)); </font>
- <font size="3">} while(0) /* LED0翻转 */</font>
复制代码 上述程序中,笔者巧妙地编写了LED0(x)宏,用于控制GPIO51管脚的电平状态。当x为1时,该宏会设置GPIO51管脚输出高电平;反之,则输出低电平。此外,作者还定义了LED0_TOGGLE()宏,能够快速翻转GPIO51管脚的电平状态。这些宏的实现基于之前小节所介绍的函数,使得对LED的控制变得简洁而高效。
下面我们再解析led.c的程序,看一下初始化函数led_init,代码如下:
- <font size="3">/**</font>
- <font size="3"> * @brief 初始化LED</font>
- <font size="3"> * [url=home.php?mod=space&uid=271674]@param[/url] 无</font>
- <font size="3"> * @retval 无</font>
- <font size="3"> */</font>
- <font size="3">void led_init(void)</font>
- <font size="3">{</font>
- <font size="3"> gpio_config_t gpio_init_struct = {0};</font>
- <font size="3"> gpio_init_struct.intr_type = GPIO_INTR_DISABLE; /* 失能引脚中断 */</font>
- <font size="3"> gpio_init_struct.mode = GPIO_MODE_INPUT_OUTPUT; /* 输入输出模式 */</font>
- <font size="3"> gpio_init_struct.pull_up_en = GPIO_PULLUP_DISABLE; /* 失能上拉 */</font>
- <font size="3"> gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE; /* 失能下拉 */</font>
- <font size="3"> gpio_init_struct.pin_bit_mask = 1ull << LED0_GPIO_PIN; /* 设置引脚的位掩码 */</font>
- <font size="3"> ESP_ERROR_CHECK(gpio_config(&gpio_init_struct)); /* 配置GPIO */</font>
- <font size="3"> LED0(1); /* 关闭LED0 */</font>
- <font size="3">}</font>
复制代码 在led_init函数中,笔者首先对gpio_init_struct结构体各个成员变量进行了参数配置。接着,调用gpio_config函数,利用该配置参数完成了GPIO的初始化工作。最后,通过调用LED0(x)宏定义,实现了LED灯的关闭操作,即输出高电平信号。整个流程清晰、简洁,有效地实现了对LED灯的控制。
值得注意的是,我们将GPIO51管脚配置为输入输出模式(GPIO_MODE_INPUT_OUTPUT),主要是因为ESP-IDF并没有提供相关的电平翻转函数。因此,要实现LED0_TOGGLE()宏的电平翻转功能,必须先读取管脚的当前电平,然后设置其相反电平,这样才能实现电平翻转。若配置为输出模式(GPIO_MODE_OUTPUT),则无法使用gpio_get_level函数获取管脚电平,从而无法实现电平翻转功能。
2,CMkaLists.txt 文件
本例程的功能实现主要依靠LED驱动。要在main函数中,成功调用LED文件中的内容,就得需要新建和配置BSP文件夹下的CMakeLists.txt文件,配置内容如下:
- <font size="3">set(src_dirs</font>
- <font size="3"> LED)</font>
- <font size="3">set(include_dirs</font>
- <font size="3"> LED)</font>
- <font size="3">set(requires</font>
- <font size="3"> driver)</font>
- <font size="3">idf_component_register( SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} </font>
- <font size="3">REQUIRES ${requires})</font>
- <font size="3">component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)</font>
复制代码 在这个CMakeLists.txt文件中,作者首先定义了源文件和头文件的目录,以及所需的驱动库。然后,通过idf_component_register命令将这些设置注册到构建系统中,以便项目能够使用LED驱动功能。此外,component_compile_options命令用于设置编译选项,以优化代码性能。
注意:在以后的章节中,我们不会再一次新建CMakeLists.txt文件,我们会在此文件上略作修改,就可以把其他驱动添加至构建系统当中。
3,main.c驱动代码
在main.c里面编写如下代码。
- <font size="3">void app_main(void)</font>
- <font size="3">{</font>
- <font size="3"> esp_err_t ret;</font>
- <font size="3"> </font>
- <font size="3"> ret = nvs_flash_init(); /* 初始化NVS */</font>
- <font size="3">if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)</font>
- <font size="3"> {</font>
- <font size="3"> ESP_ERROR_CHECK(nvs_flash_erase());</font>
- <font size="3"> ESP_ERROR_CHECK(nvs_flash_init());</font>
- <font size="3"> }</font>
- <font size="3"> led_init(); /* 初始化LED */</font>
- <font size="3"> while(1)</font>
- <font size="3"> {</font>
- <font size="3"> LED0_TOGGLE();</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(500)); /* 延时500ms */</font>
- <font size="3"> }</font>
- <font size="3">}</font>
复制代码 在这个代码中,定义了两个错误码,用于处理NVS(非易失性存储器)的初始化问题。如果在初始化时遇到没有可用空间或无法识别版本的问题,程序将擦除整个NVS分区并重新初始化。接下来,初始化LED并进入一个无限循环,不断翻转LED状态并延时500毫秒。
11.4 下载验证
程序下载完之后,可以看到 LED0以每次500ms闪烁。
|