定時器實現(xiàn)非阻塞式程序
兩個按鍵分別控制兩個LED,使其切換不同的點亮模式
按鍵靈敏,每次按鍵按下都能準(zhǔn)確切換模式模塊要高度封裝,
主程序調(diào)用要簡潔在任何時候模塊代碼都不能阻塞主程序。
Led: 熄滅-常亮-慢閃爍-快閃爍-點閃爍.
阻塞:執(zhí)行某段程序時,CPU因為需要等待延時或者等待某個信號而被迫處于暫停狀態(tài)一段時間,
程序執(zhí)行時間較長或者時間不定
非阻塞:執(zhí)行某段程序時,CPU不會等待,程序很快執(zhí)行結(jié)束。
定時中斷,也能達到類似單線程的效果.
外部中斷很難處理按鍵抖動和 松手檢測的問題.
推薦用定時器掃描按鍵.
后面還有定時器掃描按鍵,實現(xiàn)按鍵的雙擊,長按等操作.
代碼部分
定時器模塊 Timer.c Timer.h 每隔1ms進入中斷函數(shù)一次.
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
//--------------
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
/*
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
*/
單按鍵: 定時器掃描的思路.
- 定時中斷,每隔20ms(消除抖動)讀取一次本次引腳 和 上次引腳的值.
- 判斷,
如果本次是1,上次是0,則表示按鍵按下,且當(dāng)前處于剛松手的狀態(tài);
本次采樣1,上次采樣也是1, 表示按鍵沒按下;
本次是0, 上次是1,表示剛按下;
本次采樣0,上次采樣也是0, 表示按下還沒松開.
正常按鍵,是松手后,才執(zhí)行功能. - 置鍵碼標(biāo)志位Flag, 向主程序報告此事件.
主程序判斷有這個標(biāo)志位了,就知道按鍵按下且剛松手,執(zhí)行操作. - 這樣既不阻塞主程序,又能消除抖動.
定時器掃描按鍵-多按鍵的思路.
- 先寫一個獲取鍵碼值的子函數(shù)(非阻塞式);
幾號按鍵按下,就返回幾,沒按鍵按下就返回0. - 定時中斷,每隔20ms讀取一次本次鍵碼值 和 上次的鍵碼值;
- 判斷:
如果本次是0,上次非0, 則表示按鍵按下且當(dāng)前處于剛松手的狀態(tài). - 置鍵碼標(biāo)志位,向主程序報告此事件.
// key.h key.c
#ifndef __KEY_H
#define __KEY_H
void Key_Init(void);
uint8_t Key_GetNum(void);
void Key_Tick(void);
#endif
//----------------------
#include "stm32f10x.h" // Device header
#include "Delay.h"
uint8_t Key_Num;
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 Key_GetNum(void)
{
uint8_t Temp;//中間變量
if (Key_Num)
{
Temp = Key_Num;
Key_Num = 0; //讀后清零
return Temp;
}
return 0;
}
uint8_t Key_GetState(void)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
return 1;
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
return 2;
}
return 0;
}
void Key_Tick(void) //中斷中調(diào)用函數(shù).每隔1ms調(diào)用.
{
static uint8_t Count;
static uint8_t CurrState, PrevState;
Count ++;
if (Count >= 20) //20分頻,20ms進入一次if
{
Count = 0;
PrevState = CurrState; //上次采樣值
CurrState = Key_GetState(); //本次采樣值
// 捕捉到一次按鍵,且剛松手
if (CurrState == 0 && PrevState != 0)
{
Key_Num = PrevState;//返回按鍵編碼值
}
}
}
//main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "Key.h"
#include "Timer.h"
uint8_t KeyNum;
uint8_t LED1Mode;
uint8_t LED2Mode;
uint16_t i;
int main(void)
{
OLED_Init();
LED_Init();
Key_Init();
Timer_Init();
OLED_ShowString(1, 1, "i:");
OLED_ShowString(2, 1, "LED1Mode:");
OLED_ShowString(3, 1, "LED2Mode:");
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
LED1Mode ++;
LED1Mode %= 5;
LED1_SetMode(LED1Mode);
}
if (KeyNum == 2)
{
LED2Mode ++;
LED2Mode %= 5;
LED2_SetMode(LED2Mode);
}
OLED_ShowNum(1, 3, i++, 5);//心跳打印
OLED_ShowNum(2, 10, LED1Mode, 1);
OLED_ShowNum(3, 10, LED2Mode, 1);
}
}
//1ms 定時器中斷.
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Key_Tick();
LED_Tick();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
定時器實現(xiàn)LED閃爍,不阻塞主線程
- 定時中斷,每隔1ms 計次變量自增1;
- 計次變量計到周期值時,歸零;
- 判斷(定時器輸出比較模式):如果計次變量小于1個比較值, 開燈; 否則,關(guān)燈.
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED1_SetMode(uint8_t Mode);
void LED2_SetMode(uint8_t Mode);
void LED_Tick(void);
#endif
//-------------------
#include "stm32f10x.h" // Device header
uint8_t LED1_Mode; //led1的模式
uint8_t LED2_Mode; //led2的模式
uint16_t LED1_Count; //led1計數(shù)器
uint16_t LED2_Count;
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);
}
void LED1_SetMode(uint8_t Mode)//切換模式
{
if (Mode != LED1_Mode)
{
LED1_Mode = Mode;
LED1_Count = 0;
}
}
void LED2_SetMode(uint8_t Mode)
{
if (Mode != LED2_Mode)
{
LED2_Mode = Mode;
LED2_Count = 0;
}
}
void LED1_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
void LED1_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
void LED2_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
void LED2_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
void LED_Tick(void)
{
if (LED1_Mode == 0)//led1 關(guān)閉
{
LED1_OFF();
}
else if (LED1_Mode == 1)//led1 常亮
{
LED1_ON();
}
else if (LED1_Mode == 2)//led1慢閃爍
{
LED1_Count ++;
LED1_Count %= 1000;//周期值1000,比較值500
if (LED1_Count < 500)
{
LED1_ON();
}
else
{
LED1_OFF();
}
}
else if (LED1_Mode == 3)//led1快閃:亮50ms,滅50ms
{
LED1_Count ++;
LED1_Count %= 100; //周期值100,比較值50
if (LED1_Count < 50)
{
LED1_ON();
}
else
{
LED1_OFF();
}
}
else if (LED1_Mode == 4)//led點閃,
{
LED1_Count ++;
LED1_Count %= 1000;//周期值1000,比較值100
if (LED1_Count < 100)
{
LED1_ON();
}
else
{
LED1_OFF();
}
}
if (LED2_Mode == 0)
{
LED2_OFF();
}
else if (LED2_Mode == 1)
{
LED2_ON();
}
else if (LED2_Mode == 2)
{
LED2_Count ++;
LED2_Count %= 1000;
if (LED2_Count < 500)
{
LED2_ON();
}
else
{
LED2_OFF();
}
}
else if (LED2_Mode == 3)
{
LED2_Count ++;
LED2_Count %= 100;
if (LED2_Count < 50)
{
LED2_ON();
}
else
{
LED2_OFF();
}
}
else if (LED2_Mode == 4)
{
LED2_Count ++;
LED2_Count %= 1000;
if (LED2_Count < 100)
{
LED2_ON();
}
else
{
LED2_OFF();
}
}
}