已写文章链接
本专栏主要内容是记录基于ESP32的开发工作,包括介绍 ESP32 基础知识、开发环境搭建、基础外设使用、蓝牙、WiFi 、与微信小程序联动等知识,达到自己动手做一些智能硬件的目的。
开发过程中主要参考官方资料,包括官网、规格书、参考手册、编程指南、驱动包等。
本专栏适合对ESP32感兴趣,想要找一个简单入手教程的同学。
ESP32 基础知识(已完成)
开发环境搭建(已完成)
(2-1)开发环境搭建(基于Arduino)
(2-2)开发环境搭建(基于VS Code+PlatformIO)
(2-3)开发环境搭建(基于ESP-IDF软件)
基础外设使用(已完成)
(3-1)基础外设使用(GPIO)
(3-2)基础外设使用(USART)
蓝牙(已完成)
WiFi(已完成)
微信小程序(已完成)
从本章开始介绍 ESP32 的基础外设使用,我在刚开始想用 ESP32 做一些东西时,发现网上比较多的资料大多是教大家如何搭建开发环境,而对于基础外设使用并没有过多介绍。等我自己使用Arduino
之后,发现对于基础外设Arduino
的针对 ESP32 开发板的示例相当完善,似乎没有单拿出来写的必要。但鉴于自己也是一个新手,还是把基础的外设示例跑一边,当做熟悉平台了。
搭建环境的时候看好多人说Arduino
编译慢,以后大工程会卡顿。对我自己来说Arduino
的示例足够好用,网上资料比较多。先用它实现一些基础的功能,等对平台熟悉了再决定要不要换到vscode + platformio
或者ESP IDF
。
对于 ESP32 不太熟悉的可以看下面的文章。
(1)ESP32基础知识
硬件和软件版本
本文章使用的软硬件版本如下。
硬件
- 开发板:ESP32-DevKitC-32E
- 模组:ESP32-WROOM-32E
- 芯片:ESP32-DOWD-V3
软件
- 开发环境:Arduino IDE V2.2.1
- ESP32 软件包:V2.0.11
ESP32 的 GPIO 基础知识
GPIO 的定义
以下定义来自于 ChatGPT。
GPIO是通用输入/输出(General Purpose Input/Output)的缩写,是一种在嵌入式系统中常见的接口类型,用于与外部设备进行通信和控制。
GPIO可以分为以下几种分类:
- 数字GPIO:用于数字信号输入和输出。它们可以配置为输入以读取外部传感器或设备的状态,也可以配置为输出以控制外部设备。
- 模拟GPIO:有时也称为模拟输入/输出,用于传输模拟信号。与数字GPIO不同,模拟GPIO可以处理模拟信号,通常用于传感器读取或与模拟设备通信。
- 中断GPIO:允许设备在输入状态发生变化时产生中断。这样可以避免持续地轮询输入状态,而是通过中断方式告知处理器状态的变化。
使用GPIO的方法通常涉及以下步骤:
- 引脚配置:确定每个GPIO引脚的用途(输入或输出),以及引脚上使用的电压级别(高电平或低电平)。
- 初始化设置:在程序中初始化GPIO引脚,设置其方向(输入或输出)、电平状态等参数。
- 读取输入:如果GPIO被配置为输入,程序可以周期性地或基于事件来读取引脚的状态(高电平或低电平)。
- 写入输出:如果GPIO被配置为输出,程序可以控制引脚的状态,即设置引脚输出的电平状态(高电平或低电平)。
- 中断处理:如果支持中断,程序可以设置中断服务程序(ISR),在引脚状态发生变化时执行特定的操作。
在嵌入式系统中,GPIO常用于与各种外部设备进行通信,例如LED灯、按钮、传感器、电机等。使用GPIO需要根据具体的嵌入式系统和硬件平台来编写相应的驱动程序或使用相关的库函数进行控制和访问。
ESP32 的 GPIO 介绍
以STM32H743IGT6
为例,PB.8
和PB.9
在其复用功能为AF4
时只能作为I2C1_SCL
和I2C1_SDA
,比如我想把PB.8
和PB.9
当做IIS
外设使用,那就不行,PB.8
和PB.9
复用功能从AF0-AF15
就没有IIS
功能,要想使用IIS
,得去找IIS
外设对应的引脚。
但在 ESP32 中不是这样的。ESP32 芯片具有 34 个物理 GPIO 管脚(GPIO0 ~ GPIO19、GPIO21 ~ GPIO23、GPIO25 ~ GPIO27 和 GPIO32 ~ GPIO39)。每个管脚都可用作一个通用 IO,或连接一个内部的外设信号。通过 IO MUX、RTC IO MUX 和 GPIO 交换矩阵,可配置外设模块的输入信号来源于任何的 IO 管脚,并且外设模块的输出信号也可连接到任意 IO 管脚。这些模块共同组成了芯片的 IO 控制。
例如对于 ESP32,I2C 的默认引脚是 SDA (GPIO21) 和 SCL (GPIO22)。我们可以使用不同的引脚作为 如果您需要更改引脚,则默认的。 要更改引脚,我们必须在调用之前调用该函数。Wire.setPins(int sda, int scl);Wire.begin();
int sda_pin = 16; // GPIO16 as I2C SDA
int scl_pin = 17; // GPIO17 as I2C SCL
void setup()
{
Wire.setPins(sda_pin, scl_pin); // Set the I2C pins before begin
Wire.begin(); // join i2c bus (address optional for master)
}
像现在有些 STM32 芯片 USART 的 RX 和 TX 支持交换(可以避免硬件工程师把管脚连反的尴尬),但像 EPS32 这种外设引脚支持自定义这种方式还挺新颖的。
当然也不是所有外设引脚都支持自定义。以下是 ESP32 的基本外设支持表。专用 GPIO 就不支持自定义了。
类型 | 功能 |
---|---|
模数转换器 | 专用 GPIO |
DAC接口 | 专用 GPIO |
触摸传感器 | 专用 GPIO |
JTAG的 | 专用 GPIO |
SD/SDIO/MMC 主机控制器 | 专用 GPIO |
电机PWM | 任何 GPIO |
SDIO/SPI从控制器 | 专用 GPIO |
串口 | 任意 GPIO |
I2C接口 | 任何 GPIO |
I2S的 | 任何 GPIO |
LED PWM调制 | 任何 GPIO |
RMT(RMT) | 任何 GPIO |
通用IO输出接口 | 任何 GPIO |
并行 QSPI | 专用 GPIO |
EMAC公司 | 专用 GPIO |
脉冲计数器 | 任何 GPIO |
TWAI公司 | 任何 GPIO |
USB接口 | 专用 GPIO |
** **还有一些限制,以下面的框图为例。
- 某些 GPIO 仅是 INPUT。(例如 GPIO34、GPIO35、GPIO36 等)
- 某些外设具有输出信号,必须在能够配置为 OUTPUT 的 GPIO 上使用。(例如 SPI_MOSI 引脚是输出引脚,就不能自定义到 GPIO34 上,因为 GPIO34 只能作为输入)
- 一些外设,主要是高速外设、ADC、DAC、Touch 和 JTAG 使用专用的 GPIO 引脚。(例如 ADC 外设不支持引脚自定义。 想使用 ADC2_5 时,只能用 GPIO12,而不能自定义到 GPIO13)
- 一些引脚用于连接模块上的闪存 - 这可以防止它们用于任何其他用途 - 如果外围设备路由到其中一个引脚,设备将无法启动。(例如标感叹号的 GPIO6-11)
硬件连接
按键引脚为 GPIO0。LED 灯引脚为 GPIO16。忘了怎么连的可以去看最前面的基础知识,也可以参考在wokwi
中的连接(在wokwi
中没找见电容,自己画了一个,实际电路中要注意)。
软件设计
GPIO 做输入
轮询方式
代码
#define LED 16
#define BUTTON 0
void setup() {
pinMode(LED, OUTPUT);
pinMode(BUTTON, INPUT_PULLUP);
}
void loop() {
if (digitalRead(BUTTON) == 0)
{
delay(100);
if(digitalRead(BUTTON) == 0)
{
digitalWrite(LED, HIGH);
}
}
else if (digitalRead(BUTTON) == 1)
{
delay(100);
if(digitalRead(BUTTON) == 1)
{
digitalWrite(LED, LOW);
}
}
}
工程
验证结果
实际拍的gif图过大,上传限制了,跟下面延时差不多,就不放了。
中断方式
代码
中断加延时的方式不规范,仅测试验证使用。
#define LED 16
#define BUTTON 0
void ARDUINO_ISR_ATTR isr() {
if (digitalRead(BUTTON) == 0)
{
delay(100);
if(digitalRead(BUTTON) == 0)
{
digitalWrite(LED, HIGH);
}
}
else if (digitalRead(BUTTON) == 1)
{
delay(100);
if(digitalRead(BUTTON) == 1)
{
digitalWrite(LED, LOW);
}
}
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(BUTTON, INPUT);
attachInterrupt(BUTTON, isr, CHANGE);
}
void loop() {
}
工程
验证结果
结果跟GPIO 做输入章节一样。
GPIO 做输出
在上一个章节中,按键是 GPIO 作输入的例子,对 LED 灯的控制就是 GPIO 作输出的例子。此处不再赘述。
Arduino 库函数和 ESP32 库函数的对应
列举一下 arduino 和 esp32 库函数的对应关系。
在使用Arduino
的 ESP32 库的时候,会看到我们调用的都是Arduino
自己平台的库函数,比如改变 GPIO 电平的函数digitalWrite
,而且示例代码与上面 GPIO 控制的完全一致。
但是 ESP32 的库函数,用于控制 GPIO 输出电平的函数不叫digitalWrite
,而是gpio_set_level
。
那为什么使用Arduino
的库函数也能控制 ESP32 的硬件呢?我们在Arduino
的库函调用中寻找答案。从ESP32-hal-gpio.c
中就能找见digitalWrite
和gpio_set_level
的调用关系了。同理可以找见pinMod
、digitalRead
、attachInterrupt
、detachInterrupt
等,此处不再一一列举。
不知道ESP32-hal-gpio.c
文件名中的 HAL 是不是硬件抽象层Hardware Abstraction Layer
的意思。看文件开头的描述,这个文件是 ESP32 官方维护的。而ESP32-hal-gpio.h
是Arduino
官方维护的。是Arduino
官方负责给出针对 GPIO 外设的统一控制接口,而不同的开发板厂商负责具体实现?在此不做深入研究,后面碰到了再看。