学习B站上江科大stm32教学视频作的笔记
本文将从工作原理和相关固件库函数来介绍GPIO。
工作原理
GPIO简介
GPIO,(General-purpose input/output),是由STM32控制的输入输出引脚,能够与外设进行交互,输出高低电平或者与外部通讯、采集和捕获数据等等。
stm32单片机的引脚被分为很多组,每个组别又有16个引脚,各个单片机型号有着不同的引脚数,笔者的单片机是stm32f103c8t6,只有PA、PB和PC的13、14、15引脚,总的引脚数是48。
IO口的基本结构
最右边的IO引脚就是单片机引出的GPIO引脚,左边接了两个保护二极管,可以防止芯片被外部过高或者过低的输入电压烧坏,当外部电压过高,上端的保护二极管导通,当电压低于VSS,下端的二极管导通。再往左边的电路决定了GPIO的工作模式。
GPIO工作模式
图中从上到下分别是AIN模拟输入,Floating浮空输入,IPD下拉输入,IPU上拉输入,OutOD开漏输出,OutPP推挽输出,AFOD复用开漏输出,AFPP复用推挽输出。
模拟输入一般用于AD输入;浮空输入很少使用,因为极容易受外部干扰而电平跳动,输入常用上拉输入;开漏输出和推挽输出命名是根据内部的PMOS和NMOS管,在该结构中输入高电平时,经过反向后,上方的P-MOS导通,下方的N-MOS关闭,对外输出高电平;
推挽输出:芯片输出低电平,N-MOS导通,P-MOS截止,因此引脚输出低电平。芯片输出高电平,P-MOS导通,N-MOS截止,因此引脚输出高电平。
开漏输出:当芯片输出高电平,此时NMOS管截止,此时引脚输出类似浮空的高阻态,当输出低电平,此时NMOS管导通,电流被强行拉到VSS,因此引脚输出强低电平。
推挽和开漏的区别是:推挽具有强的高低电平驱动能力,而开漏只有强的低电平驱动能力。且一般情况下都是使用推挽输出,除非某些特殊场景必须使用开漏,例如I2C的通信。
GPIO端口的复用和重映射
如图所示,PA6、7等端口具有自身的主功能,又有默认的复用功能,也有重定义的功能。
主功能:本身就是作为GPIO口。
复用功能:既能作为GPIO使用,又具有其他功能,例如PA6,可以作为SPI通信1口的MISO(Master Input Slave Output)、ADC模数转换器的端口、定时器3的1通道。
重定义:可以把指定的别的引脚的功能挪到这个引脚上来使用,需要在程序中调用库函数配置。
GPIO库函数
如图是GPIO库中的函数,常用的有:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//初始化函数
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//输入读取函数,指定某一引脚
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
//输入读取函数,指定某一组GPIO全部引脚
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//输出读取函数,指定某一引脚
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
//输出读取函数,指定某一组GPIO全部引脚
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//写函数,指定某一引脚将其置1
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//写函数,指定某一引脚将其置0
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
//初始化函数,默认初始化一组GPIO
配置GPIO需要用到结构体,设置结构体的值,之后将结构体传参给结构体初始化函数,初始化指定的一组GPIO口。例如:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //PB7
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
而在配置GPIO之前,要先开启时钟。
复位和时钟控制RCC是挂载在AHB总线的,课间APB2负责的是GPIOA和GPIOB以及复用IO口AFIO的配置。示例:
//先初始化时钟,GPIO和AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//注意参数和函数名都要是APB2
点亮一个LED灯
GPIO配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
- 首先要开启RCC时钟,由于GPIO口是由APB2管理,这里函数调用要注意。
- 初始化结构体,选择输出方式是推挽输出,引脚选择GPIOA的0脚,速度选择50MHz,一般50MHz即可,除非对功耗有要求,可另行选择。
- 最后调用初始化函数,把刚刚创建的局部结构体的地址传过去,GPIO_Init函数再取结构体的成员参数来操作相应的GPIO寄存器,传址是为了减少拷贝,提高效率。
设置引脚电平
这里采用上文提到的函数就可以设置引脚的电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//写函数,指定某一引脚将其置1
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//写函数,指定某一引脚将其置0
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
GPIO_SetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
将函数放在while循环里就可以实现LED灯的闪烁,引脚置1或置0点亮LED取决于LED的连接方式