完整教程:【STM32】通用输入输出端口GPIO
GPIO(General Purpose Input/Output,通用输入输出接口),是单片机中最基础的外设,它直接表现为芯片的一个个引脚裸露在芯片外,是单片机与外部设备进行双向数据交换的必由之路。本文便捷介绍了STM32I/O端口的8种工作模式、对应电路状态以及端口相关寄存器安装(基于STM32F1系列)。
STM32的I/O端口基础结构
上图是STM32的I/O端口简化结构图,许可简单分类为保护电路、上下拉配备电路、输入缓冲电路和输出缓冲电路。下面从IO口的8种工作模式入手,分别了解各个部分的关键功能。
STM32的8种工作模式
对于STM32,GPIO口常常不只有高低电平输入输出一种功能(绝大多数MCU端口都是如此),对于同一端口实现的不同功能,我们称之为端口复用。通过合理设计复用端口允许缓解引脚资源不足的问题,减小芯片封装大小,提高硬件资源利用率,满足复杂外设的连接需求。
关于怎么配置使用复用功能不是本文的重点内容(在对应外设介绍时详细说明),本文重点介绍GPIO口的一般性配备。由于一个端口通常可以达成不同复用功能,那么每个功能对应的硬件逻辑也应该不同,对此,STM32设计了8种工作模式,用以满足不同情况下IO口的使用条件,这8种模式包括:
- 浮空输入
- 上拉输入
- 下拉输入
- 模拟输入
- 开漏输出
- 推挽输出
- 复用推挽输出
- 复用开漏输出
浮空输入(Floating input)
浮空输入上下拉电阻均断开,信号由I/O引脚依据TTL肖特基触发器直接送达输入数据寄存器(Input Data Register,IDR),CPU通过读取该寄存器值得知对应输入的高低电平状态。其中,TTL肖特基触发器是一种基于肖特基二极管的特性把掺杂了少许高频噪声波动的输入信号修正为高质量数字信号并输入到输入资料寄存器中的电平触发电路,该电路减少了输入信号中微小噪声对信号的干扰。该输入模式下,若I/O端口无输入,该引脚处于悬空状态,此时读取的电平值是不稳定的。
上拉输入(Pull-up input)
上拉输入,顾名思义,其电路结构在浮空输入电路的I/O口输入端添加了上拉电阻,在I/O端口无输入的情况下,该引脚被上拉到V D D V_{DD}VDD,此时读取到的输入电平为高电平。
下拉输入(Pull-down input)
下拉输入的电路结构在浮空输入电路的I/O口输入端添加了下拉电阻,在I/O端口无输入的情况下,该引脚被下拉到V S S V_{SS}VSS,此时读取到的输入电平为低电平。
模拟输入(Analog input)
模拟输入模式下外部输入直接连接到片上外设而没有其他缓冲元件,模拟输入一般在启用片上ADC检测模拟信号等情况下部署(在部分带有DAC片上外设的型号中,使用DAC产生模拟信号时也把I/O端口配置为模拟输入模式)。
推挽输出(Push-Pull output)
STM32的4种输出模式下均可通过输入资料寄存器读取到引脚输出内容,即上图的虚线部分。
推挽输出又叫推拉输出,CPU通过向位设置/清除寄存器(Bit Set/Reset Register,BSRR)写入0或1表示输出的高低电平,写入的值会被映射到输出数据寄存器(Output Data Register,ODR)中,当然也可以向ODR对应位中直接写入需要输出的值。ODR对应端口位把0/1输出到输出控制电路,输出控制电路同步发出两个信号控制P-MOS和N-MOS的通断实现输出数据逻辑控制:当输出为0时,P-MOS断路,N-MOS短路,I/O端口被下拉到V S S V_{SS}VSS,输出逻辑0;当输出为1时,P-MOS短路,N-MOS断路,I/O端口被上拉到V D D V_{DD}VDD,输出逻辑1。如此在任何时刻只有一个MOS管导通,一开一关,故称之为推挽电路。
开漏输出(Open-Drain output)
开漏输出电路组成看上去和推挽输出一样,唯一不同的是其推挽电路部分P-MOS无效,始终是断路状态。这样,当输出为0时,N-MOS导通,输出端被下拉到V S S V_{SS}VSS,实现逻辑0;但是,当输出为1的时候,P-MOS和N-MOS均断开,输出引脚悬空,表现为高阻态,此时输出逻辑是不确定的。一般来说,开漏输出常常联合外接上拉电阻(输出模式下内部上下拉电阻无效)应用,这样在无输出或输出逻辑1时,实际输出都被上拉到V S S V_{SS}VSS。
开漏输出最常用的是多引脚直接相连的场景。对此,若配置为推挽输出,当一个引脚输出高电平,另一个输出低电平时,高低电平直接相连,会导致短路,烧坏芯片。而对于利用开漏输出,只要有一个引脚输出低电平,所有引脚都被下拉到低电平,即产生了“线与”的效果。但是值得注意的是:若每个引脚都外接了上拉电阻,相互连接后相当于这些上拉电阻并联,总阻值变小,有流过电流过大而烧坏电路的风险。
复用推挽输出(Alternate function Push-Pull output)
复用推挽输出的输出控制及以后电路与推挽输出完全一致,只不过输出控制不再由输出数据寄存器决定,而是通过设置好的引脚的其他复用功能外设控制。
复用开漏输出(Alternate function Open-Drain output)
输出信号源由ODR改变为端口其他复用特性。就是复用开漏输出也与开漏输出基本相同,只
GPIO端口工作模式的配置
通过如上图,STM32的每个GPIO端口都有两个32位的配置寄存器(GPIOx_CRL,GPIOx_CRH)、两个32位的资料寄存器(GPIOx_IDR,GPIOx_ODR)、32位的置位/复位寄存器(GPIOx_BSRR)、一个16位的复位寄存器(GPIOx_BRR)以及一个32位锁定寄存器(GPIOx_LCKR),这些寄存器都被映射到内存的外设区中。通过配置这些寄存器,变能够实现上述8种工作方式以及输入输出控制,下面将各寄存器具体功能详细列举。
配备寄存器(GPIOx_CRL,GPIOx_CRH)
每组GPIO都有其对应的端口低配置寄存器(CRL)和端口高配置寄存器(CRH),CRL和CRH在结构上完全一致,只不过CRL控制低8位端口(Pin0Pin7),而CRH控制高8位端口(Pin8Pin15)。每个寄存器32位,即每4位对应一个引脚,具体引脚对应控制位如下表
引脚 | 对应寄存器位 | 引脚 | 对应寄存器位 |
---|---|---|---|
Pin0 | [3:0] | Pin4 | [19:16] |
Pin1 | [7:4] | Pin5 | [23:20] |
Pin2 | [11:8] | Pin6 | [27:24] |
Pin3 | [15:12] | Pin7 | [31:28] |
每个引脚的4个bit配置分为MODE[1:0]
(高 2 位)和CNF[1:0]
(低 2 位),功能如下表:
MODE[1:0] | 功能描述 | 适用场景 |
---|---|---|
00 | 输入模式 | 所有输入配置(需配合 CNF) |
01 | 输出模式,速度最高 2MHz | 低速度输出 |
10 | 输出模式,速度最高 10MHz | 中速输出 |
11 | 输出模式,速度最高 50MHz | 高速输出 |
其中,MODE[1:0]
配置不同时,CNF[1:0]
的功能也不同。
当MODE[1:0] = 00
(输入模式)时:
CNF[1:0] | 功能描述 | 典型应用场景 |
---|---|---|
00 | 模拟输入模式 | ADC 输入、DAC 输出 |
01 | 浮空输入模式(默认状态) | 外部信号输入 |
10 | 上拉 / 下拉输入模式(具体上下拉配置见下文ODR介绍) | 需稳定电平的输入 |
11 | 保留 | - |
当MODE[1:0] ≠ 00
(输出模式)时:
CNF[1:0] 值 | 功能描述 | 典型应用场景 |
---|---|---|
00 | 通用推挽输出模式 | 直接驱动外设 |
01 | 通用开漏输出模式 | 需线与机制的场景 |
10 | 复用功能推挽输出模式 | 外设复用输出 |
11 | 复用功能开漏输出模式 | 外设复用 + 线与功能 |
数据寄存器
数据寄存器包括输入资料寄存器(IDR)和输出数据寄存器(ODR),每组GPIO端口分别对应一个输入数据寄存器(GPIOx_IDR)和一个输出资料寄存器(GPIOx_ODR)。
输入数据寄存器(IDR)
IDR为只读数据存储器,,用于读取实际输入电平状态(无论引脚配置为输入模式还是输出模式均可读取),其高16位为保留位,只有低16位有效,分别对应该组GPIO的16个引脚。可以通过掩码读取目标引脚状态(如:GPIOA->IDR & GPIO_IDR_IDR0
读取PA0状态)。
输出素材寄存器(ODR)
通过ODR是可读可写寄存器,能够控制GPIO输出状态。与IDR相同,其高16位为保留位,低16位分别对应该组GPIO的16个引脚。当GPIO设置为输出模式时,修改寄存器数值即可修改对应位的引脚输出。读取ODR允许返回当前ODR中保存的值(并非等同于实际引脚输出,例如在输入模式和复用功能输出模式时)。
在输入模式的上拉/下拉模式中,ODR还可用于上下拉配置,其具体用法为:ODR低16位分别对应16个I/O端口,对应位置0则端口下拉,对应位置1则端口上拉。
置位/复位寄存器(BSRR)
BSRR也是控制GPIO引脚输出的关键寄存器之一,不同于ODR,BSRR最大的优势是支持原子操作(详细内容参考存储器映射一文中位带处理部分)和操作过程不可被中断打断,采用该寄存器控制GPIO输出,避免了直接操作ODR需要进行的“读-改-写”过程以及在该操作过程中数据发生改变的问题。
BSRR为只写寄存器(可以强行读取其值,可是无意义,通常返回0),每组GPIO端口对应有一个32位BSRR,其高16位分别对应16个Pin的复位位,对对应位写1实现该Pin输出低电平;低16位分别对应16个Pin的置位位,对对应位写1实现该Pin输出高电平。对所有位写0无效,即:
位范围 | 名称 | 功能描述 |
---|---|---|
31:16 | BRy | 复位位(Bit Reset):- 位 n(n=16~31)对应引脚 Pin (n-16)(如位 16 对应 Pin0,位 17 对应 Pin1,…,位 31 对应 Pin15)- 写 “1” → 对应引脚输出低电平;写 “0” → 无操作 |
15:0 | BSy | 置位位(Bit Set):- 位 n(n=0~15)对应引脚 Pin n(如位 0 对应 Pin0,位 1 对应 Pin1,…,位 15 对应 Pin15)- 写 “1” → 对应引脚输出高电平;写 “0” → 无操作 |
因此,对于需要修改输出状态的引脚,只需通过掩码确定对应位的位置,直接写入即可(而无需关心其他引脚)。
复位寄存器(BRR)
BRR是一个16位只写寄存器,其特性与BSRR的高16位完全重合,其存在是为了历史兼容性,一般直接应用BSRR而不用BRR。
锁定寄存器(LCKR)
LCKR是用于锁定GPIO引脚配置参数的特殊寄存器,引脚锁定后,即使修改调整寄存器(CRL、CRH),引脚工作模式也不会改变,直到下一次芯片复位,该操作是不可逆的。
每组GPIO都对应一个LCKR,其引脚作用如下:
位范围 | 名称 | 功能描述 |
---|---|---|
31:16 | 保留 | 无效,读写无意义 |
15:0 | LCKy | 锁定位(Lock Bit):- 位 n(n=0~15)对应引脚 Pin n(如位 0 对应 Pin0,位 1 对应 Pin1,…,位 15 对应 Pin15)- 用于标记该引脚是否被锁定(锁定序列完成后,该位会被硬件置 1) |
16 | LOCK | 锁定控制位(Lock Control Bit):- 用于触发锁定操作的关键位,需通过特定 “写入序列” 激活- 锁定搞定后,该位会被硬件置 1,读取时可判断是否锁定成功 |
容易写入1即可,其具体操作流程为:就是为防止误操作,锁定操控不
- 向GPIOx_LCKR写入目标值(同时LOCK位置1);
- 向GPIOx_LCKR写入相同值(同时LOCK位置0);
- 再次向GPIOx_LCKR写入相同值(同时LOCK位置1);
- 读取GPIOx_LCKR的值,若LOCK位为 1,说明锁定序列已触发;再读取一次,若目标引脚的LCKy位为 1,则锁定成功。
锁定成功后,对应位被置1,且在下次芯片复位前无法软件清除,即有操作不可逆的特性。当然,部分特殊引脚(如JTAG和SWD等系统机制引脚)不支持锁定功能。