嵌入式设计与开发项目-独立按键扫描程序设计
一、实现的功能
- ①单击按键PB1控制LED1 -> LED8移动一位点亮,长按按键PB1会控制LED1 -> LED8移动两位点亮,如果超出LED8,重新从LED1重新开始移位点亮;
- ②单击按键PB2控制LED8 -> LED1移动一位点亮,长按按键PB2会控制LED8 -> LED1移动两位点亮,如果超出LED1,重新从LED8重新开始移位点亮;
- ③长按PB3,蜂鸣器一直打开,释放PB3,蜂鸣器关闭。
二、根据功能实现代码
1、主文件main.c
#include"key.h"
#include"led.h" //包含LED与key头文件
unsigned char ucLed =1; //亮灯标志位
unsigned char ucKey_Long; //按键长按标志位
unsigned long ulTick_ms,ulKey_Time; //记录时间标志位
void KEY_Proc(void); //声明按键实现功能函数
int main(void)
{
SysTick_Config(72000);
KEY_Init();
LED_Init();
BUZ_Init(); //初始化key、led、buz外设
while(1)
{
KEY_Proc();
LED_Disp(ucLed);
}
}
void KEY_Proc(void)
{
unsigned char ucKey_Val; //定义一个局部变量保存按键值
ucKey_Val = KEY_Scan();
if(ucKey_Val !=ucKey_Long)
{
ucKey_Long = ucKey_Val; //把当前保存的按键值保存到ucKey_Long中
ulKey_Time = ulTick_ms; //把当前运行的时间值保存到ulKey_Time 中
}
else
ucKey_Val = 0; //如果ucKey_Val 和ucKey_Long相等,则把ucKey_Val 置0,重新扫描按键值
if(ucKey_Val == 1) //B1短按
{
ucLed <<=1;
if(ucLed ==0)
ucLed = 1;
}
if(ucKey_Val == 2) //B2短按
{
ucLed >>=1;
if(ucLed ==0)
ucLed =0x80; //重新从LED8开始移位
}
if(ucKey_Long == 1) //B1长按
{
if(ulTick_ms - ulKey_Time >800)
{
ulKey_Time = ulTick_ms;
ucLed <<= 2;
if(ucLed ==0)
ucLed=1;
}
}
if(ucKey_Long == 2) //B2长按
{
if(ulTick_ms - ulKey_Time >800)
{
ulKey_Time = ulTick_ms;
ucLed >>= 2;
if(ucLed ==0)
ucLed=0x80;
}
}
if(ucKey_Long ==3) //B3长按
GPIO_ResetBits(GPIOB,GPIO_Pin_4);
else
GPIO_SetBits(GPIOB,GPIO_Pin_4);
}
//SysTick中断处理
void SysTick_Handler(void)
{
ulTick_ms++; //进行定时计数
}
主函数分析:❤️ ❤️ ❤️
- 需要添加一个长按和长按时间全局标志位的定义,方便判断按键是否长按;
- 在主函数main后面定义的KEY_Proc()函数,需要在main之前进行声明,否则无法使用;
- 第27行的if(ucKey_Val !=ucKey_Long)判断当前按下的值是否与上一次保存的按键值相等。如果相等则把按键值置0,根据ucKey_Long来判断是否属于长按;否则ucKey_Long保存ucKey_Val的值以及保存时间,执行当前ucKey_Long扫描到的按键;
- 以800ms作为按键长按的判断;
2、独立按键头文件“key.h”
#include "stm32f10x.h"
void KEY_Init(void);
unsigned char KEY_Scan(void);
void Delay_KEY(unsigned int ms);
3、独立按键源文件“key.c”文件
#include"key.h"
//KEY接口初始化
void KEY_Init(void)
{
//初始化结构体
GPIO_InitTypeDef GPIO_InitStruct;
//使能GPIOA、GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//配置PA0和PA8引脚参数,并设置为浮空输入(复位状态,可不配置)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置PB1和PB2引脚参数,并设置为浮空输入(复位状态,可不配置)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
//KEY扫描
unsigned char KEY_Scan(void)
{
unsigned char ucKey_Val = 0;
//判断B1、B2按键是否按下
if(~GPIO_ReadInputData(GPIOA) & 0X101) //GPIO有16个位,每个位代表一个引脚
{
//延时10ms消抖
Delay_KEY(10);
if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))
ucKey_Val = 1;
if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8))
ucKey_Val = 2;
}
//判断B1、B2是否按下
else if(~GPIO_ReadInputData(GPIOB) & 6) //6=0x0006代表一个PB1和PB2引脚
{
//延时10ms消抖
Delay_KEY(10);
if(!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1))
ucKey_Val = 3;
if(!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2))
ucKey_Val = 4;
}
return ucKey_Val;
}
//延时函数
void Delay_KEY(int ms)
{
unsigned int i,j;
for(i=0;i<ms;i++)
for(j=0;j<7992;j++); //SYSCLK = 72MHz,延时毫秒
}
简要分析:❤️ ❤️
- PA0、PA8、PB1、PB2连接着四个按键,复位状态为浮空输入模式,可省略配置GPIO参数的步骤,但需要使能端口对应的时钟;
- ~GPIO_ReadInputData(GPIOA) & 0x101判断PA0与PA8是否接收到按键输入的的低电平,通过取反再和0x101进行与运算,即可判断按键是否按下(0x101=0000 0001 0000 0001);
- Delay_KEY()延时函数是根据单片机的晶振进行粗略延时的;
- 按键按下时需要延时一段时间进行消抖,否则按键不能灵活使用;
三、实现功能过程的注意与学习点
1、注意点
- 在按键长按时,都会先执行一次短按的程序,然后再进行长按操作(可优化);
- 每个GPIO有16个位,每个位代表一个引脚的电平状态;
2、学习的知识点
- 通过GPIO寄存器的数值取反再和对于的位进行与运算判断按键是否按下。
- 定义长按标志位ucKey_Long保存上一次按键的值,再与当前值进行判断,然后再根据800ms的间隔判断是否属于长按。
- 根据单片机系统SYSCLK的晶振的频率进行粗略的延时。
❤️ ❤️ ❤️ ❤️ ❤️ ❤️