Warning:寫作不易,請勿轉載,感謝。
題目要求
在藍橋杯嵌入式第九屆的省賽題出現了這樣一個功能要求,每短按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(u8 mode);
#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