文章目录
一、GPIO是什么?
GPIO全称general-purpose input/output ,即通用输入输出端口。
二、外设框图
输入通道整体输入逻辑: 外界信号通过IO口进入单片机后,首先由保护二极管检查是否具有破坏性,检查通过后经过上下拉电路,该电路是设置默认电平的,从这里开始,信号要么直接输入,即模拟输入,要么经过触发器转化为数字信号,然后再分叉,要么保存在输入数据寄存器由用户读取,要么复用,被片上外设读取。
1、保护二极管
利用二极管达到阈值电压单向导通的特性,防止从引脚输入过大或过小电压。
当输入电压超过VDD+0.7V(假使是硅管),则上面的二极管导通。
当输入电压小于Vss-0.7V,则下面的二极管导通。
由此将真正输入芯片的电压钳位在Vss-0.7V至VDD+0.7V之间。
2、上下拉电阻
通过软件可以控制内部是否接入上下拉电阻,一共有三种情况:
①仅开启上拉电阻,引脚默认输出高电平
②仅开启下拉电阻,引脚默认输出低电平
③上拉下拉电阻都不开启,此时引脚处于浮空状态
浮空模式下,引脚的电压是不确定的,可能为1,也可能为0,来回摇摆。
根据拉电阻的阻值大小,可以分为强拉或弱拉。
拉电阻较小时为强拉,可以抵抗外部噪声的能力较强,代价是相应的功耗也较大。
STM32内部的拉电阻属于弱拉,通过查阅数据手册可知拉电阻阻值在40K左右,通过此拉电阻输出的电流很小,如果想要输出一个大电流。那么就需要外接阻值较小的拉电阻了,其实就是增加导线的输出电流。
3、输入数据寄存器
经过上下拉电阻的信号有三个去向,首先就是经过触发器转化为0/1进入输入数据寄存器。
4、模拟输入
其次信号如果不经过触发器,则可以将外部模拟信号直接输入内部,如ADC。
5、复用功能输入
第三个去向就是将信号转化为0/1后交由外设处理。
输出通道的整体逻辑: 输出的数据来源有3个,首先我可以控制位设置/清除寄存器,将IO口写1/0,我也可以将多个二进制数据写入数据输出寄存器让IO口依次输出,我还可以将控制权交给片内外设,让它控制IO输出它想要输出的信号。是用户控制还是外设控制由输出控制左边的梯形结构进行切换,输出信号经过双MOS 结构可以调整为推挽模式还是开漏模式,最后经过IO口输出。
6、输出数据寄存器
将想要输出的数据放入该寄存器内
7、位设置/清除寄存器
将IO口置0或置1。
其中高16位负责reset,低16位负责set。
除了GPIOx_BSRR,还有一个类似的寄存器叫GPIOx_BRR,它相当于GPIOx_BSRR的高16位
这个“多余“的寄存器只是为了方便IO口清0,不像BSRR要清零还得先将数据左移16位再写入。
8、复用功能输出
将GPIO输出控制权交给内部外设,输出外设想要输出的信号。
9、双MOS管结构
该结构可以实现推挽输出与开漏输出。
(1)推挽输出
推挽输出两个MOS管都工作。
当输入为1时,经过反相器得到0,此时PMOS导通,NMOS截止,电流从VDD“推向”输出端,最后输出1;
当输入为0时,经过反相器得到1,此时PMOS截止,NMOS导通,电流从输出端被“拉回”地,拉回也就是挽的意思,最后输出0;
(2)开漏输出
开漏输出只用到了NMOS。
当输入为1时,经过反相器得到0,此时NMOS截止,引脚相当于在内部被断开,既不输出1也不输出0,对输出高阻态;
当输入为0时,经过反相器得到1,此时NMOS导通,对外输出0;
想要输出高电平则需要外加上拉电阻:
这种结构有一个重要的特性就是线与特性,即当有多个设置为开漏输出模式的引脚连在一起时,只要有一个引脚输出0,则整个线路为0,只有所有引脚输出为高阻态,线路上电平才为1。
开漏输出的应用:
①I2C通讯,该通讯协议通过线与解决多主机同时发送数据时的优先级判断,详见【STM32】I2C
②电平转换,如果要用3.3V单片机控制5V外设,则可以用开漏输出模式,外加一个连接5V的上拉电阻
10、端口配置寄存器(不在框图中)
端口配置寄存器有两个,其中GPIOx_CRL控制低8位,GPIOx_CRH控制高8位。
这两个32位寄存器分别控制16个IO口,每个IO口都有一个专属的MODE[1:0]和CNF[0:1]控制。
其中,MODE控制是输入还是输出,输出又有详细的速度配置,支持速率越高,功耗越大。
CNF控制在输入和输出模式下具体的工作模式配置。
GPIO共有8种工作模式:
①模拟输入,如ADC采集
②上拉输入
③下拉输入
④浮空输入,如按键检测
⑤推挽输出,如点亮LED
⑥开漏输出,如I2C,电平转换
⑦复用推挽输出,即由外设控制输出
⑧复用开漏输出,即由外设控制输出
三、编程
首先初始化GPIO
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK, ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/* 关闭所有led灯 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
然后就可以库函数了
void GPIO_DeInit(GPIO_TypeDef* GPIOx);//恢复默认设置
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);//将配置参数写入寄存器
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);//填充GPIO_InitStruct内的元素为复位值
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读指定IO引脚输入数据
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);//读指定端口输入数据
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读指定端口引脚输出数据
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);//读指定端口输出数据
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//置1指定的端口引脚
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//清0指定的端口引脚
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);//设置或清除选择的数据端口引脚
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);//写指定数据到GPIOx端口寄存器
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);//选择GPIO引脚作为事件输出
void GPIO_EventOutputCmd(FunctionalState NewState);//允许或禁止事件输出
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);//选择IO口作为事件线
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
四、答疑
1、GPIO引脚驱动能力?
25mA,可以驱动一般的发光二极管。
2、我怎么知道每个外设相应的引脚应该配置为什么模式?
参考手册中有明确规定
参考资料
1、《STM32库开发实战指南》
2、https://zhuanlan.zhihu.com/p/362467204
待补充内容:GPIO中断