目录
1 GPIO概述
GPIO通用输入输出接口(General Purpose Input/Output)的简称,主要用于数字量的输入输出,是微控制器中使用频率最高的外设。包括以下功能:
输出功能:输出高低电平,控制外围设备
输入功能:读取引脚电平状态,获取按键和各类传感器的信息输入
复用功能:作为片内外设的接口
时序模拟:通过改变引脚高低电平模拟时序信号
2 GPIO的特性
多种工作模式:提供四种工作模式
灵活复用:STM32微控制器内部集成了很多外设,这些外设的对外引脚都是与GPIO引脚复用
5V电压容限:大部分引脚具备5V电压的输入,方便与5V供电的外围电路连接
外部中断:每一个GPIO引脚都具备外部中断的能力
3 两个重要概念
3.1 端口(PORT):
一组端口可以看成独立的子模块,由多个寄存器控制,包括多个引脚,如GPIOA。每一组端口可以看成独立的子模块,包括多个引脚,通过多个硬件寄存器(端口寄存器组)来控制引脚的工作模式
3.2 引脚(PIN):
引脚对应微控制器的一个管脚,归属于端口,每组端口最多包含16个引脚;引脚的工作模式由端口寄存器组的对应位控制
4 电路基本结构
4.1 端口寄存器组
用于设置该组端口所含引脚的工作模式,通过设置这三个寄存器的内容,并结合驱动电路,就可以在该端口指定的引脚上输出高低电平或者读取指定引脚的电平状态。
4.2 输入驱动器
输入驱动器的主要电路元件是TTL肖特基触发器(由肖特基管构成的施密特触发器),其原理是当输入电压高于正向阈值电压时,输出高电平;当输入电压低于负向阈值电压时,输出低电平。引脚上的模拟电压经过触发器后,转换为0或1的数字信号,并输入到输入数据寄存器。
4.3 输出驱动器
输出驱动器的主要元件是一对P-MOS管和N-MOS管,由输出数据寄存器控制两个MOS管的导通和截止,根据导通和截止状态,使得GPIO具有“推挽输出”和“开漏输出”的模式。
4.4 引脚电路
4.4.1保护二极管
用于防止外部过高或过低的电压输入芯片而损坏芯片。当引脚电压高于Vdd_pt(5V电压容限)时,上方二极管导通,将输入电压限制在“Vdd_pt + Ud”(Ud表示二极管导通电压);当引脚电压低于Vss时,下方二极管导通,将输入电压限制在“Vss - Ud”。
4.4.2 上拉/下拉电阻
上拉/下拉电阻用于设置引脚的默认电压。上拉使能,引脚默认电压为高电平,下拉使能,引脚默认电压为低电平。
5 工作模式
5.1 输入模式
禁用输出驱动器,打开施密特触发器,引脚电平状态经过施密特触发器转换后,送到输入数据寄存器,以供读取引脚的电平状态。
根据是否开启上拉/下拉电阻,可分为三种输入模式:
浮空输入:不使能上拉/下拉电阻。这种情况下,IO引脚的电平状态不确定,完全由外部输入决定。MCU上电复位后,引脚默认的工作模式就是浮空输入。
上拉输入:使能上拉电阻。引脚外部没有信号输入时,默认为高电平。
下拉输入:使能下拉电阻。引脚外部没有信号输入时,默认为低电平。
5.2 输出模式
启用输出驱动器,打开施密特触发器,可以读取引脚状态。
根据MOS管工作状态,可分为两种输出模式:
推挽输出:P-MOS管和N-MOS管轮流工作,可以输出高低电平。输出数据寄存器中某一位为1时,经过非门,P-MOS导通,N-MOS截止,引脚输出高电平;输出数据寄存器中某一位为0时,经过非门,P-MOS截止,N-MOS导通,此时引脚的输出低电平。
开漏输出:P-MOS一直处于截止状态,只有N-MOS工作,只能输出低电平。输出数据寄存器中某一位为0时,N-MOS导通,此时引脚的输出低电平;但如果输出数据寄存器中某一位为1时,N-MOS也截止,此时引脚的输出电平跟引脚外部的上拉/下拉电阻决定,如果没有上拉/下拉电阻,则引脚处于悬空状态。开漏输出一般用于IIC总线(开漏输出加上拉电阻实现双向数据通信)或者作为电平转换(3.3V与5V之间的转换)
5.3 模拟模式:
模拟模式主要用于片内模拟外设(A/D,D/A,模拟比较器等)的信号通道。
当引脚工作于模拟模式时,施密特触发器被旁路,输出驱动器关闭,上拉/下拉电阻禁能,输入数据寄存器的值恒为0。此时IO引脚的功耗较小,因此在工程应用中,常常将没有用到的IO引脚设置为模拟模式,以减少系统功耗,提高抗干扰能力。
5.4 复用模式:
GPIO的基本功能是数字IO,而STM32微控制器片内集成了大量外设,如TIMER和UART等,这些片内外设的外部引脚可与GPIO引脚复用。
复用模式下,引脚的电平状态不再受端口寄存器的控制,而是由片内外设直接控制。但此时输出驱动器依旧可以启用,可以·配置为推挽输出和开漏输出;施密特触发器依旧可以打开,可以读取引脚的电平状态。
6 寄存器介绍
6.1 基于寄存器开发的特点
- 程序编写直接面向底层的寄存器,与硬件密切相关
- 要求编程者熟悉STM32微控制器的体系架构和工作原理,特别是每个寄存器的功能以及寄存器中每一位的含义
- 程序代码简练,执行效率高
- 开发难度大,开发周期长,后期维护升级以及硬件平台移植的工作量较大
6.2 GPIO相关寄存器
6.2.1模式寄存器(GPIOx_MODER)
31~30 | 29~28 | 27~26 | 25~24 | 23~22 | 21~20 | 19~18 | 17~16 |
MODER 15[1:0] | MODER 14[1:0] | MODER 13[1:0] | MODER 12[1:0] | MODER 11[1:0] | MODER 10[1:0] | MODER 9[1:0] | MODER 8[1:0] |
15~14 | 13~12 | 11~10 | 9~8 | 7~6 | 5~4 | 3~2 | 1~0 |
MODER 7[1:0] | MODER 6[1:0] | MODER 5[1:0] | MODER 4[1:0] | MODER 3[1:0] | MODER 2[1:0] | MODER 1[1:0] | MODER 0[1:0] |
模式寄存器一共有32位,每2位一组,对应一个不同的IO口,通过软件写入,配置IO引脚的模式:
00 | 输入模式(复位值) |
01 | 通用输出模式 |
10 | 复用功能模式 |
11 | 模拟模式 |
6.2.2 输出类型寄存器(GPIOx_OTYPER)
31~16 | |||||||||||||||
保留 | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
OT15 | OT14 | OT13 | OT12 | OT11 | OT10 | OT9 | OT8 | OT7 | OT6 | OT5 | OT4 | OT3 | OT2 | OT1 | OT0 |
该寄存器用于控制GPIO端口各引脚的输出类型,该寄存器需在引脚设置为输出模式下才有效,其他工作模式无效。该寄存器高16位保留,低16位的每1位对应一个不同的IO口
0 | 推挽输出(复位值) |
1 | 开漏输出 |
6.2.3 输出速度寄存器(GPIOx_OSPEEDR)
31~30 | 29~28 | 27~26 | 25~24 | 23~22 | 21~20 | 19~18 | 17~16 |
OSPEEDR 15[1:0] | OSPEEDR 14[1:0] | OSPEEDR 13[1:0] | OSPEEDR 12[1:0] | OSPEEDR 11[1:0] | OSPEEDR 10[1:0] | OSPEEDR 9[1:0] | OSPEEDR 8[1:0] |
15~14 | 13~12 | 11~10 | 9~8 | 7~6 | 5~4 | 3~2 | 1~0 |
OSPEEDR 7[1:0] | OSPEEDR 6[1:0] | OSPEEDR 5[1:0] | OSPEEDR 4[1:0] | OSPEEDR 3[1:0] | OSPEEDR 2[1:0] | OSPEEDR 1[1:0] | OSPEEDR 0[1:0] |
该寄存器用于控制GPIO端口各引脚的输出速度,该寄存器需在引脚设置为输出模式下才有效,其他工作模式无效。
00 | 低速(复位值) |
01 | 中速 |
10 | 高速 |
11 | 超高速 |
【注意】引脚的输出速度跟芯片的最高工作频率有关
6.2.4 上拉/下拉寄存器(GPIOx_PUPDR)
31~30 | 29~28 | 27~26 | 25~24 | 23~22 | 21~20 | 19~18 | 17~16 |
PUPDR 15[1:0] | PUPDR 14[1:0] | PUPDR 13[1:0] | PUPDR 12[1:0] | PUPDR 11[1:0] | PUPDR 10[1:0] | PUPDR 9[1:0] | PUPDR 8[1:0] |
15~14 | 13~12 | 11~10 | 9~8 | 7~6 | 5~4 | 3~2 | 1~0 |
PUPDR 7[1:0] | PUPDR 6[1:0] | PUPDR 5[1:0] | PUPDR 4[1:0] | PUPDR 3[1:0] | PUPDR 2[1:0] | PUPDR 1[1:0] | PUPDR 0[1:0] |
该寄存器用于使能GPIO端口各引脚的上拉/下拉电阻,可用于输入模式和输出模式
00 | 不上拉也不下拉(复位值) |
01 | 上拉 |
10 | 下拉 |
11 | 保留 |
6.2.5 输入数据寄存器(GPIOx_IDR)
31~16 | ||||||||||||||||
保留 | ||||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
IDR15 | IDR14 | IDR13 | IDR12 | IDR11 | IDR10 | IDR9 | IDR8 | IDR7 | IDR6 | IDR5 | IDR4 | IDR3 | IDR2 | IDR1 | IDR0 |
该寄存器用于读取GPIO端口各引脚的电平状态,可用于输入和输出模式
0 | 对应引脚输入低电平 |
1 | 对应引脚输入高电平 |
【注意】输入数据寄存器是只读模式,不能通过软件写入。当引脚设置为输出或复用模式时,也可以通过输入数据寄存器读取引脚电平状态
6.2.6 输出数据寄存器(GPIOx_ODR)
31~16 | ||||||||||||||||||
保留 | ||||||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||
ODR15 | ODR14 | ODR13 | ODR12 | ODR11 | ODR10 | ODR9 | ODR8 | ODR7 | ODR6 | ODR5 | ODR4 | ODR3 | ODR2 | ODR1 | ODR0 |
该寄存器用于控制GPIO端口各引脚的输出电平,仅用于输出模式
0 | 对应引脚输出低电平 |
1 | 对应引脚输出高电平 |
6.2.7 置位/复位寄存器(GPIOx_BSRR)
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
BR 15 | BR 14 | BR 13 | BR 12 | BR 11 | BR 10 | BR 9 | BR 8 | BR 7 | BR 6 | BR 5 | BR 4 | BR 3 | BR 2 | BR 1 | BR 0 |
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
BS 15 | BS 14 | BS 13 | BS 12 | BS 11 | BS 10 | BS 9 | BS 8 | BS 7 | BS 6 | BS 5 | BS 4 | BS 3 | BS 2 | BS 1 | BS 0 |
该寄存器用于置位或复位GPIO端口的各个引脚,功能和输出数据寄存器类似,都可以控制引脚输出高低电平
高16位 | 低16位 | ||
0 | 无效 | 0 | 无效 |
1 | 输出低电平 | 1 | 输出高电平 |
和输出数据寄存器相比,置位复位寄存器有两个优势:
- 置位复位寄存器的操作是原子操作,不会被中断打断
- 在控制多个引脚同时输出时更加方便
6.3 利用指针访问单个寄存器
#define GPIOA_MODER *(volatile unsigned int *)(0x40020000UL) //寄存器映射
GPIOA_MODER = 0x00000004; //向GPIOA_MODER写入0x03
unsigned int a; //定义无符号整形变量a
a = GPIOA_MODER; //读取GPIOA_MODER的值并赋值给a
【代码解析】:
- 0x40020000表示GPIOA_MODER的起始地址
- 后缀UL表示无符号长整型(强制类型转换,将signed int 转为 unsigned long)
- 地址前加(unsigned int *)修饰,表示将该地址强制转换为一个指向无符号整形数据的指针,来实现访问0x40020000这个地址时,可以连续访问4字节存储单元
- volatile关键字的作用是避免编译器优化,即每次访问该存储单元时,都要去访问它的原始数据
- 最外面的 * 表示解引用,即访问指针所指向地址的内容
6.4 利用寄存器操作点亮LED
#define GPIOA_MODER *(unsigned int *)(0x40020000UL) //模式寄存器映射
#define GPIOA_ODR *(unsigned int *)(0x40020014UL) //输出数据寄存器映射
#define RCC_AHB1ENR *(unsigned int *)(0x40023830UL) //外设时钟使能寄存器映射
unsigned int delay = 1000000; //定义延时大小
int main()
{
RCC_AHB1ENR |= 1<<0; //开启GPIOA的时钟
GPIOA_MODER &= ~(3<<(5*2)); //清除模式寄存器的bit11:bit10为00
GPIOA_MODER |= 1<<(5*2); //设置bit11:bit10为01,引脚A5为推挽输出
while(1)
{
GPIOA_ODR |= 1<<5; //设置bit5为1,输出高电平
delay = 1000000; //重新赋值
while(delay--); //延时
GPIOA_ODR |= ~(1<<5); //设置bit5为0,输出低电平
delay = 1000000; //重新赋值
while(delay--); //延时
}
}
【LED其他实验可参考:基于STM32G0B1+Cubemx + HAL库的LED实验】