定时器实现非阻塞式程序

定时器实现非阻塞式程序

两个按键分别控制两个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();
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 一段非常通透的话:“你不需要让所有的人都喜欢你,重要的是,你要始终喜欢你自己。 遇到知己,要珍惜相遇,常来常往;遇...
    佳依我心阅读 35评论 0 1
  • 巴西嘉宾:你长得不像主播,像翻译。 董宇辉:像翻译也行,至少把中文的美传出去。 哈哈。 宇辉是个英文好的主播。
    慕容小微阅读 1,045评论 0 13
  • 不浮不躁, 不徐不疾, 不争不抢, 不惧寒意渐深, 不惧霜露渐浓。 沏一壶热茶敬过往, 点一柱清香,迎来日, 愿欢...
    王室之兰阅读 31评论 0 0
  • 黄生借书说 清·袁枚《小仓山房文集》 【原文】 黄生允修借书。随园主人授以书而告之曰:“书非借不能读也。子不闻藏书...
    平平静静哒阅读 525评论 0 1
  • 有些人整天地为着欲望活着,他就是欲望的奴隶。
    20adc56248ba阅读 27评论 0 0

友情链接更多精彩内容