- GPIO(General-purpose input/output)通用-型输入/输出
【 1. 工作模式 】
1.1 GPIO基本结构
1.2 四种输入模式 - 读
1.2.1 输入浮空 GPIO_Mode_IN_FLOATING
- I/O 电平由外部上下拉决定,并可被CPU读到。
- 一般多用于外部按键输入。
- 上电复位后,GPIO默认为输入浮空状态。
1.2.2 输入上拉 GPIO_Mode_IPU
- 无外接上下拉时 IO 处于高电平,CPU读 IO 为高电平(这是因为内部上拉电阻处的开关闭合,电平拉高)
- PS 输入上、下拉必要性:如果没有上拉,在没有外界输入的情况下输入端是悬空的,它的电平是未知的也就是说是无法保证的,上拉就是为了保证无信号输入时输入端的电平为高电平,同样 下拉是为了保证无信号输入时输入端的电平为低电平。
1.2.3 输入下拉 GPIO_Mode_IPD
- 无外接上下拉时 IO 处于低电平,CPU读 IO 为低电平(这是因为内部下拉电阻处的开关闭合,即内部接上拉电阻,电平被拉低)
1.2.4 模拟输入 GPIO_Mode_AIN
- 模拟量转化为数字量,被CPU读到。
1.3 四种输出模式 - 写
1.3.1 开漏输出 GPIO_Mode_Out_OD
- 此时输出控制电路的作用:点击此处是有关场效应管的知识
- 令P-MOS管栅极G的电压为高电平,从而让P-MOS管截止、完全不工作。
- 选择器与N-MOS管之间的输出控制电路相当于一个反相器:写为 1/0 时,N-MOSA管栅极G处为 0/1
- 于是我们将上图简化,得到下图所示开漏输出的N-MOS门电路:
- 当 写 为 1 时,经输出控制电路内部的反向器到达栅极Gate Input 处变为 0,NMOS管不工作,此时 I/O为高阻态,电平由外部上下拉决定,并可被CPU读到;
- 当 写 为 0 时,经输出控制电路内部的反向器到达到栅极Gate Input 处变为 1,NMOS管工作,I/O被下拉成低电平,并可被CPU读到低电平。
- 开漏输出输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合做电流型的驱动,其吸收电流的能力相对较强(20mA以内)
- 一句话总结:开漏输出,写 0 输出0,写 1 输出高阻(外部加上拉电阻时,写 1 输出 1)。
1.3.2 开漏复用输出 GPIO_Mode_AF_OD
- 电路的基本原理和开漏输出类似:
- 外设模块输出为1,I/O电平由外部上下拉决定,并可被CPU读到;
- 外设模块输出为0,I/O为低电平,并可被CPU读到低电平。
1.3.3 推挽输出 GPIO_Mode_Out_PP
-
此时输出控制电路的作用:
选择器与两个MOS管之间的输出控制电路相当于一个反相器:写为 1/0 时,两个MOS管栅极处都为 0/1。
-
于是,当写为 1/0 且无外部上下拉时,I/O为 高/低 电平,并可被CPU读到 1/0 ;
- 当 写 为 1 时,反向后为0,NMOS管截止,PMOS管电流从S -> G -> D,输出为1, 推 ,灌电流
- 当 写 为 0 时,反向后为1,PMOS管截止,NMOS管电流从D -> G -> S,输出为0, 挽 ,拉电流
-
两个管子轮流导通,使其负载能力和开关速度都比普通的方式有很大的提高。
-
推挽输出的低电平为 0伏,高电平为 3.3伏。
-
一句话总结:推挽输出,写 0 输出 0,写 1 输出1(其带负载能力很大)。
1.3.4 推挽式复用输出 GPIO_Mode_AF_PP
- 电路的基本原理和推挽输出类似:
- 复用功能输出为 1/0 且无外部上下拉时,I/O为 高/低 电平,并可被CPU读到 1/0。
1.3.5 推挽输出和开漏输出在应用上的区别
1.4 三种最大翻转速度
- 2MHZ
- 10MHz
- 50MHz
【 2. 相关寄存器 】
-
STM32103ZET6共有7组GPIO(从A到G),每组GPIO有16个端口(从0到15),每组GPIO端口有以下7个寄存器,每组寄存器可以控制16个I/O端口。
GPIOx_CRL :端口配置低寄存器 GPIOx_CRH:端口配置高寄存器 GPIOx_IDR:端口输入寄存器 GPIOx_ODR:端口输出寄存器 GPIOx_BSRR:端口位设置/清除寄存器 GPIOx_BRR :端口位清除寄存器 GPIOx_LCKR:端口配置锁存寄存器
-
每个I/O端口位可以自由编程,然而I/O端口寄存器必须按32位字被访问(不允许半字或字节访问) 。所有IO口都可以作为中断输入。
2.1 端口 工作模式配置 寄存器(GPIOx_CRL , GPIOx_CRH)
- 选择GPIO工作模式
GPIOx_CRH
GPIOx_CRL
端口配置表
- 每组GPIO有16个IO口,每个IO口需要4位来控制,这4位分成两部分,每部分2位,低2位的部分用于选择输入或输出、速度,高2位的部分用于选择输入或输出的模式
- GPIOx_CRL用于控制低8个IO口,GPIOx_CRH用于控制高8个IO口。(例如:GPIOA_CRL控制PA0-PA7,GPIOA_CRH控制PA8-PA15)。
2.2 端口 输入数据 寄存器( GPIOx_IDR )
- 读取 I/O 口电平
2.3 端口 输出数据 寄存器 ( GPIOx_ODR )
- 控制 I/O 口输出
2.4 端口 位设置/清除 寄存器(GPIOx_BSRR)
- 间接地设置ODR来控制端口输出
2.5 端口 位清除 寄存器(GPIOx_BRR)
- 间接地清除ODR来控制端口输出
2.6 GPIO 使能 寄存器 (RCC_APB2ENR)
- (APB2 外设时钟使能寄存器 (RCC_APB2ENR) 32位 )
【 3. 相关函数 】
- 头文件:stm32f10x_gpio.h
- 源文件:stm32f10x_gpio.c
3.1 使能函数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx,ENABLE);
3.2 初始化函数
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
- 作用
初始化一个或多个IO口(同一组)的工作方式。
该函数主要是操作GPIO_CRL(CRH)寄存器,在上拉或者下拉的时候有设置BSRR或者BRR寄存器。 - 范例
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO参数结构体
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //选择要配置的IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
3.3 读取输入电平
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
- 作用
读取GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。 - 范例
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取PA^5的输入电平
GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有IO口输入电平
3.4 读取输出电平
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读取GPIOx组下某个引脚电平
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx); //读取GPIOx该组所有引脚输出电平
- 作用
读取GPIO的输出电平。实际操作的是GPIO_ODR寄存器。 - 范例
GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平
GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平
3.5 设置输出电平
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); //不常用
作用
- 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口输出电平。
【 4. 范例:开关灯 】
- 硬件设计1:
根据下图分析,三个按键可以设置成上拉输入模式,即未按下按键时,由于内部上拉电阻的作用,I/O口为高电平;当按键按下后,I/O口被下拉到低电平。
- 硬件设计2:
根据下图分析,两个LED可设置为推挽输出模式(即写0输出0,写1输出1),正常的让LED输出高/低即可灭/亮。
- 软件设计:
//按键控制灯的闪烁
#include "stm32f10x.h"
#include "sys.h"
#define LED0 PBout(5)// PB5
#define LED1 PEout(5)// PE5
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键0
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)//读取按键2
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP)
#define KEY0_PRES 1 //KEY0按下
#define KEY1_PRES 2 //KEY1按下
#define KEY2_PRES 3 //KEY2按下
#define WKUP_PRES 4 //KEY_UP按下(即WK_UP/KEY_UP)
/**********************************
延时函数
**********************************/
void Delay(u32 count)
{
u32 i=0;
for(;i<count;i++);
}
/**********************************
按键判断
返回按键值
mode:0,不支持连续按;1,支持连续按;
0,没有任何按键按下
1,KEY0按下
2,KEY1按下
3,KEY2按下
4,KEY3按下 WK_UP
注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!!
**********************************/
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
Delay(10);//去抖动
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(KEY2==0)return KEY2_PRES;
else if(WK_UP==1)return WKUP_PRES;
}
else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)
key_up=1;
return 0;// 无按键按下
}
/**********************************
初始化LED
**********************************/
void led_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO参数结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);//使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //选择IO: 5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //将结构体参数配置到GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB^5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //选择IO: 5
GPIO_Init(GPIOE, &GPIO_InitStructure); //将结构体参数配置到GPIOE
GPIO_SetBits(GPIOE,GPIO_Pin_5); //PE^5 输出高
}
/**********************************
初始化key
**********************************/
void key_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO参数结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能GPIOA,GPIOE时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//选择IO: 2,3,4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);//将结构体参数配置到GPIOE
//初始化 WK_UP-->GPIOA.0 下拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //选择IO:0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成输入,默认下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//将结构体参数配置到GPIOA
}
int main(void)
{
vu8 key=0;
led_init(); //LED端口初始化
key_init(); //初始化与按键连接的硬件接口
LED0=0; //先点亮红灯
while(1)
{
key=KEY_Scan(0); //得到键值
if(key)
{
switch(key)
{
case KEY2_PRES: //控制LED0翻转
LED0=!LED0;
break;
case KEY1_PRES: //控制LED1翻转
LED1=!LED1;
break;
case KEY0_PRES: //同时控制LED0,LED1翻转
LED0=!LED0;
LED1=!LED1;
break;
}
}
else Delay(10);
}
}