定时器实现非阻塞式程序
两个按键分别控制两个LED,使其切换不同的点亮模式
按键灵敏,每次按键按下都能准确切换模式模块要高度封装,
主程序调用要简洁在任何时候模块代码都不能阻塞主程序。
Led: 熄灭-常亮-慢闪烁-快闪烁-点闪烁.
阻塞:执行某段程序时,CPU因为需要等待延时或者等待某个信号而被迫处于暂停状态一段时间,
程序执行时间较长或者时间不定
非阻塞:执行某段程序时,CPU不会等待,程序很快执行结束。
定时中断,也能达到类似单线程的效果.
外部中断很难处理按键抖动和 松手检测的问题.
推荐用定时器扫描按键.
后面还有定时器扫描按键,实现按键的双击,长按等操作.
代码部分
定时器模块 Timer.c Timer.h 每隔1ms进入中断函数一次.
#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,则表示按键按下,且当前处于刚松手的状态;
本次采样1,上次采样也是1, 表示按键没按下;
本次是0, 上次是1,表示刚按下;
本次采样0,上次采样也是0, 表示按下还没松开.
正常按键,是松手后,才执行功能. - 置键码标志位Flag, 向主程序报告此事件.
主程序判断有这个标志位了,就知道按键按下且刚松手,执行操作. - 这样既不阻塞主程序,又能消除抖动.
定时器扫描按键-多按键的思路.
- 先写一个获取键码值的子函数(非阻塞式);
几号按键按下,就返回几,没按键按下就返回0. - 定时中断,每隔20ms读取一次本次键码值 和 上次的键码值;
- 判断:
如果本次是0,上次非0, 则表示按键按下且当前处于刚松手的状态. - 置键码标志位,向主程序报告此事件.
// 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) //中断中调用函数.每隔1ms调用.
{
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);
}
}
定时器实现LED闪烁,不阻塞主线程
- 定时中断,每隔1ms 计次变量自增1;
- 计次变量计到周期值时,归零;
- 判断(定时器输出比较模式):如果计次变量小于1个比较值, 开灯; 否则,关灯.
#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计数器
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 关闭
{
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();
}
}
}