ESP32外设

入门文档

2025-05-28

写在前边

    弄这个文档的初衷是为了更快地入门ESP32

GPIO

GPIO有四种常见的工作模式:

输出:可以设置GPIO的高低电平

输入:可以获取外部输入的高低电平信息,一般要设置加上拉电阻或下拉电阻

浮空输出:可以设置GPIO的高低电平,但要在电路外部中增加上拉电阻

开漏输入:可以获取外部输入的高低电平信息,但要在电路外部中增加上拉电阻

GPIO的使用流程:

初始化->设置GPIO工作模式

GPIO初始化

定义结构体->调用API初始化GPIO

①定义结构体

结构体由以下内容组成

②使用API初始化GPIO

GPIO输出

输出的两个参数分别是:

gpio_num:GPIO引脚编号(如GPIO_NUM_2)。

level:电平值(0/1)

这里level非零即为1

GPIO交换矩阵

GPIO交换矩阵的核心作用:

动态信号路由:允许将90%以上的外设信号(如UART、PWM、SPI等)自由映射到 任意可用的GPIO引脚(部分特殊功能引脚除外)。

突破物理限制:解决PCB设计时的引脚冲突问题,例如可将同一外设(如 UART0)的TX和RX分配到不同位置的引脚。

简而言之,可以让任意引脚使用任意的功能。

定时器

PWM

定时器是通道的基础.

通道必须绑定到一个已通过ledc_timer_config配置的定时器。定时器定义了PWM的全局参数(如频率),而通道负责将这一时间基准转化为具体GPIO的输出。

共享性:

一个定时器可被多个通道共享。例如,定时器配置为1kHz频率后,多个通道可基于同一频率输出不同占空比的信号。

参数一致性:

通道的speed_mode必须与绑定的定时器一致,否则PWM信号无法正确生成。

配置流程:配置PWM,绑定通道和GPIO

①配置PWM:创建PWM配置结构体->通过API使用结构体对PWM的定时器进行配置

②绑定:完成通道绑定设置->通过API使用结构体对PWM的定时器进行绑定

创建PWM配置结构体

对PWM进行初始化设置,设置定时器频率、通道,时钟源,翻转频率,分辨率

↓↓ESP32的时钟源类型↓↓

时钟源类型

频率范围

适用模式

特点

APB_CLK 

80MHz(默认)

高速模式

高精度、低抖动  

RTC8M_CLK 

~8MHz(可校准)

低速模式

低功耗、可深度睡眠工作

REF_TICK 

1MHz

特殊应用

同步系统时钟

此处一般为自动

使用API配置PWM

ledc_timer_config用于初始化用到的定时器()

通道初始化

指定配置的通道,及其绑定的定时器与GPIO,并指明定时器频率,中断类型和信号占空比

使用API进行绑定

ledc_channel_config用于初始化ledc输出通道 以及将timer关联起来

这样对应的通道就能输出PWM频率了(上例为On_Board_LED_GPIO这个GPIO接口输出PWM)

计时计数

IIC协议:

注意!注意!新旧I2C协议代码不通用!不兼容!

旧版"driver/i2c.h"

新版"driver/i2c_master.h"

配置流程:写初始化I2C函数,写I2C对寄存器读写函数(用于寄存器初始化函数中),主函数调用I2C初始化函数与寄存器初始化函数

①配置I2C:创建I2C初始化结构体->配置参数->安装I2C驱动

②寄存器读写函数->寄存器初始化函数需要用到寄存器值读写

③在主函数中调用I2C初始化函数与寄存器初始化函数

文件中创建结构体初始化I2C

通过I2C对寄存器值进行读取写入(用于寄存器初始化函数中)

BSP_I2C_NUM依旧是使用的I2C外设编号,SENSOR_ADDR是寄存器的地址,I2C通过地址找到寄存器并进行读写操作。

在主函数中调用初始化函数

ESP_ERROR_CHECK 是一个宏函数,位于 esp-idf 文件中,用于检测执行函数的返回结果,如果没有错误,什么事情都不会发生,如果有错误,会引起单片机重启。使用这个宏函数,需要包含 esp_err.h 头文件。

串口(UART)

使用方法与其他的配置大同小异

使用流程:创建配置结构体->调用结构体进行配置->安装驱动(设置RX/TX缓冲区)->读取/发送

创建配置结构体

baud_rate 波特率 设置数据传输速率,单位是每秒比特数(bps)
data_bits 数据位 定义每个数据包的位数
parity 校验位 错误检测机制,检测单比特错误
stop_bits 停止位 标识数据包结束的位数
flow_ctrl 流控制 防止数据溢出的硬件流控
source_clk 时钟源 选择 UART 的基准时钟源

