[Bare-metal development] EPIT timer - key debounce

In actual projects, you cannot directly debounce through delay! Here we use a timer to debounce, which is also a way for the kernel to deal with debounce.


Table of contents

1. Basic principles

1. Disadvantages of delay debounce

2. The principle of timer debounce

2. Realization of button debounce

1. Button interrupt

2. Timer interrupt

3. Additional: button/timer interrupt initialization 

1. Button initialization

2. Timer initialization


1. Basic principles

1. Disadvantages of delay debounce

When the button is pressed, there is jitter, and the delay debounce is actually no matter how many times there are jitters in the middle, all are ignored. In this case, there are too many uncertainties.

  • If the delay setting is too short, the debounce effect may not be achieved;
  • If the delay setting is too long, there may be a situation that "the first key press does not respond, but the second time it responds". This is because the delay is too long and the first press is ignored.

2. The principle of timer debounce

The timer debounce considers every jitter. When a jitter occurs at t1 , we start a timer with a period of 10 ms (10ms has basically covered all the jitter)

When the jitter is detected for the second time, it is reasonable to say that the interrupt has not been triggered at this time. We start the timer again. The same timer is used for the first time and the second time, so this time the timer will start counting again.

And so on, until the last jitter occurs, the timer is constantly being refreshed and counted, and the interrupt has never been triggered , that is, the interrupt service function has not been called.

When the last jitter occurs, it means that the jitter process has ended, and the interrupt service function will be called at this time. What the interrupt service function needs to do is the processing logic after the button is pressed.

There are two interrupts involved here, namely key interrupt and timer interrupt.

  • Key interrupt : when the interrupt is triggered, start the timer
  • Timer interrupt : When the interrupt is triggered, execute the processing logic after the button is pressed

2. Realization of button debounce

The key to the timer to realize button debounce is to set two interrupt service functions.

1. Button interrupt

Every time the key interrupt is triggered (assuming it is triggered by a falling edge), we have to restart the EPIT1 timer, and the timer will refresh the count at this time and start counting again.

void key_irqhandler(void* userParams)
{
    restart_epit(33000000/100);     // 启动定时器(每隔10ms触发一次中断)
    GPIO1_ISR |= (1 << 18);
}

The restart_epit function is in the last part of "Timer Interrupt Initialization". Before using restart_epit, you need to call the epit_init function in main.c to initialize the timer.

2. Timer interrupt

When the button shakes, the timer interrupt will not be triggered. Only when the button shakes for the last time (the button shaking process ends), the timer interrupt will be triggered. At this time, we can realize what we originally wanted to do when the button was pressed.

Suppose we want to switch the state of the LED light when the button is pressed.

void epit_irqhandler(void* userParams)
{
    stop_epit();        // 防止处理中断时,按键再次被按下,影响现有的中断处理
    if (((GPIO1_DR >> 18) & 0x01) == 0)
    {
        switch_led(epit_stat);
        epit_stat = !epit_stat;
    }
    
    EPIT1_SR |= 1;           // 清除中断标志位
}

3. Additional: button/timer interrupt initialization 

Before using the interrupt, it is necessary to initialize the interrupt first, and the initialization operation of the button and the timer is attached here.

1. Button initialization

/* 按键初始化 */
void key_init()
{
    /* 1、设置IO复用为GPIO1_IO03 */
    IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B = 0x5;

    /* 2、初始化复用引脚,设置电气属性 */
    IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B = 0xF080;

    /* 3、初始化GPIO1 */
    GPIO1_GDIR &= ~(1 << 18);

    /* 4、按键中断初始化 */
    key_int_init();
}

/* 按键中断初始化 */
void key_int_init()
{
    /* 使能GIC 中 IRQ 的某个外设对应的中断 */
    GIC_EnableIRQ(99);    // 99 表示 GPIO1_IO16 ~ 31 的中断

    // GPIO1 中断使能
    GPIO1_IMR |= (1 << 18);

    // 配置 GPIO1_IO18 中断触发方式(下降沿触发)
    GPIO1_EDGE_SEL &= ~(1 << 18);
    GPIO1_ICR2 |= (3 << 4);

    // 注册中断服务函数
    register_irqhandler(99, key_irqhandler);
}

2. Timer initialization

When the timer is initialized, the timer will not be turned on temporarily, because the timer will be started after the button interrupt is triggered.

/*
 * frac: 代表分频数
 * interval: 代表计数器的初始值
 */
void epit_init(unsigned int frac, unsigned int value)
{
    if (frac < 1 )
        frac = 1;
    else if(frac > 4096)
        frac = 4096;
    
    EPIT1_CR = 0;       // CR 寄存器清零(停止定时器)
    /* EPIT1 使能、分频、时钟源初始化 */
    EPIT1_CR |=(0xE | ((frac - 1) << 4) | (0x1 << 24));

    /* 加载寄存器 */
    EPIT1_LR = value;

    /* 比较寄存器 */
    EPIT1_CMPR = 0;

    /* EPIT1 中断使能 */
    GIC_EnableIRQ(88);

    /* 注册中断服务函数 */
    register_irqhandler(88, epit_irqhandler);

    /* 计数寄存器 */
    // EPIT1_CNR = value;

    // EPIT1_CR |= 1;          // 启动定时器
}

/*
 * 停止定时器
 */
void stop_epit()
{
    EPIT1_CR &= ~(1 << 0);
}

/*
 * 启动定时器
 */ 
void restart_epit(unsigned int val)
{
    stop_epit();
    EPIT1_LR = val;
    EPIT1_CR |= 1;          // 启动定时器
}

Guess you like

Origin blog.csdn.net/challenglistic/article/details/131381028