100ask七天物联网训练营学习笔记 - GPIO
今天回看了训练营GPIO
的课程讲解,认真查阅芯片手册结合自己的理解把内容总结一下。
1. GPIO介绍
GPIO
是英文General-Purpose Input/Output
的缩写,意为通用目的输入输出端口,是MCU中最简单也是最常用的外设,由于芯片引脚资源原因,部分外设往往要与GPIO
端口复用芯片的引脚。这就导致部分GPIO
端口除了基本的输入输出功能外,还可能作为其他外设的功能引脚,由外设进行控制。所有功能都需要通过软件来进行配置。下面我们来看看GPIO
的配置。
还有一点,STM32
通常工作电压为3.3V
,部分GPIO
口是可以兼容5V
电压工作,可以从芯片手册来确定某个GPIO
是否兼容5V
电压。如果某个GPIO
的I/O Leve标注为FT
,则说明它兼容5V
电压。
2. GPIO硬件结构(以STM32F103为例)
2.1 GPIO功能描述
通过芯片手册我们可以看出,每组GPIO Port
都有2
个32-bit
配置寄存器、2
个32-bit
数据寄存器,1
个32-bit
置位/复位寄存器,1
个16-bit
复位寄存器以及一个32-bit
的锁定寄存器。GPIO
可以设置的模式如下:
- 浮空输入
- 上拉输入
- 下拉输入
- 模拟输入
- 开漏输出
- 推挽输出
- 复用推挽输出
- 复用开漏输出
2.2 GPIO结构框图
- 保护二极管:当引脚电压高于VDD时,上侧二极管导通,当引脚电压低于VSS时,下侧二极管导通,可以有效防止过高或过低电压损坏芯片内部电路。
- P-MOS和N-MOS:
- 推挽输出:推挽输出时P-MOS和N-MOS都参与工作,内部输出高电平,经过反向后,P-MOS管导通,N-MOS管关闭,对外输出高电平;内部输出低电平经过反向后,N-MOS导通,P-MOS关闭,对外输出低电平。在推挽模式下,GPIO输出具有一定的驱动能力,P-MOS负责灌电流,N-MOS负责拉电流。这种模式可以应对绝大部分使用情况。
- 开漏模式:当设置为开漏模式后,P-MOS不在参与工作。当内部输出低电平时,经过反向,N-MOS导通,使得输出低电平;当内部输出高时,N-MOS关闭,P-MOS也是关闭,引脚即无法输出高电平也无法输出低电平,为高阻态。所以,开漏模式我们使用时想要输出高电平,必须外部上拉。开漏模式一般应用在需要“线与”功能的电路中,常见在IIC等通信总线。若当两个推挽输出结构连接在一块时,如果一个引脚输出高,一个引脚输出低,就会造成第一个引脚P-MOS导通,另一个引脚N-MOS导通,电流会从VCC直接流向GND发生短路。这也解释了为什么推挽输出不能实现“线与”。
- 输出数据寄存器:为输出控制电路输入信号,数据由GPIOx_ODR寄存器提供。
- 复用功能输出:
GPIO
由其他外设对引脚进行控制。例如,进行UART
通信时,一个引脚作为TX
,一个引脚作为RX
。 - 输入数据寄存器:引脚配置为输入模式时,内部先经过上拉下拉电阻,可以通过软件配置成上拉、下拉,再经过施密特触发器,即可以保证信号为输入,也将模拟信号转换为数字信号。
- 复用功能输入:与复用功能输出一致,作为某个外设的引脚由外设进行控制。
- 模拟输入:从信号进入施密特触发器前采集,直接使用模拟输入信号。例如引脚作为
ADC
的输入引脚。
3. GPIO配置(100ASK_STM32F103_MINI为例)
使用资源:
- 用户LED,连接在GPIOA1,低电平使能。
- 用户KEY,连接在GPIOA0,按下为低电平,松开后为高电平。
3.1 通过寄存器配置
4. 实验(100ASK_STM32F103_MINI为例)
本实验实现用户KEY按下,LED点亮,用户KEY松开,LED熄灭。
// main.c
#include <stdint.h>
#define RCC_BASE 0x40021000
#define RCC_APB2ENR (RCC_BASE + 0x18)
#define GPIOA_BASE 0x40010800
#define GPIOA_CRL (GPIOA_BASE + 0x00)
#define GPIOA_IDR (GPIOA_BASE + 0x08)
#define GPIOA_ODR (GPIOA_BASE + 0x0C)
int main(void)
{
volatile uint32_t *rcc_apb2enr = (uint32_t *)RCC_APB2ENR;
volatile uint32_t *gpioa_crl = (uint32_t *)GPIOA_CRL;
volatile uint32_t *gpioa_idr = (uint32_t *)GPIOA_IDR;
volatile uint32_t *gpioa_odr = (uint32_t *)GPIOA_ODR;
// 使能GPIOA时钟
*rcc_apb2enr |= (1 << 2);
// 清除GPIOA_CRL寄存器[7:4]、[3:0]位
*gpioa_crl &= ~((0xFU << 0) | (0xFU << 4));
// 设置GPIOA0为浮空输入模式、GPIOA1为推挽输出模式
*gpioa_crl |= (0 << 0) | (1 << 2) | (1 << 4) | (0 << 6);
while (1)
{
// 检测按键是否被按下,GPIOA0高电平表示按键未被按下
if (*gpioa_idr & (1 << 0))
{
// GPIOA1 输出高电平,LED熄灭
*gpioa_odr |= (1 << 1);
}
else
{
// GPIOA1 输出低电平,LED点亮
*gpioa_odr &= ~(1U << 1);
}
}
}
实验完成。