这里对流控制进一步解释(因为我也不会)

流控制的作用:

防止数据溢出:当接收方缓冲区满时,通过流控制信号让发送方暂停传输。

保证传输可靠性:避免高速发送导致低速接收设备无法处理数据。

适应不同硬件性能:协调不同处理速度的设备(如MCU与PC)之间的通信。

硬件流控制(通过物理引脚信号控制数据流):

RTS(RequestToSend):接收方准备好接收数据时,拉高此信号。

CTS(ClearToSend):发送方检测到CTS有效时,才允许发送数据。

调用API配置结构体

这里UART_PORT_NUM是串口号

ESP32芯片通常包含3个独立的UART硬件控制器,串口0默认串口下载,串口1一般为FLASH的引脚(可通过GPIO交换矩阵更改引脚),串口2常用

安装UART驱动

串口号-发送缓存大小-接收缓存大小-队列长度-队列句柄(传回来用的)-中断标志(一般使用0,使用默认配置)

读取串口数据

这函数有返回值:如果成功,则返回读取的字节数;失败则返回-1。

串口号-缓冲区-缓冲区大小-等待数据的超时时间 portMAX_DELAY表示无限等待

发送串口数据

串口号-待发送数据的源地址-发送数据的字节数

更改串口引脚(可选)

通过GPIO矩阵,将默认的串口引脚更改。若更改,则需在安装驱动前配置该项。

WiFi

STA模式

STA模式:连接已有的网络,ESP32作为终端设备

使用流程:

总流程:创建并初始化函数->监听设备事件->分情况调用回调函数进行各项初始化和配置

初始化流程:初始化NVS(存储SSID和密码),初始化TCP/IP栈(使用esp_netif_init),初始化事件系统循环(存储系统事件,包括WIFI MQTT BLU等),初始化WIFI,设置事件回调函数并监听事件

初始化NVS,TCP/IP,系统事件循环

初始化WIFI

先定义个WIFI初始化结构体->再创建个设置STA的初始化结构体->调用API进行初始化(WIFI和WIFI设置)->启动WIFI

设置事件回调函数

WIFI事件的回调函数

事件回调函数有以下几个要素:

  1. 有个能被调用的"注册回调函数的接口"(就是将回调函数与事件进行绑定)
  2. 有个回调函数(被调用之后产生的动作)

WIFI需要注册多个回调函数:WIFI和网络(IP_EVENT)

这里的事件回调函数,主要是为了判断WIFI的各个事件段该做什么,比如未连接WiFi怎么办,获取ip之后怎么办。

一键配网模式(SmartConfig)

MQTT

基于TCP/IP的低带宽需求报文传输服务

使用流程:

①创建MQTT句柄(通过这个对MQTT进行操作)->创建并调用结构体(存储初始化MQTT的参数)->②->注册回调函数->启用MQTT

②配置事件回调函数,对MQTT各个状态进行反应(对订阅、发布、接收数据等进行操作)

①MQTT基础设置

创建MQTT句柄

这里实际上创建了一个MQTT客户端,名为s_mqtt_client

后续所有对MQTT客户端的操作,都使用s_mqtt_client

创建并填充结构体

调用配置结构体并初始化客户端

注册回调函数

为 MQTT 客户端注册事件回调函数。具体来说:

  1. 调用 esp_mqtt_client_register_event() 来关联 MQTT 客户端 s_mqtt_client 和事件回调函数aliot_mqtt_event_handler。
  2. 指定 ESP_EVENT_ANY_ID 表示接收所有类型的 MQTT 事件(例如连接、断开、发布、订阅、接收数据等)。
  3. 回调函数中可以根据事件类型(event_id)做出相应处理,例如记录日志、修改连接状态或发布配置信息。
  4. 最后传入 s_mqtt_client 作为用户数据,这样在回调中可以访问到该 MQTT 客户端句柄或相关信息。

启用MQTT

②配置MQTT事件回调函数

可以用这个回调函数直接读取MQTT订阅的数据

配置方式与WIFI大差不差

注:

使用MQTT发送数据

发送流程:

NVS 是 Non-Volatile Storage(非易失性存储)的缩写,是 ESP32 用来持久化保存数据的一种机制。

它类似于"小型文件系统",常用于保存 WiFi 配置、密钥、参数等,即使断电数据也不会丢失。

此处,NVS 主要用于保证系统能正常保存和读取配置信息。

