前言
本篇介绍GPIO八种工作模式的原理(简单易懂,适合初学者初学时建立简单印象框架),并通过点亮小灯来学习常用的控制GPIO的库函数。
一、GPIO是什么?
GPIO(General Purpose Input/Output,通用输入/输出) 是用于与外部设备进行数字信号交互的基本外设模块,下表是stm32f10 3c8t6的其他外设。
二、8个工作模式
1.总括
这是GPIO的基本结构,对于八种工作模式是通过一个寄存器,控制该结构的变化来实现不同的功能。
2. 四种输入
部分③是用于过滤信号的,有时接收到的型号是非数字信号,会出现部分信号似零非零,似一非一,很容易出现误判,该触发器设置了上界和下界,就能将非数字信号转换成为数字信号。简单来说这个触发器就是将模拟信号转换为数字信号的
当配置为输入模式时,输出相当于断开的,即输出模块中的P-MOS和N-MOS均是无效的
浮空输入
浮空输入就是将上图中部分②和部分①两个开关全都断开,当两个开关全都断开时,输入就会处于一种浮空的状态,输入电平极易受外界干扰而发生改变。
上拉输入
上拉输入就是将上图中部分①闭合,部分②断开,VDD接入输入部分,默认的输入信号就是高电平,即在该模式下,当接口没有信号接入时,默认输入高电平信号
下拉输入
下拉输入就是将上图中部分①断开,部分②闭合,VSS接入输入部分,默认的输入信号就是低电平,即在该模式下,当接口没有信号接入时,默认输入低电平信号
模拟输入
模拟输入时接收模拟信号,跳过触发器,即信号不经过触发器直接传入接收信号处
3.四种输出
推免模式和开漏模式
推免输出(强推输出)
推免输出时,结构中的P-MOS和N-MOS都有效,输出高电平时,P-MOS导通,接口接VDD,呈现高电平,而输出低电平时,N-MOS导通,接口介VSS,呈现低电平。因此在推免输出模式下,STM32对高低电平具有绝对的控制权,高低电平都由STM32说了算。这种模式是真正的输出了高低电平,因此高低电均有驱动能力,即有输出电流的能力,简单来说假如接口接了小灯,无论是高电平还是低电平,均有足够的电流来保证小灯的正常工作
开漏输出
开漏输出时,P-MOS是无效的,只有N-MOS有效,从而输出低电平时,N-MOS导通,直接接地,吸入电流,强驱动;当输出高电平时,因为P-MOS的无效,N-MOS的断开,此时接口处的高低电平是不确定的,接口处与结构内部是完全断开的状况(称为高阻态),电平有外部决定,因此在接口处添加一个上拉电阻,保证在接口输出不确定的情况下,得到高电平。
复用推免和复用开漏
复用开漏输出
除数据来源与开漏输出不一致,其他均与开漏模式相同
复用推免输出
除数据来源与推免输出不一致,其他均与推免模式相同
4.小小总结
八种工作模式,四种输入包含 浮空
(默认输入不确定可高可低),上拉
(默认输入时高电平),下拉
(默认输入时低电平),模拟
(不经过触发器直接接ADC),四种输出包含推免
(高低电平均有强驱动能力),开漏
(低电平具有强驱动能力,高电平需外接上拉电阻),复用开漏和复用推免
(数据源与片上外设)。在输入模式下,输出均不可用,但在输出模式下,输入是可以用的,即在输出模式下,也可同时进行输入,除了在模拟输入模式状况外,上拉输入,下拉输入,浮空输入总是可以工作 。
三、输出练习(点亮小灯)
使用PA0口,点亮小灯有(1)、(3)两种电路连接方式,(1)方式PA0口输出低电平点亮小灯,可使用开漏和推免两种方式进行控制,而方式(3)需通过PA0输出高电平点亮小灯,只能使用推免方式控制
1.配置RCC
已知STM32有诸多外设,RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
该函数是用于开启或禁用APB2总线上的外设时钟。此处需使用PA0,属于GPIOA,因此传入参数为RCC_APB2Periph_GPIOA
ENABLE
,即打开GPIOA的时钟。通过此表选择要传入的参数。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
2.配置GPIO
已知GPIO有8种工作模式,根据点亮小灯的需求,选择输出模式,因此开漏和推免都可选。配置方式是通过创建结构体对象,配置其中的属性,从而达到配置GPIO寄存器的目的。
创建结构体
GPIO_InitTypeDef GPIO_InitStructure;//创建GPIO配置对象
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//设置工作模式为推免模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//设置端口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置速度一般为50MHZ
GPIO_Init(GPIOA, &GPIO_InitStructure);//将设置好的结构体对象传入函数,对GPIO配置寄存器进行配置
3.设置输出信号
库函数设置输出信号有三种方式
GPIO_ResetBits&GPIO_SetBits
GPIO_ResetBits(GPIOA, GPIO_Pin_0);//对PA0设置低电平
GPIO_SetBits(GPIOA, GPIO_Pin_0);//对PA0设置高电平
GPIO_WriteBit
GPIO_WriteBit(GIOA, GPIO_Pin_0, Bit_RESET);//对PA0设置低电平
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);//对PA0设置高电平
GPIO_Write
uint16_t PortValLow = 0xfffe;
GPIO_Write(GPIOA, PortValLow);//对PA0设置低电平
uint16_t PortValHigh = 0x0001;
GPIO_Write(GPIOA, PortValHigh);//对PA0设置高电平
自此编译上传便可点亮小灯
4.延时闪烁
通过延时函数,添加Delay.h和Delay.c文件(源与B站江协科技学习包),调用延时函数,完成小灯的闪烁
while(1){
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);//选择三种方式之一设置低电平
Delay_ms(100);//延时100毫秒
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);//选择三种方式之一设置高电平
Delay_ms(100);
}
四、输入练习(认识四种传感器)
四中传感器的原理非常相似,大致仅是更换了N1电阻,光敏传感器N1是光敏电阻,热敏传感器N1是热敏电阻等。
以光敏电阻为例,光线越强,电阻越小。AO处电压越大,因此在AO处输出线性的模拟信号,IN+处于AO电压一致。下图图(2)也会输出一个电压,该电压可以理解为设置好的一个标准电压通过IN-输出,两个电压通过图(1)的电压比较器,IN+的电压如果小于IN-处的电压,图(1)中DO口就会输出低电平,反之输出高电平,从而将模拟信号转换为数字信号接入图(4)中进行输出
这就是四个传感器的大致原理,每个传感器有四个接口:模拟信号、数字信号、GND、VCC
5、连接方式
图(1)、(2)、(3)、(4)是按钮的接线方式,图(1)(2)都是按钮按下是输入低电平,但图(1)未接上拉电阻,浮空模式时,默认输入是不确定的,图(2)有接上拉电阻,从而在浮空模式下,默认输入是高电平;
而图(3)(4)是按钮按下是输入高电平,区别与图(1)(2)的区别一致,在浮空模式下,其默认输入不一样;
图(5)是将传感器的数字信号端接入PA0口
6、输入接收函数
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);
这两个函数是用来读取输出寄存器的值,用来查看输出寄存器输出了哪个值。第一个函数是看输出寄存器的某一位,而第二个函数是用来看整个输出寄存器的值。
7、按钮控制小灯
使用PB1和PB11两个端口作为输入端口,连接两个按钮
使用PA1和PA2两个端口作为输出端口,连接两个LED小灯
输入端口初始化
void KEY_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
检测按钮按下
uint8_t KEY1_GetNum(void)
{
uint8_t KeyNum = 0;//定义一个变量作为返回值,记录按钮状态
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)//检测按钮1按下
{
Delay_ms(20);//消除按钮按下的抖动
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);//循环检测按钮松手
Delay_ms(20);//消除松手的抖动
KeyNum = 1;
}
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
Delay_ms(20);
KeyNum = 2;
}
return KeyNum;
}
输出端口初始化
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIOInitStructure;
GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIOInitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_1;
GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIOInitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);//初始化小灯熄灭
}
按键控制小灯
当小灯熄灭时,按键按下是点亮小灯,当小灯点亮时,按键按下是熄灭小灯,实现此功能需知晓按键按下时,小灯的状态,即该接口输出了什么。刚好需要用到上文介绍的 GPIO_ReadOutputDataBit函数
void LED_Turn(uint8_t Led)//通过传入的参数判断是哪个按键按下
{
//Led
if(Led == 1)
{//按键1按下,控制小灯1
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)//获取小灯当前状态
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);//熄灭小灯
}
else{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);//点亮小灯
}
}
else{//按键2按下控制小灯2
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0)//获取小灯当前状态
{
GPIO_SetBits(GPIOA, GPIO_Pin_2);//熄灭小灯
}
else{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);//点亮小灯
}
}
}
主函数调用
uint8_t KeyNum = 0;
//按钮控制小灯
int main(void)
{
LED_Init();//初始化LED小灯
KEY_Init();//初始化按键模块
while(1)
{
KeyNum = KEY1_GetNum();//获取按键状态
if(KeyNum == 1)
{//按键1按下
LED_Turn(1);
}
if(KeyNum == 2)
{//按键2按下
LED_Turn(2);
}
}
}
8、光敏电阻控制蜂鸣器
光敏电阻初始化
void LightSensor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
蜂鸣器初始化
void Buzzer_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIOInitStructure;
GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIOInitStructure.GPIO_Pin = GPIO_Pin_1;
GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIOInitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_1);
}
光敏电阻输入信号
uint8_t LightSensor_GetNum(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
}
蜂鸣器输出信号
void Buzzer_ON(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_1);
}
void Buzzer_OFF(void)
{
GPIO_SetBits(GPIOB, GPIO_Pin_1);
}
void Buzzer_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
GPIO_SetBits(GPIOB, GPIO_Pin_1);
}
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_1);
}
}
主函数控制
int main(void)
{
Buzzer_Init();
LightSensor_Init();
uint8_t KeyNum = 1;
while(1)
{
KeyNum = LightSensor_GetNum();
if(KeyNum == 1)
{
Buzzer_ON();
}
else{
Buzzer_OFF();
}
}
}
总结
本文介绍了GPIO的八种工作模式电路结构的简单介绍,以及控制GPIO接口输入输出代码编写步骤和函数理解。