Skip to content

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 上根本就没有。

先看总表,再看几个容易踩坑的:

外设芯片上有你能用说明
GPIO45 个约 30 个有一批接了 Flash,见 2.2 的禁忌表
UART3 个2 ~ 3 个UART0 被日志占着,见下
I2C2 个2 个都能用
SPI4 个⭐ 只有 2 个SPI0/SPI1 是芯片自己用的,见下
I2S2 个2 个接麦克风、功放(2.6 用)
ADC2 组(共 20 通道)⭐ 只有 1 组ADC2 和 WiFi 冲突,见下
DAC❌ 没有—S3 砍掉了,老款 ESP32 有 2 路
LEDC(PWM)4 个定时器 / 8 个通道8 路调光、发声(2.5 用)
通用定时器4 个4 个定时触发中断
MCPWM2 组2 组电机专用,带死区和刹车保护
RMT8 个通道(4 发 4 收)8 个红外遥控、驱动 WS2812 灯带
SDMMC2 个2 个读 SD 卡(3.9 用)
TWAI(CAN)1 个1 个汽车/工业总线
触摸传感器14 通道14 个触摸按键
温度传感器1 个1 个测的是芯片自己的温度,不是环境温度
USB2 个控制器同时只能用 1 个都走 GPIO19/20
WiFi1 个✅只有 2.4GHz,连不上 5G 频段
蓝牙1 个✅⭐ 只有 BLE 5.0,没有经典蓝牙
以太网 MAC❌ 没有—老款 ESP32 有,S3 砍掉了

下面四条是最容易踩的。

SPI:有 4 个,但只能用 2 个 ⭐ ​

名字能用吗干什么的
SPI0—❌ 不可用芯片内部专用,CPU 通过它读取片上 Flash 和 PSRAM
SPI1—❌ 不可用和 SPI0 共用一条总线,给 Flash 驱动用(擦写固件)
SPI2FSPI✅ 可用最常用的一个,支持 4 线/8 线高速模式
SPI3HSPI✅ 可用只支持 4 线

为什么 SPI0/SPI1 碰不得:你的程序本身就存在 Flash 里,CPU 每执行一条没缓存的指令,都要通过 SPI0 去 Flash 里取。把它改成别的用途,等于把自己脚下的地板拆了 —— 芯片会立刻崩溃或者根本起不来。

SPI2 有"专用引脚",SPI3 没有

SPI2(FSPI)有一组直连引脚,走这几个脚速度能跑到最高:

信号专用引脚
CLKGPIO12
MOSIGPIO11
MISOGPIO13
CSGPIO10

不走这几个脚也能用,只是信号要绕一圈(见下面的"GPIO 矩阵"),高速时稍微差一点。SPI3 没有专用引脚,只能绕。

UART:有 3 个,但 UART0 最好别动 ​

默认引脚建议
UART0TX=GPIO43,RX=GPIO44⚠️ 留给日志和下载,占了就看不到 printf 了
UART1TX=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,而不是非要用某几个固定脚。

只有两种情况例外:

  1. 走"专用引脚"速度更快(比如 SPI2 那四个脚),高速传输时有区别
  2. 有些引脚天生不能动(接 Flash 的那几个),矩阵再灵活也救不了

而 ADC、触摸这类模拟信号走不了矩阵 —— 它们必须接在物理上连着那个模块的引脚上,所以 ADC1 只能是 GPIO1–10。

四、代码是怎么控制硬件的 ​

寄存器(Register)是外设内部的一组特殊存储单元,每一位(bit)控制外设的一项行为。CPU 通过往指定的内存地址读写数据来操作它们。

好消息是:这些你都不用自己写。 ESP-IDF 已经封装好了:

c
// 你只需要写这一行
gpio_set_level(2, 1);      // 让 2 号引脚输出高电平

至于它内部往哪个地址写了什么数字,完全不用管。

五、怎么看数据手册 ​

拿到一个新模块(比如温湿度传感器),别从头看到尾,只看这四样:

  1. 工作电压 —— 3.3V 还是 5V?这是第一个要确认的
  2. 引脚定义 —— 哪个脚是电源、哪个是数据
  3. 接口类型 —— 是 I2C 还是 SPI
  4. 示例电路 —— 照着抄最省事

电压不匹配会烧芯片

ESP32-S3 的引脚只能承受 3.3V。

买模块时看清楚,标着"3.3V/5V 兼容"的最省心。拿不准就问卖家。

六、什么是中断 ​

中断(Interrupt)是一种硬件通知机制:当外设发生特定事件时,会打断 CPU 当前的执行流程,转去执行一段专门的处理程序(中断服务函数),处理完再回到原处继续。

为什么需要它?看两种做法:

轮询中断
做法CPU 每隔一会儿去问"按了吗?"按键自己举手说"我按了!"
CPU 占用高,一直在问低,没事不打扰
响应速度取决于问的频率立即

打个比方:轮询是你每分钟去门口看一次快递到没到;中断是快递员到了直接按门铃。

中断服务函数的三条规矩

  1. 越短越好 —— 几行代码就该出来
  2. 不能睡 —— 不能用 vTaskDelay
  3. 只能用带 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,中断里只报信不干活

动手试试 ​

  1. 查一下你手上开发板的资料,列出它有哪些片上外设。
  2. 数一数你的项目要用掉几个 SPI、几个 UART、几路 ADC,看看够不够分。
  3. 看看你买的传感器模块,确认它的工作电压和接口类型。
  4. 用自己的话说清楚轮询和中断的区别。