外观
2.1 外设基础:片上 / 外部外设
这节要搞懂:什么是外设,代码怎么控制硬件。 需要基础:第一章
一、什么是外设
外设(Peripheral)指的是 CPU 之外、用于完成特定功能的硬件模块。它们负责与外部世界交互 —— 输入输出信号、通信、模数转换等,由 CPU 通过读写寄存器来控制。
打个比方:如果 CPU 是大脑,外设就是手和脚。
大脑本身不能拿东西、不能走路,它只负责发指令:
| 你想干什么 | 用哪个外设 |
|---|---|
| 让引脚输出高低电平(点灯) | GPIO |
| 跟另一块芯片说话 | UART / I2C / SPI |
| 测一个电压 | ADC |
| 精确计时、发出方波 | 定时器 / PWM |
二、片上外设 vs 外部外设
| 片上外设 | 外部外设 | |
|---|---|---|
| 在哪 | 就在芯片内部 | 是另一块独立的芯片 |
| 怎么用 | 直接调 API | 先用总线连上,再按协议对话 |
| 快不快 | 快 | 受总线速度限制 |
| 例子 | GPIO、定时器、ADC、UART | 温湿度传感器、屏幕、SD 卡 |
ESP32-S3 自带的片上外设很多:GPIO、3 个串口、2 个 I2C、4 个 SPI、ADC、定时器,还有 WiFi 和蓝牙。但"有"不等于"你能用" —— 下一节把这笔账算清楚。
三、ESP32-S3 上每种外设有几个 ⭐
"有几个"和"能用几个"往往不是一回事 —— 有些被芯片自己占着,有些和 WiFi 冲突,还有些在 S3 上根本就没有。
先看总表,再看几个容易踩坑的:
| 外设 | 芯片上有 | 你能用 | 说明 |
|---|---|---|---|
| GPIO | 45 个 | 约 30 个 | 有一批接了 Flash,见 2.2 的禁忌表 |
| UART | 3 个 | 2 ~ 3 个 | UART0 被日志占着,见下 |
| I2C | 2 个 | 2 个 | 都能用 |
| SPI | 4 个 | ⭐ 只有 2 个 | SPI0/SPI1 是芯片自己用的,见下 |
| I2S | 2 个 | 2 个 | 接麦克风、功放(2.6 用) |
| ADC | 2 组(共 20 通道) | ⭐ 只有 1 组 | ADC2 和 WiFi 冲突,见下 |
| DAC | ❌ 没有 | — | S3 砍掉了,老款 ESP32 有 2 路 |
| LEDC(PWM) | 4 个定时器 / 8 个通道 | 8 路 | 调光、发声(2.5 用) |
| 通用定时器 | 4 个 | 4 个 | 定时触发中断 |
| MCPWM | 2 组 | 2 组 | 电机专用,带死区和刹车保护 |
| RMT | 8 个通道(4 发 4 收) | 8 个 | 红外遥控、驱动 WS2812 灯带 |
| SDMMC | 2 个 | 2 个 | 读 SD 卡(3.9 用) |
| TWAI(CAN) | 1 个 | 1 个 | 汽车/工业总线 |
| 触摸传感器 | 14 通道 | 14 个 | 触摸按键 |
| 温度传感器 | 1 个 | 1 个 | 测的是芯片自己的温度,不是环境温度 |
| USB | 2 个控制器 | 同时只能用 1 个 | 都走 GPIO19/20 |
| WiFi | 1 个 | ✅ | 只有 2.4GHz,连不上 5G 频段 |
| 蓝牙 | 1 个 | ✅ | ⭐ 只有 BLE 5.0,没有经典蓝牙 |
| 以太网 MAC | ❌ 没有 | — | 老款 ESP32 有,S3 砍掉了 |
下面四条是最容易踩的。
SPI:有 4 个,但只能用 2 个 ⭐
| 名字 | 能用吗 | 干什么的 | |
|---|---|---|---|
| SPI0 | — | ❌ 不可用 | 芯片内部专用,CPU 通过它读取片上 Flash 和 PSRAM |
| SPI1 | — | ❌ 不可用 | 和 SPI0 共用一条总线,给 Flash 驱动用(擦写固件) |
| SPI2 | FSPI | ✅ 可用 | 最常用的一个,支持 4 线/8 线高速模式 |
| SPI3 | HSPI | ✅ 可用 | 只支持 4 线 |
为什么 SPI0/SPI1 碰不得:你的程序本身就存在 Flash 里,CPU 每执行一条没缓存的指令,都要通过 SPI0 去 Flash 里取。把它改成别的用途,等于把自己脚下的地板拆了 —— 芯片会立刻崩溃或者根本起不来。
SPI2 有"专用引脚",SPI3 没有
SPI2(FSPI)有一组直连引脚,走这几个脚速度能跑到最高:
| 信号 | 专用引脚 |
|---|---|
| CLK | GPIO12 |
| MOSI | GPIO11 |
| MISO | GPIO13 |
| CS | GPIO10 |
不走这几个脚也能用,只是信号要绕一圈(见下面的"GPIO 矩阵"),高速时稍微差一点。SPI3 没有专用引脚,只能绕。
UART:有 3 个,但 UART0 最好别动
| 默认引脚 | 建议 | |
|---|---|---|
| UART0 | TX=GPIO43,RX=GPIO44 | ⚠️ 留给日志和下载,占了就看不到 printf 了 |
| UART1 | TX=GPIO17,RX=GPIO18 | ✅ 接外部模块用它 |
| UART2 | ⭐ 没有默认引脚 | ✅ 能用,但必须在代码里手动指定 |
UART2 为什么没有默认引脚:S3 的引脚资源紧张,芯片设计时干脆没给它分配固定的直连脚。所以用 UART2 时,uart_set_pin() 里不能填 UART_PIN_NO_CHANGE,必须明确写出用哪两个 GPIO,否则根本发不出东西。
c
// UART1:不指定就用默认的 17/18
uart_set_pin(UART_NUM_1, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
// UART2:⭐ 必须自己写清楚
uart_set_pin(UART_NUM_2, 4, 5, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);ADC:有 2 组,但一联网就只剩 1 组
| 对应引脚 | 能用吗 | |
|---|---|---|
| ADC1(CH0–CH9) | GPIO1 – 10 | ✅ 联网时也能用 |
| ADC2(CH0–CH9) | GPIO11 – 20 | ❌ 开了 WiFi 就失效 |
为什么:ADC2 和 WiFi 射频电路共用同一套硬件资源。WiFi 一开,这套资源归射频管,ADC2 读出来就是错的(函数直接返回失败)。
这个坑特别隐蔽 —— 单独测试好好的,一加上联网代码就读不出来了。所以所有模拟量都接 ADC1。细节见 2.5。
这几样 S3 上根本没有
| 老款 ESP32 有 | S3 上 | 想用怎么办 |
|---|---|---|
| DAC(真模拟输出) | ❌ 没有 | 用 PWM + 一个 RC 滤波电路凑合 |
| 经典蓝牙(BR/EDR) | ❌ 只有 BLE | 要连蓝牙音箱、蓝牙串口模块,S3 干不了 |
| 以太网 MAC | ❌ 没有 | 外挂 SPI 以太网芯片(W5500 之类) |
蓝牙这条最容易踩
很多人买 S3 是为了做"蓝牙音箱"或者"蓝牙串口透传",这两个用的都是经典蓝牙,S3 做不了。
S3 的蓝牙只有 BLE,适合传少量数据(配置、控制指令、传感器读数)—— 第三章的天气时钟就是用 BLE 下发配置的。
为什么"默认引脚"其实不太重要
S3 内部有个叫 GPIO 矩阵的东西,可以把几乎任何外设的信号,接到几乎任何一个 GPIO 上。
所以 I2C、UART、SPI 这些的引脚,基本都是你想接哪就接哪,代码里填一下就行 —— 这也是为什么本课程的 I2C 能接 GPIO13/14,而不是非要用某几个固定脚。
只有两种情况例外:
- 走"专用引脚"速度更快(比如 SPI2 那四个脚),高速传输时有区别
- 有些引脚天生不能动(接 Flash 的那几个),矩阵再灵活也救不了
而 ADC、触摸这类模拟信号走不了矩阵 —— 它们必须接在物理上连着那个模块的引脚上,所以 ADC1 只能是 GPIO1–10。
四、代码是怎么控制硬件的
寄存器(Register)是外设内部的一组特殊存储单元,每一位(bit)控制外设的一项行为。CPU 通过往指定的内存地址读写数据来操作它们。
好消息是:这些你都不用自己写。 ESP-IDF 已经封装好了:
c
// 你只需要写这一行
gpio_set_level(2, 1); // 让 2 号引脚输出高电平至于它内部往哪个地址写了什么数字,完全不用管。
五、怎么看数据手册
拿到一个新模块(比如温湿度传感器),别从头看到尾,只看这四样:
- 工作电压 —— 3.3V 还是 5V?这是第一个要确认的
- 引脚定义 —— 哪个脚是电源、哪个是数据
- 接口类型 —— 是 I2C 还是 SPI
- 示例电路 —— 照着抄最省事
电压不匹配会烧芯片
ESP32-S3 的引脚只能承受 3.3V。
买模块时看清楚,标着"3.3V/5V 兼容"的最省心。拿不准就问卖家。
六、什么是中断
中断(Interrupt)是一种硬件通知机制:当外设发生特定事件时,会打断 CPU 当前的执行流程,转去执行一段专门的处理程序(中断服务函数),处理完再回到原处继续。
为什么需要它?看两种做法:
| 轮询 | 中断 | |
|---|---|---|
| 做法 | CPU 每隔一会儿去问"按了吗?" | 按键自己举手说"我按了!" |
| CPU 占用 | 高,一直在问 | 低,没事不打扰 |
| 响应速度 | 取决于问的频率 | 立即 |
打个比方:轮询是你每分钟去门口看一次快递到没到;中断是快递员到了直接按门铃。
中断服务函数的三条规矩
- 越短越好 —— 几行代码就该出来
- 不能睡 —— 不能用
vTaskDelay - 只能用带
FromISR的函数
记法:中断里只报信,不干活。 干活交给任务(第一章 1.7 的模式)。
本节要点
- 外设 = CPU 之外负责具体功能的硬件模块,CPU 指挥,外设干活
- 片上外设直接调 API,外部外设要用总线连
- ⭐ "有几个"不等于"能用几个":SPI 有 4 个只能用 2 个(SPI0/1 给 Flash),ADC 有 2 组联网后只剩 1 组
- ⭐ UART0 留给日志别动;UART2 没有默认引脚,必须手动指定
- ⭐ S3 没有 DAC、没有经典蓝牙、没有以太网 MAC,蓝牙只有 BLE
- GPIO 矩阵让数字外设几乎能接任意引脚;模拟信号(ADC/触摸)不行
- 寄存器是外设的开关,但我们只用封装好的 API
- 看手册先看电压
- 中断 = 硬件主动通知 CPU,中断里只报信不干活
动手试试
- 查一下你手上开发板的资料,列出它有哪些片上外设。
- 数一数你的项目要用掉几个 SPI、几个 UART、几路 ADC,看看够不够分。
- 看看你买的传感器模块,确认它的工作电压和接口类型。
- 用自己的话说清楚轮询和中断的区别。