初始化WIFI

传入回调函数,用于通知连接成功事件

监听WIFI连接事件

直到WiFi连接成功后,才启动MQTT连接

发布数据

JSON

JSON是一种规范好的数据格式,本质上就是字符串。用于传输数据信息,基于"键-值对",可以包含对象、数组、字符串、数字、布尔、NULL这六种最基本的数据。

ESP32中可以使用cJSON数据解析器来分析和打包JSON数据,需要调用"cJSON.h"。

JSON的基本格式如下

解析JSON

流程:解析JSON->……>解析JSON->读取键的数据->读取数据的值

解析JSON并保存

本文档中只说明ESP32中的情况

读取JSON中某个键的数据

这里的数据,可以是具体的数值和字符串,也可以是对象。

若JSON层层嵌套,则需层层解析。重复使用cJSON_GetObjectItem,一层一层解析,以下操作也同理

读取获取数据的值

如果要获取数据的值为字符串,则用cJSON_GetStringValue()

如果是数值,则用cJSON_GetNumberValue()

生成JSON

流程:创建cJSON格式的对象->向对象中添加键-值

创建cJSON格式的对象

这里创建了个名为js_obj_tx_humidity的cJSON对象。

向对象中添加键-值

这里向js_obj_tx_humidity这个对象中添加的键-值为"Humidity","%d"

然后向js_obj_tx_humidity这个对象中又添加了一个对象js_obj_sys

WS2812

以下是调用已有配置对WS2812进行驱动

单总线控制的彩色LED灯珠,因为对控制时序比较严格,故使用RMT红外控制。

同GPIO等操作方式,需先使用结构体进行初始化,再使用API通过结构体配置。

Tips:使用"led_strip.h"

使用流程(基于官方Example):

创建结构体(共两个)->调用API进行配置->使用函数配置亮度颜色等参数

结构体创建

先创建LED的配置结构体和被操控的LED对象

再创建RMT的配置结构体

调用API进行配置

使用led_strip_new_rmt_device函数

利用传递进来两个参数(LED和RMT的配置结构体),生成到先前创建的LED对象(led_strip)中

配置LED亮度等参数

对生成的对象(led_strip),进行参数配置;配置后刷新LED状态

这样WS2812就会常亮了

(不基于BLink例程)以下是从RMT驱动开始,驱动WS2812

基于此帖子(【ESP32 IDF】用RMT控制 WS2812 彩色灯带 - 东邪独孤 - 博客园

DHT11

单总线工作的数字温湿度传感器,工作范围"-20~60摄氏度""5%~90%"

DHT11基于单总线,同样利用RMT红外进行控制(接收数据)。

工作流程:

定义RMT接收用结构体->调用RMT接收通道API->使用队列接收数据->接收完使用回调至接收模式

初始化DHT11

调用rmt接受通道

使用队列接收数据

将RMT读取到的脉冲数据处理为温度和湿度

脉冲信号处理,复制即可

调整脉冲信号以接受DHT11数据

脉冲信号调整部分复制即可

调整后接收数据

LCD液晶显示屏

这里采用液晶屏驱动芯片ST7789,采用SPI通信方式与ESP32连接.

由于esp32的引脚数量少,可以使用寄存器PCA9557,对远程位置上的输入输出控制和状态监控,用于解决微控制器I/O资源不足的问题,并允许通过简单的I2C接口实现复杂的I/O操作。

液晶屏的LCD_CS引脚由IO扩展芯片pca9557控制,所以需要先初始化pca9557,而pca9557是i2c通信芯片,所以又需要先初始化i2c。

初始化PCA8559

首先需要设置PCA9557的工作模式(如输入或输出)。这是通过向特定寄存器写入配置数据完成的。

读写PCA8559

控制PCA9557任意一个单独引脚变高变低

 

控制PCA9557_LCD_CS输出高低电平

初始化I2C

初始化LCD

使用ESP32中遇到的奇奇怪怪的问题

  1. 明明添加了"esp_log.h"这种头文件,但是根本识别不到该怎么办?

    1. ESP-IDF的路径设置对了吗。
    2. 需要用"ESP-IDF: 添加 VS Code 配置文件夹"给这个项目添加以下配置文件夹
  2. 编译卡在CMake过不去,但是检查了一圈程序发现没问题

    1. 删掉build文件夹,再重新编译一下
  3. 明明头文件设置好了,但是头文件中的函数无法调用怎么办?

    1)在CMakeLists.txt中,按照格式把需要编译的.C程序添加进去(根据已有的格式设置就行)