题目要求
在蓝桥杯嵌入式第九届的省赛题出现了这样一个功能要求,每短按B3键一次,数字递增一次;按住B3键超过0.8s,则数字快速递增,直到松开。
思路过程
最初看到这道题的时候,我想的是在Key_Scan()函数里面加入很多个时间很短的延时,并且每次在延时的后面有一个判断,如果说还在按下就继续加,如果不按下就退出。但是这样操作会造成很多的问题,第一是太浪费资源了,延时的时候除了中断什么都不能干;第二是不准确不能保证是0.8s,因为现在是机审所以要严谨一些;第三是真的太蠢了!!!后面我自问了我一下,可不可以在万能的定时中断里做文章,答案是可以的,并且可以说非常的完美(当然是自认为,哈哈)。
解决过程
这个需要用到32的两个东西,一个是定时器,另外一个是按键。废话不多说直接上文件!
anjian.c
#include "anjian.h"
_Bool Key_Flag=1;
void Key_Init(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
u8 Key_Scan(void){
if((Key1==0||Key2==0||Key3==0||Key4==0)&&Key_Flag){
Delay_Ms(10);
Key_Flag=0;
if(Key1==0) return 1;
else if(Key2==0) return 2;
else if(Key3==0) return 3;
else if(Key4==0) return 4;
}
else if(Key1==1&&Key2==1&&Key3==1&&Key4==1) Key_Flag=1;
return 0;
}
注意
- Key_Init()这就是一个初始化,自己去做就可以了,不多讲。
- Key_Scan()这个函数的实现应该很多人都清楚了,就是做到每按一次只加一次,无论你按得时间有多长永远只会返回一次真实按键值,那是因为有Key_Flag这个标志位的存在。具体因为是你第一次按下的时候,Key_Flag的值是1,然后就是判断查找是那一个按键,然后返回相应的数值并且对标志位Key_Flag清0。如果说你的手还在按个按键,开始第二次进入这个函数,这个时候由于Key_Flag是0就不能进入到后面的判断按键和返回值了,只能返回0代表没有按下。只有当你按键全部松开的时候,变量Key_Flag才可以再次置1,才可以再一次进入到判断哪个按键,并且返回相应的值。
anjian.h
#ifndef __ANJIAN_H
#define __ANJIAN_H
extern _Bool Key_Flag;
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#define Key1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define Key2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define Key3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define Key4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)
void Key_Init(void);
u8 Key_Scan(void);
#endif
timer.c
#include "timer.h"
void Timer2_Init(void){
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 999;
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
u32 Timer2_AnjianFlag=0;
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
if(Key2==0){
Timer2_AnjianFlag++;
if(Timer2_AnjianFlag>=800){
Key_Flag=1; //这个变量就是Key_Scan()里面的标志位
}
}
else if(Key1==1&&Key2==1&&Key3==1&&Key4==1){
Timer2_AnjianFlag=0;
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
注意
- Timer2_Init()函数是定时器2的定时模式,定时为每1ms进一次中断。这个很简单,我就不多讲了。
- 在定时器中断函数里,有个变量很重要就是Timer2_AnjianFlag,他是用来记录你按下按键的时间,如果超过0.8s,那么以后每1ms进入中断都会令Key_Scan()里的标志位Key_Flag=1,相当于把那道每按一次才能加一次的门槛给弄走了,现在是每次进入Key_Scan()函数都可以进行按键的判断了。这样就实现了长按连续加,短按就加一次。
timer.h
#ifndef __TIMER_H
#define __TIMER_H
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "anjian.h"
void Timer2_Init(void);
#endif