[TOC]

开始stm32的学习路程了,每一步都要打下坚实基础,今天看了stm32 gpio的操作,写一篇总结和心得吧!

什么是IO口?STM32的几种IO输入输出模式

IO口即input & out 输入与输出,单片机控制外设的一个重要方法。

IO口一般上限接3.3v,但由于有的端口存在保护二极管,可以容忍5v电压,一般在资料中用TF标出。

stm32的IO口可以由软件来配置为8种模式:
输入:
1、输入浮空:GPIO_Mode_IN_FLOATING
电平进入后,不经过上下拉,在触发施密特触发器后,进入输入数据寄存器,最后由CPU读取。
浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定(输入达到条件就触发),如果在该引脚悬空的情况下,读取该端口的电平是不确定的。
在这里插入图片描述
2、输入上拉:GPIO_Mode_IPU ,原本需要低电平触发,有上拉电阻存在,使得端口为高电平,达到抗干扰作用,只接受低电平!
在这里插入图片描述
3、输入下拉: GPIO_Mode_IPD 原本需要高电平触发,有下拉电阻存在,使得端口为低电平,达到抗干扰作用,只接受高电平!
在这里插入图片描述
4、模拟输入:GPIO_Mode_AIN 输入没有上拉下拉电阻,且输入的是电压而非电平(电平只有高低之分,电压则是一个连续值);该输入方式可用于AD转换接受模拟信号
在这里插入图片描述
输出:
5、开漏输出:GPIO_Mode_Out_OD
先写入输出寄存器中,再经过输出控制电路,最终到达端口(在此模式下,IO口也可以读取IO口电压)
优点:
1、输出端相当于三极管的集电极,要得到高电平需要上拉电阻才行,适合做电流型的驱动,其吸收电流的能力较强(20ma以内)
2、开漏是用来连接不同电平的器件,匹配电平用的,因为开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。比如加上上拉电阻就可以提供TTL/CMOS电平输出等
但其也带来上升沿的延时!
3、可以将多个开漏输出的Pin,连接到一条线上。通过一只上拉电阻,在不增加任何器件的情况下,形成“与逻辑”关系。这也是I2C,SMBus等总线判断总线占用状态的原理。
(补充:什么是“线与”?: 在一个结点(线)上, 连接一个上拉电阻到电源 VCC 或 VDD 和 n 个 NPN 或 NMOS 晶体管的集电极 C 或漏极 D, 这些晶体管的发射极 E 或源极 S 都接到地线上, 只要有一个晶体管饱和, 这个结点(线)就被拉到地线电平上. 因为这些晶体管的基极注入电流(NPN)或栅极加上高电平(NMOS),晶体管就会饱和, 所以这些基极或栅极对这个结点(线)的关系是或非NOR 逻辑. 如果这个结点后面加一个反相器, 就是或 OR 逻辑.)
在这里插入图片描述
6、开漏复用输出功能:GPIO_Mode_AF_OD
复用与不是复用的区别在于—非复用输出是cpu控制,复用功能则是从复用功能输出口上接受输出信号,可以是从外设接受输入信号输出。
在这里插入图片描述
7、推挽输出:GPIO_Mode_Out_PP
与开漏输出不同,在于其在经过输出控制电路后面的MOS管不同!
优点:推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
在这里插入图片描述
8、推挽式复用输出功能:GPIO_Mode_AF_PP
复用功能与之前类似,输出信号来源来自复用功能输出口。

IO口总结:

在STM32中选用IO模式总结:
(1) 浮空输入_IN_FLOATING ——浮空输入,可以做KEY识别
(2)带上拉输入_IPU——IO内部上拉电阻输入
(3)带下拉输入_IPD—— IO内部下拉电阻输入
(4) 模拟输入_AIN ——应用ADC模拟输入,或者低功耗下省电
(5)开漏输出_OUT_OD ——IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现STM32的IO双向功能

(6)推挽输出_OUT_PP ——IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的
(7)复用功能的推挽输出_AF_PP ——片内外设功能(I2C的SCL,SDA)
(8)复用功能的开漏输出_AF_OD——片内外设功能(TX1,MOSI,MISO.SCK.SS)

正点原子对常用输入输出方式的作用的总结:(我借用一波)

1)作为普通GPIO 输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时不要使能该引脚对应的所有复用功能模块。
2)作为普通GPIO 输出:根据需要配置该引脚为推挽输出或开漏输出,同时不要使能该引脚对应的所有复用功能模块。
3)作为普通模拟输入:配置该引脚为模拟输入模式,同时不要使能该引脚对应的所有复用功能模块。
4)作为内置外设的输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时使能该引脚对应的某个复用功能模块。
5)作为内置外设的输出:根据需要配置该引脚为复用推挽输出或复用开漏输出,同时使能该引脚对应的所有复用功能模块。

最后介绍方面呢,I每个IO口都有中断能力,只要配置一下,就可以来打开外部中断。
还有GPIO锁定机制,锁定后除非复位,否则不能再修改,当然需要在寄存器中来配置啦,大致的介绍就这么多,下面一篇文章我将介绍GPIO如何配置的问题,也是必须要学会的地方。

