韦东山《ARM 架构与编程》3-2 GPIO 引脚操作方法概述 · 学习笔记
所属课程 :ARM 架构与编程 · 基于 STM32F103
所在章节 :第三章 第一个程序(点亮 LED)· 第 2 节
原视频 :https://video.100ask.net/p/t_pc/course_pc_detail/video/v_5f858f7de4b0e95a89c3d4c6
整理日期:2026-09-28
目录
- 摘要
- 一、本节在课程中的位置
- [1.1 承上启下](#1.1 承上启下)
- [1.2 本节的定位:通用方法论,而非芯片手册导读](#1.2 本节的定位:通用方法论,而非芯片手册导读)
- [二、GPIO 的概念](#二、GPIO 的概念)
- [三、GPIO 的使用:四要素初始化框架](#三、GPIO 的使用:四要素初始化框架)
- [3.1 要素一:使能(电源 / 时钟)](#3.1 要素一:使能(电源 / 时钟))
- [3.2 要素二:模式(Mode / 功能复用)](#3.2 要素二:模式(Mode / 功能复用))
- [3.3 要素三:方向(Direction)](#3.3 要素三:方向(Direction))
- [3.4 要素四:数值(Value / 电平)](#3.4 要素四:数值(Value / 电平))
- [四、GPIO 寄存器的读写方法](#四、GPIO 寄存器的读写方法)
- [4.1 方法一:读-改-写(read-modify-write)](#4.1 方法一:读-改-写(read-modify-write))
- [4.2 方法二:set-reg / clr-reg(置位 / 清零寄存器)](#4.2 方法二:set-reg / clr-reg(置位 / 清零寄存器))
- [4.3 两种方法的对比](#4.3 两种方法的对比)
- [五、GPIO 的其他功能](#五、GPIO 的其他功能)
- 六、本节结论与延伸
- [附录 A:术语速查](#附录 A:术语速查)
- [附录 B:读完本节后的自查清单](#附录 B:读完本节后的自查清单)
摘要
本节承接 3-1 的点灯命题,抽象出 GPIO 通用操作框架:先使能时钟,再由模式寄存器选定 GPIO 功能,继而设方向为输入或输出,最后读写数据寄存器输出或采集电平。读写遵循"不影响其他位"原则,有读-改-写与 set/clr 寄存器两种实现,为后续芯片级编程建立统一范式。
一、本节在课程中的位置
1.1 承上启下
第三章的"点灯链路"中,本节处于从硬件到软件的抽象层:
text
3-1 硬件知识_LED 原理图 ← 已完成:知道引脚是哪根、什么电平点亮
↓
3-2 GPIO 引脚操作方法概述 ← 本节:不谈具体芯片,回答"通用地看,
↓ 让引脚输出/输入电平要走哪几步"
3-3 STM32F103 的 GPIO 操作方法
↓
3-4 STM32F103 的 LED 编程
- 承上:3-1 的结论是"引脚输出高/低电平即可控制 LED",但"怎么让引脚输出电平"悬而未决------这正是本节的命题。
- 启下:3-3 将把本节的通用框架落到 STM32F103 的具体寄存器(CRL/CRH、ODR、BSRR 等)上,3-4 再翻译成可编译的程序。
1.2 本节的定位:通用方法论,而非芯片手册导读
课程刻意把这一节做成芯片无关的概述:无论 STM32F103、I.MX6ULL 还是 STM32MP157,也无论是 51、AVR 还是其他 MCU,GPIO 的操作要素都相同。先建立这个"通用模板",再去看任何一款芯片的手册,都只是往模板里填寄存器名字------这是本节最重要的学习收益。
二、GPIO 的概念
GPIO :General-purpose input/output,通用输入输出口。
它是芯片与外部世界交互的最基础通道:
| 方向 | 物理行为 | 典型应用 |
|---|---|---|
| 输出 | 引脚上出现高电平 / 低电平 | 驱动 LED、蜂鸣器、继电器 |
| 输入 | 读取引脚当前电平 | 按键检测、传感器信号采集 |
"通用"二字的含义:这些引脚不固定用途,由软件配置决定其方向与功能,既可当输出口也可当输入口。
芯片上的 GPIO 通常分组编排 :有多组 GPIO(如 GPIOA、GPIOB、GPIOC...),每组含多个引脚(典型为 16 个,Pin0~Pin15)。3-1 中从原理图读出的 PC13,指的就是"C 组第 13 号引脚"------引脚编号体系与本节的分组概念直接对应。
三、GPIO 的使用:四要素初始化框架
本节的核心内容是把 GPIO 的初始化与使用归纳为四个要素,顺序不可颠倒:
text
① 使能(电源/时钟)
↓
② 模式(Mode:GPIO 还是其他功能)
↓
③ 方向(Direction:输出还是输入)
↓
④ 数值(Value:写电平 / 读电平)
3.1 要素一:使能(电源 / 时钟)
- 大多数芯片为降低功耗,默认关闭外设的电源或时钟;GPIO 模块不使能,其寄存器写不进去、读出来也无意义。
- 对应地,芯片手册一般有专门的 power/clock 章节:向指定寄存器的指定位写 1,使能某个 GPIO 模块(Module)。
- 并非所有芯片都有这一步------有些芯片的 GPIO 没有使能开关,永远处于使能状态(典型如不少传统 51 单片机)。是否存在这一步,查手册即知。
工程教训:在 STM32 等芯片上"程序写了对的寄存器但引脚毫无反应",最常见的原因就是忘了先开 GPIO 时钟。3-1 的误区清单埋下的伏笔,在本节得到解释。
3.2 要素二:模式(Mode / 功能复用)
现代 MCU 的引脚大多是复用引脚:同一个物理引脚,既可以当普通 GPIO,也可以接给串口、USB、I²C、定时器等片上外设使用。
- 因此在把引脚当 GPIO 用之前,必须先通过功能选择寄存器(IOMUX / 复用选择位)明确声明:"这个引脚用于 GPIO 功能"。
- 跳过这一步,引脚可能仍连接在其他外设上,后续的"方向""数值"设置全部无效。
3.3 要素三:方向(Direction)
引脚被设置为 GPIO 功能之后,还要指定它是输出 还是输入:
- 输出:由芯片驱动引脚电平;
- 输入:由外部器件驱动引脚电平,芯片只负责读取。
方向由方向寄存器(或输出使能寄存器)中的对应位决定。这一步"方向先行"的逻辑很重要:方向都没定,谈输出或输入电平就是无本之木。
3.4 要素四:数值(Value / 电平)
方向确定后,操作数据寄存器即可:
| 引脚方向 | 操作 | 效果 |
|---|---|---|
| 输出 | 向数据寄存器写 1 / 0 | 引脚输出高电平 / 低电平 |
| 输入 | 读数据寄存器对应位 | 得到引脚当前电平(1 = 高,0 = 低) |
至此,"让 PC13 输出低电平点亮 LED"这句话被完整翻译成四条寄存器级动作:开时钟 → 选 GPIO 功能 → 设为输出 → 数据寄存器写 0。
四、GPIO 寄存器的读写方法
四要素落到代码上就是"读写寄存器"。但 GPIO 寄存器是一组引脚共用一个 32 位寄存器------每一位属于不同的引脚,这就引出一条铁律:
原则:操作某个引脚对应的位时,不能影响到其他位。
课程给出两种满足该原则的读写方法:
4.1 方法一:读-改-写(read-modify-write)
适用于只有数据寄存器(data_reg)的芯片:
c
val = data_reg; // 1. 读:取出整个寄存器的当前值
val = val | (1 << n); // 2. 改:用位或把第 n 位置 1(清 0 则用 val & ~(1 << n))
data_reg = val; // 3. 写:整体写回
三步缺一不可:直接写 data_reg = (1 << n) 会把其他 31 位全部清零,殃及同组其他引脚。
位操作速记:
| 操作 | 表达式 | 用途 |
|---|---|---|
| 置 1 | `reg | = (1 << n)` |
| 清 0 | reg &= ~(1 << n) |
关闭第 n 位 |
| 检测 | reg & (1 << n) |
读出第 n 位 |
4.2 方法二:set-reg / clr-reg(置位 / 清零寄存器)
部分芯片除数据寄存器外,还提供一对专用寄存器:
- set_reg(置位寄存器):向第 n 位写 1 → 该位被置 1;
- clr_reg(清零寄存器):向第 n 位写 1 → 该位被清 0;
- 写 0 的位不产生任何效果------这正是"不影响其他位"原则的硬件级实现。
c
set_reg = (1 << n); // 只把第 n 位置 1,一步完成
clr_reg = (1 << n); // 只把第 n 位清 0,一步完成
4.3 两种方法的对比
| 对比项 | 读-改-写 | set/clr 寄存器 |
|---|---|---|
| 硬件要求 | 仅需一个数据寄存器 | 需要额外的置位/清零寄存器 |
| 操作步数 | 三步 | 一步 |
| 并发风险 | 读、改、写之间若被中断打断并修改了同一寄存器,结果可能互相覆盖 | 单次写操作,天然原子,无竞争风险 |
| 代码形态 | 位数组组合 | 直观、简洁 |
读-改-写的竞争问题在中断与多任务环境下尤为致命------这也解释了为什么后来的芯片(包括 STM32 的 BSRR 寄存器)普遍采用 set/clr 方案。本节作为概述,正是要提前建立这一判断力:拿到一款芯片,先看它有没有 set/clr 寄存器,再决定用哪种写法。
五、GPIO 的其他功能
除"输出/输入电平"这一基本职能外,GPIO 模块通常还提供若干扩展功能,课程点到即止:
- 防抖动(去抖):输入信号抖动时的硬件滤波能力,多用于按键类应用;
- 中断:引脚电平变化触发中断,避免软件轮询;
- 唤醒:低功耗休眠模式下,由指定引脚的电平变化唤醒芯片;
- 输出速率(驱动速度):可配置引脚的翻转速率。
其中"输出速率"一条暗含重要的工程权衡:
GPIO 配置的输出速率越高,信号边沿越陡,对外产生的电磁干扰(EMI)越大。
因此速率并非越高越好------驱动 LED 这类低速负载,选择较低的输出速率反而更稳妥。这些功能将在后续章节(按键、中断)中逐一展开。
六、本节结论与延伸
本节的知识可以浓缩为一张"通用 GPIO 操作模板":
text
┌─ 1. 使能:power / clock 寄存器(部分芯片无此步)
├─ 2. 模式:功能选择寄存器 → 选定 GPIO 功能
├─ 3. 方向:方向寄存器 → 输出 / 输入
├─ 4. 数值:数据寄存器 → 写电平(输出)/ 读电平(输入)
└─ 读写纪律:不影响其他位 → 读-改-写 或 set/clr 寄存器
两点延伸:
- 这套四要素框架是跨芯片复用的:下一节 3-3 只需把 STM32F103 手册里的寄存器名字(GPIOx_CRL/CRH、GPIOx_ODR、GPIOx_BSRR、RCC_APB2ENR)填进模板的四个空位。
- "不影响其他位"的原则与 set/clr 寄存器的取舍,是裸机编程与后续 RTOS/Linux 驱动中反复出现的主题------凡是共享资源,都存在同样的"局部修改不得殃及整体"问题。
附录 A:术语速查
| 术语 | 含义 |
|---|---|
| GPIO | General-purpose input/output,通用输入输出口 |
| 时钟使能(Clock Enable) | 打开外设工作时钟的寄存器操作,多数芯片 GPIO 使用的前置步骤 |
| 复用(Alternate Function) | 同一物理引脚可连接不同片上外设,需由功能选择寄存器指定 |
| 方向寄存器 | 决定引脚为输出或输入的寄存器 |
| 数据寄存器(data_reg) | 写入 / 读出引脚电平的寄存器 |
| set_reg / clr_reg | 置位 / 清零寄存器,写 1 生效、写 0 无效 |
| 读-改-写(read-modify-write) | "读出 → 位操作 → 写回"的三步寄存器局部修改法 |
| 输出速率 | 引脚电平翻转速度,速率越高 EMI 越大 |
附录 B:读完本节后的自查清单
- 能背出 GPIO 使用的四要素及它们的先后顺序。
- 能解释"为什么操作一个引脚的位不能影响其他位"。
- 能写出置 1、清 0、检测某一位的三条位操作表达式。
- 能说明读-改-写与 set/clr 寄存器两种方法的差异及各自适用场景。
- 能解释为什么输出速率不是越高越好。
- 拿到一款没学过的芯片手册,知道按哪四个关键词去检索 GPIO 章节(power/clock、功能选择、方向、数据)。
本文档依据课程公开目录、原视频页信息及公开学习笔记整理,配合原视频 3-2 使用。