1、STM32F1 GPIO 简介
GPIO ( General Purpose Input Output )通用输入输出口
可配置为 8 种输入输出模式
引脚电平: 0V~3.3V ,部分引脚可容忍 5V
输出模式下可控制端口输出高低电平,用以驱动 LED 、控制蜂鸣器、模拟通信协议输出时序等
输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、 ADC 电压采集、模拟通信协议接收数据等
GPIO 是控制或者采集外部器件的信息的外设,即负责输入输出。它按组分配,每组 16 个
IO 口,组数视芯片而定。STM32F103ZET6 芯片是 144 脚的芯片,具有 GPIOA、GPIOB、GPIOC、
GPIOD、GPIOE、GPIOF 和 GPIOG 七组 GPIO 口,共有 112 个 IO 口可供我们编程使用。这里 重点说一下 STM32F103 的 IO 电平兼容性问题,STM32F103 的绝大部分 IO 口,都兼容 5V, 至于到底哪些是兼容 5V 的,请看 STM32F103xE 的数据手册(注意是数据手册,不是中文参考 手册),见表 5 大容量 STM32F103xx 引脚定义,凡是有 FT 标志的,都是兼容 5V 电平的 IO 口, 可以直接接 5V 的外设(注意:如果引脚设置的是模拟输入模式,则不能接 5V!),凡是不带 FT
标志的,就建议大家不要接 5V 了,可能烧坏 MCU。
2、GPIO基本结构
GPIO中寄存器有32位,而端口只有16位,故寄存器只有低16位对应的有端口
驱动器用来增加信号的驱动能力
寄存器只用于存放数据
3、GPIO 功能模式
GPIO 有八种工作模式,分别是:
1、输入浮空
2、输入上拉
3、输入下拉
4、模拟功能
5、开漏输出
6、推挽输出
7、开漏式复用功能
8、推挽式复用功能
4、GPIO 寄存器介绍
STM32F1 每组(这里是 A~D)通用 GPIO 口有 7 个 32 位寄存器控制,包括 :
2 个 32 位端口配置寄存器(CRL 和 CRH)
2 个 32 位端口数据寄存器(IDR 和 ODR)
1 个 32 位端口置位/复位寄存器 (BSRR)
1 个 16 位端口复位寄存器(BRR)
1 个 32 位端口锁定寄存器 (LCKR)
(1)端口配置低\高寄存器(GPIOx_CRL/GPIOx_CRH)(x=A…E)
端口配置寄存器共16位,但每4位数据表示1位,共需要64位,而STM32中每个寄存器都为32位,因此分为端口配置低寄存器和端口配置高寄存器。通过端口配置寄存器可以配置GPIO工作模式与端口输出速度。
注意:输出速度可以限制输出引脚的最大翻转速度,作用是降低功耗、提高稳定性,一般情况下配置为50MHz。
(2)端口输入数据寄存器(GPIOx_IDR)(x=A…E)
输入数据共16位,但寄存器共32位,因此寄存器高16位为空。
(3)端口输出数据寄存器(GPIOx_ODR)(x=A…E)
输出数据共16位,但寄存器共32位,因此寄存器高16位为空。
(4)端口位设置/清除寄存器(GPIOx_BERR)(x=A…E)
高16位用于位清除,低16位用于位设置。高16位:为0不影响;为1清0;
低16为:为0不影响;为1置1。
(5)端口位清除寄存器(GPIOx_BER)(x=A…E)
高16位为空,低16位用于清除,方法同上。
(6)端口位配置锁定寄存器(GPIOx_LCKR)(x=A…E)
高15位为空,低17位用于锁定,较少使用。
五、GPIO常用函数
(1)RCC常用函数
在RCC时钟控制的函数库中,我们最经常用到的是以下三个函数:
在函数的后面是三个总线可以设置的外设
//AHB系统总线时钟控制
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
//APB2总线时钟控制
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
//APB1总线时钟控制
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
/*第一个参数为外设选择,与STM32互联型的设备在下列列表中选择:
* @arg RCC_AHBPeriph_DMA1
* @arg RCC_AHBPeriph_DMA2
* @arg RCC_AHBPeriph_SRAM
* @arg RCC_AHBPeriph_FLITF
* @arg RCC_AHBPeriph_CRC
* @arg RCC_AHBPeriph_OTG_FS
* @arg RCC_AHBPeriph_ETH_MAC
* @arg RCC_AHBPeriph_ETH_MAC_Tx
* @arg RCC_AHBPeriph_ETH_MAC_Rx
*
* @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
* RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
* RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
* RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
* RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
* RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
* RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11
*
* @arg RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4,
* RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7,
* RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3,
* RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_USART4,
* RCC_APB1Periph_USART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2,
* RCC_APB1Periph_USB, RCC_APB1Periph_CAN1, RCC_APB1Periph_BKP,
* RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC,
* RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14
*
* 其他设备在下列列表中选择:
* @arg RCC_AHBPeriph_DMA1
* @arg RCC_AHBPeriph_DMA2
* @arg RCC_AHBPeriph_SRAM
* @arg RCC_AHBPeriph_FLITF
* @arg RCC_AHBPeriph_CRC
* @arg RCC_AHBPeriph_FSMC
* @arg RCC_AHBPeriph_SDIO
*
*可用按位或(|)来选择多个外设
*
*第二个参数为选择使能或失能,选择:ENABLE or DISABLE
*/
(2)通用IO口的函数库
在GPIO通用IO口的函数库中,我们最经常用到的是以下函数:
//复位函数,调用这个函数后,所指定的GPIO外设就会被复位
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
//复位函数,可以复位AFIO外设
void GPIO_AFIODeInit(void);
//初始化函数,功能:用结构体的参数来初始化GPIO口
//初始化时,我们需要先定义一个结构体变量,然后给结构体赋值,最后调用初始化函数,函数内部会自动读取结构体的值,然后把外设的各个参数配置好。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//功能:把结构体变量赋一个默认值
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
//以下四个函数为GPIO的读取函数,均有返回值
//功能:读取输入数据寄存器某一个端口的输入值
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//功能:读取整个输入数据寄存器
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);
//以下四个函数为GPIO的写入函数
//功能:将指定端口引脚设置为高电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//功能:将指定端口引脚设置为低电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//功能:对指定端口进行写入操作
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
//功能:同时对16个端口进行写入操作
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
/*参数注释:
*①GPIO_TypeDef* GPIOx,GPIO端口选择,值为GPIOx(x=A~G);
*可用按位或(|)来选择多个端口
*
*②uint16_t GPIO_Pin,IO口引脚选择,一个GPIO端口有16个引脚,所以其值为GPIO_Pin_x(x=0~15)
*可用按位或(|)来选择多个引脚
*
*③BitAction BitVal,指定写入的数据值,这个参数可以是BitAction枚举中的一个值,值为:
* @arg Bit_RESET:清除端口值,即置低电平
* @arg Bit_SET:设置端口值,即置高电平
*
*④uint16_t PortVal,指定要写入端口输出数据寄存器的值
*
*⑤GPIO_InitTypeDef* GPIO_InitStruct,GPIO初始化结构体的地址
*/
/*初始化结构体定义:
*①定义一个结构体变量
*GPIO_InitTypeDef GPIO_InitStructure;
*结构体如下:
*typedef struct
*{
* uint16_t GPIO_Pin;
* GPIOSpeed_TypeDef GPIO_Speed;
* GPIOMode_TypeDef GPIO_Mode;
*}GPIO_InitTypeDef;
*由此可知该结构体有三个成员,分别为GPIO_Pin、GPIO_Speed和GPIO_Mode,下一步就是分别赋值
*
*②给结构体赋值:
*GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出
*GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //使用GPIO的0号引脚
*GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
*
*GPIO_Mode,用于设置工作模式,其枚举如下:
*typedef enum
*{ GPIO_Mode_AIN = 0x0, //模拟输入模式(Analog IN)
* GPIO_Mode_IN_FLOATING = 0x04, //浮空输入模式
* GPIO_Mode_IPD = 0x28, //下拉输入模式(In Pull Down)
* GPIO_Mode_IPU = 0x48, //上拉输入模式(In Pull Up)
* GPIO_Mode_Out_OD = 0x14, //开漏输出模式(Out Open Drain)
* GPIO_Mode_Out_PP = 0x10, //推挽输出模式(Out Push Pull)
* GPIO_Mode_AF_OD = 0x1C, //复用开漏模式(Atl Open Drain)
* GPIO_Mode_AF_PP = 0x18 //复用推挽模式(Atl Push Pull)
*}GPIOMode_TypeDef;
*
*GPIO_Pin,选择引脚,值为GPIO_Pin_x(x=0~15和All)
*
*GPIO_Speed,选择输出速度,其枚举如下:
*typedef enum
*{
* GPIO_Speed_10MHz = 1,
* GPIO_Speed_2MHz,
* GPIO_Speed_50MHz
*}GPIOSpeed_TypeDef;
*常用速度为50MHz
*/
六、主要程序
1、第一步:使用RCC初始化GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIOA是位于APB2总线上的
2、 第二步:使用GPIO_Init函数初始化GPIO
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;//50兆hz的速度
//用结构体的函数初始化GPIO口
GPIO_Init(GPIOA, &GPIO_InitStructure);//第二个参数为指向结构体的指针,故传地址
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
若要使用多个端口GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
若要使用A/B/C/D的全部端口GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All
(3)使用输出或输入的函数控制GPIO口
调用输入的函数,GPIO_SetBits()、GPIO_ResetBits()和GPIO_WriteBit()均可单独设置引脚低电平,代码如下:
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(250);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
Delay_ms(250);
//通过强制转换为BitAction的枚举类型来直接输入高低电平
GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)0);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)1);
Delay_ms(250);
GPIO_WriteBit直接写1或0来设置电平的时候,需要将将数字强制转换成枚举类型BitAction
Bit_RESET和Bit_SET也是枚举类型BitAction
GPIO_Write(GPIOA, ~0x0040)表示将整个GPIOA寄存器进行配置,16位的数据
七、完整程序
1、点亮一个灯(低电平亮)(LED连接PA0口)
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
//使用RCC初始化GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//使用GPIO_Init函数初始化GPIO
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;//50兆hz的速度
//用结构体的函数初始化GPIO口
GPIO_Init(GPIOA, &GPIO_InitStructure);//第二个参数为指向结构体的指针,故传地址
while (1)
{
//使用输出或输入函数控制GPIO口
GPIO_ResetBits(GPIOA, GPIO_Pin_0);//0
Delay_ms(500);
GPIO_SetBits(GPIOA, GPIO_Pin_0);//1
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);//0
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);//1
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
Delay_ms(500);
}
}
2、流水灯(低电平亮)(LED连接PA0~PA6)
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while (1)
{
GPIO_Write(GPIOA, ~0x0001); //0000 0000 0000 0001 低电平点亮
Delay_ms(100);
GPIO_Write(GPIOA, ~0x0002); //0000 0000 0000 0010
Delay_ms(100);
GPIO_Write(GPIOA, ~0x0004); //0000 0000 0000 0100
Delay_ms(100);
GPIO_Write(GPIOA, ~0x0008); //0000 0000 0000 1000
Delay_ms(100);
GPIO_Write(GPIOA, ~0x0010); //0000 0000 0001 0000
Delay_ms(100);
GPIO_Write(GPIOA, ~0x0020); //0000 0000 0010 0000
Delay_ms(100);
GPIO_Write(GPIOA, ~0x0040); //0000 0000 0100 0000
Delay_ms(100);
GPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000
Delay_ms(100);
}
}
3、蜂鸣器(低电平的时候响)(蜂鸣器接口为PB12)
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
while (1)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
Delay_ms(100);
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
Delay_ms(700);
}
}
八、其他基础知识
1、A15、B3、B4三个端口默认是JTAG调试端口,如果要当作普通端口,要进行一些相关的配置
2、有源蜂鸣器:内部自带振荡器,频率固定
无源蜂鸣器:不带振荡器,要提供震荡脉冲才能发生,可以发出不同频率的声音
3、推挽输出高低电平都有驱动能力
开漏输出高电平相当于高阻态,没有驱动能力,低电平有驱动能力