了解了GPIO口的工作原理,我们下一步的操作就是如何去配置GPIO,今天我将分享一下我对GPIO配置的了解(参考资料:正点原子开发手册)

配置每组IO口的寄存器:(每一组一套寄存器)

1、两个32位的配置寄存器;GPIOx_CRL,GPIOx_CRH
2、两个32位的数据寄存器:GPIOx_IDR,GPIOx_ODR
3、一个32位置位/复位寄存器:GPIOx_BSRR
4、一个16位的复位寄存器GPIOx_BRR
5、一个32位的锁定寄存器GPIOx_LCKR

详解 :

CRL和CRH控制IO口,是四个位控制一个口,所以要两个32位的来控制16个口

1、GPIOx_CRL:端口配置低寄存器:

每个口由四位控制,为MODE+CNF联合配置,具体模式在下图中。(输入要与输入对应,输出也要与输出对应)
在这里插入图片描述
具体对应输入输出配置表(对上图的详解):
在这里插入图片描述

2、GPIOx_CRH:端口配置高寄存器(与低配置寄存器一样,这里把配置图放这,供大家参考)

在这里插入图片描述

3、GPIOx_IDR:端口数据输入寄存器:

32位寄存器其中16-31位一直保留,始终为0;0-15位则对应着16个IO口,1为高电平,0为低电平。
在这里插入图片描述

4、GPIOx_ODR:端口数据输出寄存器:

与数据输入的配置差不多,但是ODR在输入模式下可以控制上升沿和下降沿的配置!(前面也提到)
在这里插入图片描述
在这里插入图片描述

5、GPIOx_BSRR:端口位设置/清除寄存器:

BSRR比之上面的寄存器有些复杂,但也不是很难,32位分两个16位高和低,共同控制ODR数据输出寄存器的每一位输出;具体的控制方式在下图中。高低位0不产生影响,高位置1则对应ODR位为0.低位置1则对应ODR为1;高低位同事作用时,则只有低16位起作用!
在这里插入图片描述

6、GPIOx_BRR:端口位清除寄存器:

该寄存器与上面BSRR高16位作用相同,F1中有些赘余,但F4中已优化。
在这里插入图片描述
锁定寄存器不常用到,我在就没有详细了解,今天就写到这里吧!
以上就是常用寄存器配置的讲解!

STM32库函数GPIO口开发
资料参考正点原子F1开发资料

GPIO重要函数:

在这里插入图片描述

重要函数详解:

1个初始化函数:

void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef*GPIO_InitStruct);
在这里插入图片描述
使用方式:
GPIO_Init(GPIOB, &GPIO_InitStructure); 库的原理:
1、先定义一个结构体,(方便多次定义端口)
在这里插入图片描述
上面创建一个结构体,用他来创建一个结构体:GPIO_InitTypeDef GPIO_InitStructure;
2、配置结构体
将结构体内部对应的成员赋值;

1
2
3
4
5
6
7
8
9
10
11
12
13
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置;范围:0~15;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
//对应方式:
//GPIO_Mode_AIN = 0x00, 模拟输入
//GPIO_Mode_IN_FLOATING = 0x04, 输入浮空
//GPIO_Mode_IPD = 0x28, 输入下降沿
//GPIO_Mode_IPU = 0x48, 输入上升沿
//GPIO_Mode_Out_OD = 0x14, 开漏输出
//GPIO_Mode_Out_PP = 0x10, 推挽输出
//GPIO_Mode_AF_OD = 0x1C, 复用开漏
//GPIO_Mode_AF_PP = 0x18 复用推挽
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz 、2MHZ 、10MHZ

2个读取输入电平函数:

*uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin);**
作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。
例如:
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平,第一个是定义哪一个组。第二个是定义哪一个口

*uint16_t GPIO_ReadInputData(GPIO_TypeDef GPIOx);**
作用:读取某组GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。
例如:
GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有io口输入电平,返回一个16位值

2个读取输出电平的函数:与上面一样

uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输出电平。实际操作的是GPIO_ODR寄存器
例如:
GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平

uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输出电平。实际操作的是GPIO_ODR寄存器。
例如:
GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平

4个设置输出电平的函数:

*void GPIO_SetBits(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin);**
作用:设置某个IO口输出为高电平(1)。实际操作BSRR寄存器

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为低电平(0)。实际操作的BRR寄存器。

void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);**
*void GPIO_Write(GPIO_TypeDef GPIOx, uint16_t PortVal);

这两个函数不常用,也是用来设置IO口输出电平。

eg:
跑马灯例程:
1、使能IO口时钟。
调用函数RCC_APB2PeriphColckCmd();(不同IO组,调用时钟使能不一样)
2、初始化IO口模式。
调用函数:GPIO_Init();
3、操作IO口,输出高低电平。
GPIO_SetBits();
GPIO_ResetBits();

wechat