Timer Menu

Thực tế đưa ra tình huống sau:

Một sản phẩm của bạn chỉ sử dụng 1 nút nhấn, muốn dùng để thay đổi giữa các chế độ theo dạng:

Hoặc nhiều nhiều những ý tưởng khác dạng như thế bạn có thể nghĩ ra. Giải pháp là đo thời gian nhấn nút qua timer để MCU phân biệt được chế độ mà bạn set cho nó là gì.

Mã nguồn dưới đây thực hiện trên KIT LaunchPad của Texas Instrument, MCU MSP430G2xxx.

/********************************
LaunchPad KIT
Author: TrungThanh
Website: thanhnt.com

Description:
Menu thay đổi dựa trên thời gian nhấn giữ phím menu. Thông báo thông qua số lần nháy LED.
Sử dụng WatchDog Timer. Có thể chuyển qua dùng Timer bình thường cũng được.
*********************************/
#include <msp430g2553.h>

#define set |=
#define clr &=~
#define tgl ^=

#define SETTING 1
#define RUNNING 2
#define CLEAR 3

#define BUTTON_MENU BIT3
#define LED_RED BIT0

unsigned int TACounter = 0;
static unsigned char Mode = RUNNING;

void Blink_Led(unsigned int times, unsigned char Port);

int main(void)
{
    WDTCTL = WDTPW + WDTHOLD;               // Stop watchdog timer

    if (CALBC1_1MHZ==0xFF)                  // If calibration constant erased
    {
        while(1);                           // do not load, trap CPU!!
    }
    DCOCTL = 0;                             // Select lowest DCOx and MODx settings
    BCSCTL1 = CALBC1_1MHZ;                  // Set DCO to 1MHz
    DCOCTL = CALDCO_1MHZ;

    P1DIR set LED_RED;
    P1OUT clr LED_RED;

    //Setup Menu Button
    P1IES set BUTTON_MENU;
    P1IFG clr BUTTON_MENU;
    P1IE set BUTTON_MENU;

    __bis_SR_register(LPM0_bits + GIE);

    while(1)
    {
        if(Mode == RUNNING)
        {
            //Do some thing...
        }
        else if(Mode == SETTING)
        {
            //Do some thing...
        }
        else if(Mode == CLEAR)
        {
            //Do some thing...
        }
        else
        {
            //Do some thing...
        }
    }
}

//Port1 Interrupt
#pragma vector = PORT1_VECTOR
__interrupt void port1_isr(void)
{
    if((P1IFG & BUTTON_MENU) == BUTTON_MENU) //Menu button pressed
    {
        WDTCTL = WDT_MDLY_0_5;        // Setup 0.5ms
        IE1 |= WDTIE;                 // Enable WDT interrupt
        P1IFG clr BUTTON_MENU;        // Clear Interrupt Flag
        return;
    }

    P1IFG clr BUTTON_MENU;
}

//timer interrupt
#pragma vector=WDT_VECTOR  
__interrupt void watchdog_timer(void)
{
    if(!(P1IN & BUTTON_MENU)) //Check Menu button is pressing
    {
        TACounter++;
        if(TACounter == 2000) //Stop Setting manual (After hold 1s)
        {
            Blink_Led(1, LED_RED); //Blink 1 time
            Mode = RUNNING;
        }
        if(TACounter == 5000) //Start Setting Mode (After hold 2.5s)
        {
            Blink_Led(2, LED_RED); //Blink 2 times
            Mode = SETTING;
        }
        else if(TACounter > 10000) //Clear command memory (After hold >= 5s)
        {
            Blink_Led(5, LED_RED); //Blink 5 times
            TACounter = 0;
            Mode = CLEAR;
            IE1 clr WDTIE;         //Stop WDT interrupt
            WDTCTL = WDTPW + WDTHOLD; //Stop WatchDog Timer
        }
    }
    else {TACounter = 0; IE1 clr WDTIE; WDTCTL = WDTPW + WDTHOLD; }
}

void Blink_Led(unsigned int times, unsigned char Port)
{
  for(int a = 0; a < times; a++)
  {
    P1OUT set Port;
    __delay_cycles(100000);
    P1OUT clr Port;
    __delay_cycles(200000);
  }
}