定時器實現(xiàn)非阻塞式程序

定時器實現(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();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一段非常通透的話:“你不需要讓所有的人都喜歡你,重要的是,你要始終喜歡你自己。 遇到知己,要珍惜相遇,常來常往;遇...
    佳依我心閱讀 43評論 0 1
  • 巴西嘉賓:你長得不像主播,像翻譯。 董宇輝:像翻譯也行,至少把中文的美傳出去。 哈哈。 宇輝是個英文好的主播。
    慕容小微閱讀 115評論 0 13
  • 不浮不躁, 不徐不疾, 不爭不搶, 不懼寒意漸深, 不懼霜露漸濃。 沏一壺?zé)岵杈催^往, 點一柱清香,迎來日, 愿歡...
    王室之蘭閱讀 36評論 0 0
  • 黃生借書說 清·袁枚《小倉山房文集》 【原文】 黃生允修借書。隨園主人授以書而告之曰:“書非借不能讀也。子不聞藏書...
    平平靜靜噠閱讀 102評論 0 1
  • 有些人整天地為著欲望活著,他就是欲望的奴隸。
    20adc56248ba閱讀 29評論 0 0

友情鏈接更多精彩內(nèi)容