3、ESP32基础外设驱动:GPIO、ADC、I2C、SPI的初始化与使用,点亮第一盏LED。
好,咱们正式开始动手了。
前面聊了那么多架构和协议,我知道你肯定手痒了。今天这一章,咱们就真刀真枪地干——把ESP32的四个最基础的外设驱动讲透:GPIO、ADC、I2C、SPI。最后,我会带你点亮第一盏LED。
别小看这个LED。我做了这么多年嵌入式,每次拿到一块新板子,第一件事就是点灯。灯亮了,说明你的开发环境、编译链、下载工具、芯片最小系统,全通了。这是硬件工程师的“Hello World”。
3.1 GPIO:最基础,也最容易翻车
GPIO,通用输入输出口。说白了,就是芯片的“手脚”。你可以让它输出高电平或低电平,也可以读取外部信号是0还是1。
初始化流程
在ESP-IDF里,GPIO的初始化分三步:
- 配置引脚功能(输入/输出/上拉/下拉)
- 设置中断模式(如果需要)
- 使能并开始使用
代码长这样:
#include "driver/gpio.h"
#define LED_GPIO GPIO_NUM_2
void app_main(void)
{
// 1. 配置GPIO为推挽输出
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << LED_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);
// 2. 循环闪烁
while (1) {
gpio_set_level(LED_GPIO, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
gpio_set_level(LED_GPIO, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
#define LED_GPIO GPIO_NUM_2。别问为什么,问就是吃过亏——项目大了,引脚号满天飞,改起来想哭。
避坑指南
- ESP32的GPIO2默认是板载LED,但有些模组把它用作其他功能。拿到板子先看原理图。
- GPIO6-11是Flash引脚,别乱动。我曾经手贱配置了GPIO6,结果程序直接跑飞。
- 输出模式记得关掉上下拉,否则电平可能不对。
3.2 ADC:把模拟世界翻译给数字芯片听
ADC,模数转换器。传感器输出的电压是连续的模拟量,芯片只认0和1。ADC就是那个翻译官。
ESP32有两个ADC模块,每个模块支持多个通道。我一般用ADC1,因为它更稳定。
初始化与读取
#include "driver/adc.h"
#include "esp_adc_cal.h"
#define ADC_CHANNEL ADC1_CHANNEL_0 // GPIO36
void app_main(void)
{
// 1. 配置ADC
adc1_config_width(ADC_WIDTH_BIT_12); // 12位精度,0-4095
adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_DB_11); // 0-3.6V范围
// 2. 校准(推荐)
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars);
while (1) {
uint32_t raw = adc1_get_raw(ADC_CHANNEL);
uint32_t voltage = esp_adc_cal_raw_to_voltage(raw, &adc_chars);
printf("Raw: %d, Voltage: %d mV\n", raw, voltage);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
另外,ESP32的ADC线性度其实一般。如果你需要高精度测量,建议用外部ADC芯片,比如ADS1115。这个咱们后面I2C章节会用到。
3.3 I2C:两根线串起一箩筐传感器
I2C总线,两根线:SCL(时钟)和SDA(数据)。一根线上可以挂多个设备,每个设备有唯一地址。
我个人特别喜欢I2C,因为它接线少、调试方便。在物联网项目里,温湿度传感器、气压计、OLED屏幕,基本都是I2C接口。
初始化与读写
#include "driver/i2c.h"
#define I2C_MASTER_SCL_IO 22
#define I2C_MASTER_SDA_IO 21
#define I2C_MASTER_FREQ_HZ 100000
void i2c_master_init(void)
{
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_param_config(I2C_NUM_0, &conf);
i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
}
// 写一个字节到从机
esp_err_t i2c_write_byte(uint8_t dev_addr, uint8_t reg, uint8_t data)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg, true);
i2c_master_write_byte(cmd, data, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
3.4 SPI:要速度?用它就对了
SPI,全双工、高速、四线制(MOSI、MISO、SCLK、CS)。如果你要驱动LCD屏幕、SD卡、高速ADC,SPI是首选。
ESP32的SPI控制器很强大,支持DMA传输。但咱们先从基础讲起。
初始化与发送
#include "driver/spi_master.h"
#define SPI_HOST SPI2_HOST
#define PIN_MOSI 23
#define PIN_MISO 19
#define PIN_SCLK 18
#define PIN_CS 5
void spi_init(void)
{
spi_bus_config_t buscfg = {
.mosi_io_num = PIN_MOSI,
.miso_io_num = PIN_MISO,
.sclk_io_num = PIN_SCLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096,
};
spi_bus_initialize(SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 10 * 1000 * 1000, // 10MHz
.mode = 0,
.spics_io_num = PIN_CS,
.queue_size = 7,
};
spi_device_handle_t spi;
spi_bus_add_device(SPI_HOST, &devcfg, &spi);
}
void spi_write_byte(spi_device_handle_t spi, uint8_t data)
{
spi_transaction_t t = {
.length = 8,
.tx_buffer = &data,
};
spi_device_transmit(spi, &t);
}
3.5 点亮第一盏LED:把知识串起来
好了,理论讲完了。咱们来点实际的。
我选GPIO2作为LED控制引脚。为什么?因为大多数ESP32开发板,GPIO2都连着一个LED。你不需要额外接线。
完整代码
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#define LED_PIN GPIO_NUM_2
void app_main(void)
{
// 配置GPIO
gpio_reset_pin(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
printf("ESP32 LED Blink Started!\n");
while (1) {
gpio_set_level(LED_PIN, 1); // 亮
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(LED_PIN, 0); // 灭
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
编译、下载、上电。如果LED以0.5秒间隔闪烁,恭喜你——你的ESP32活了。
3.6 本章小结
咱们今天干了四件事:
- GPIO:学会了推挽输出和输入读取,点亮了LED。
- ADC:知道了怎么把模拟电压转成数字值,也记住了别超3.3V。
- I2C:两根线搞定多设备通信,速率别贪快。
- SPI:高速全双工,适合屏幕和SD卡,时钟模式要对。
这些外设,是物联网节点和传感器交互的基础。下一章,咱们会把这些外设真正用起来——接一个温湿度传感器,把数据通过MQTT发到云端。
嗯,到时候你就能远程看到家里的温度了。想想是不是有点小激动?