文章目录
- 前言
- 模块介绍
- 原理图
- 编程思路
- 检测IO口的电平
前言
昨天,通过配置通用输出模式,实现了LED灯的点亮、熄灭以及流水等操作,解决了通用输出的问题,今天我们再借用最常见的输入模块,按键来实现一个按键控制LED的功能,重点是配置GPIO为输入模式,以及如何检测GPIO的输入电平。
模块介绍
原理图
笔者用的这款最小系统有三个独立按键,可以操作,首先,第一步还是看原理图来确定我们需要使用的端口和管脚,可以看出K_UP使用的是PA0、K0使用的是PE4、KEY1使用的是PE3。
注意观察这三个按键的电路,其中KEY0和KEY1是没有上拉电阻的,只有按下按键直接接地这一个电平模式,这个我们在前面讲解GPIO模式的时候提到过,如果没有外部上拉的电路,想要实现高低电平的检测需要在内部编程实现上下拉,这两个按键就是需要使用到内部上拉,使得默认PE4、PE3端口默认是高电平,也就是1,只有按键按下,才会被拉到低电平,也就是0。
而K_UP,刚好相反,只有上拉电路,按下按键是高电平,不按下的时候应该要其默认状态是低,也就是说需要我们为其配置下拉。
编程思路
在看清楚检测原理后,就需要理清编程思路,根据昨天的按键技巧来,首先需要新建文件,命名保存key.c存在src文件夹下,key.h存在inc文件夹下,然后将Key.c添加到工程,再然后是定义头文件,编写初始化函数。
编写初始化代码:
伪代码:
①编写注释:
/***************************************************************************
*函数名 :Key_Init
*函数功能 :按键所用的管脚的初始化配置
*函数参数 :无
*函数返回值:无
*函数描述 :
KEY_UP------PA0------通用输入模式,默认状态采取内部下拉,按下按键为高电平
K0----------PE4------通用输入模式,默认状态采用内部上拉,按下按键为低电平
K1----------PE3------通用输入模式,默认状态采用内部上拉,按下按键为低电平
***************************************************************************/
②初始化函数
void Key_Init(void)
{
③使能对应端口的时钟,有两个,一个是GPIOA(昨天用过),一个是GPIOE;GPIOA对应第0位,GPIOE对应第4位。(先在数据手册查其挂接的时钟总线,然后再再第六章RCC找到对应使能进行配置)
④设置对应管脚的模式,为通用输入模式,分两组分别配置,A0:应该配置GPIOA的MODER 0 1两位,写入00;E3E4对应GPIOE的MODER的9 8 7 6 位,也都应该写入0000;
⑤设置上下拉,其中PA0设置为下拉模式,应该对GPIOA的PUPDR 的1 0两位写入10;PE4,PE3则应该将GPIOE的PUPDR 寄存器的9 8 7 6 位写入0101。
}
好了,可以发现整个配置过程比昨天的输入配置稍微简单一点,而且昨天输出使用的寄存器在按键输入上都是没有用上的。
接下来来看看代码吧。
//注释
void Key_Init(void)
{//打开AHB1上GPIOA端口RCC->AHB1ENR |= (1<<0);//打开GPIOE端口对应的AHB1时钟RCC->AHB1ENR |= (1<<4);//配置GPIOA0为通用输入模式GPIOA ->MODER &=~(3<<0);//清0 GPIOA_MODER寄存器为00通用输入模式GPIOA ->PUPDR &=~(3<<0);//清0 GPIOA_PUPDR寄存器为00 浮空GPIOA ->PUPDR|=(1<<1);//清0 GPIOA_PUPDR寄存器为10 下拉GPIOE->MODER &= ~(0XF<<6);//通用输入GPIOE->PUPDR &= ~(0XF<<6);//清零GPIOE->PUPDR |= (0X5<<6);//写入0101配置为上拉模式
}
检测IO口的电平
在GPIO做输出的时候,是通过对对应端口的ODR寄存器的对应位进行写0与写1来实现输出低电平和高电平的,很明显,如果需要获取GPIO的输入状态,肯定只能采取读的方式,那么,该怎么读取呢,参照输出,肯定会有一个对应的输入数据寄存器用来获取GPIO的状态,前面介绍GPIO寄存器的时候提到过一个叫做IDR的寄存器,它的作用就是存储GPIO的高低电平的。也就是说,在获取输入信号时,需要直接操作的就是这个IDR寄存器的对应位。
其中Key_Up使用的是GPIOA->IDR的第0位;
K0使用的是GPIOE->IDR的第四位;K1使用的是GPIOE->IDR的第三位;
可以发现这个寄存器本身就是只读的,所以在编程的时候就不能对其使用‘=‘赋值语句。
由于需要获取IDR对应位的高和低,所以考虑使用 & 操作,在对应位 & 上1,当寄存器内部对弈位是1则输出1;当寄存器内部对应位是0则输出0;
具体的实现方式如下:
#define KEY_UP (GPIOA->IDR&(1<<0))//Key_Up的状态
#define KEY0 (GPIOE->IDR&(1<<3)) //KEY0的状态
#define KEY1 (GPIOE->IDR&(1<<4)) //KEY1的状态
采用宏定义的方式,这样通过条件判断KEY1、KEY0、KEY_UP是否为真即可知道按键是否按下,其中由于KEY1、KEY0是默认上拉,未按下时是高电平此时KEY1与KEY0是非0,只有按下按键才是低电平,此时KEY1和KEY0是0;而KEY_UP默认是下拉状态,默认是低电平,也就是KEY_UP是0;只有当按键按下,GPIO才变成高电平,KEY_UP是非0。
宏定义记得放回到Key.h的头文件中。
然后在主函数进行调用(记得添加Key.h进入main.h)。
1.检测按键状态,肯定是需要实时刷新才能检测到,因此有关判断必须放在While(1)主循环中,而不是上方的单次运行区;
2.三个按键的检测原理是不同的,KEY0与KEY1是需要检测低电平,所以判断按下的语句是if(!KEY0)和if(!KEY1);而KEY_UP需要检测的是高电平,所以语句是if(KEY_UP);
3.为了看见效果,需要定义三个变量,Key_Up、k0、k1三个变量(使用ST-LINK仿真,用iWatch查看数据,当然也可以使用LED灯);
4.使用一个模块之前一定要在初始化区调用对应模块的初始化函数。