zoukankan      html  css  js  c++  java
  • MCU软件最佳实践——独立按键

    1. 引子

    在进行mcu驱动和应用开发时,经常会遇到独立按键驱动的开发,独立按键似乎是每一个嵌入式工程师的入门必修课。笔者翻阅了许多书籍(包括上大学时候用的书籍)同时查阅了网上许多网友的博客,无一例外,清一色采用检测、延时、再检测,千篇一律,似乎是按键过于简单,因此没有人愿意推陈出新。
    本文介绍一种独立按键驱动,消除抖动不采用软件延时(让CPU死等,mcu无休止的运行_nop_()函数,在稍微复杂应用场景,cpu时间捉襟见肘的场合,一个独立按键便让CPU如此耗力,是非常不明智的)。本驱动短小精悍,可以是适用于裸机应用,也可方便移植到rtos。

    2. 思路

    分析独立按键按下的过程,分为稳定状态非稳定状态
    根据经验值,抖动消除时间为10ms左右;
    累计检测到超过10ms按下状态,视为按键按下;
    累计检测到超过10ms弹起状态,视为按键弹起;

    2.1 按键驱动key.c

    /**
     * @file: key.c
     * @brief: 按键驱动实现
     */
    #include <reg52.h>
    #define STAT_RELEASED 	(1)
    #define STAT_PRESSED  	(0)
    unsigned char (*keyfn)(void);// 应用提供本函数实现,返回1表示读到高电平,返回0表示读到低电平
    unsigned char flg_down;
    unsigned char flg_up;
    unsigned char stat; // 默认是弹起状态
    unsigned char timer;
     
    void key_init(unsigned char (*fn)(void))
    {
    	stat = STAT_RELEASED;
    	keyfn = fn;
    }
     
    void key_scan(void)
    {
    	if(keyfn && !keyfn()) // 有按键按下
    	{
    		if(timer < 1)  // 非稳态
    		{
    			timer++;
    			return;
    		}
    		if(stat == STAT_RELEASED)
    		{
    			stat = STAT_PRESSED;
    			// post down event
    			flg_down = 1;
    		}
    	}
    	else
    	{
    		if(timer > 0) // 非稳态
    		{
    			timer--;
    			return;
    		}
    		if(stat == STAT_PRESSED)
    		{
    			stat = STAT_RELEASED;
    			// post up event
    			flg_up = 1;
    		}
    	}
    }
    

    2.2 按键对外接口key.h

    /**
     * @file:key.h
     * @brief:按键驱动对外接口
     */
     /**
      * @function: key_init
      * @param fn: 函数指针,有返回值,读到高电平返回1,读到低电平返回0
      * @brief: 按键模块初始化函数,传递io电平读取函数到按键模块
      */
    extern void key_init(unsigned char (*fn)(void));
    extern unsigned char flg_down;
    extern unsigned char flg_up;
    extern void key_scan(void); // 10ms周期调用
    

    3 实验

    假设单片机P2.1口为按键;
    按下和弹起时,串口打印输出

    为了方便实验,添加串口驱动、定时器驱动程序。

    3.1 串口驱动

    /**
     * @file: uart.c
     * @brief: 串口驱动,波特率9600bps,10bit模式
     *
     */
    #include <reg52.h>
     
    void uart_init(void)
    {
    	SCON = 0X50; // 10bit 可变波特率模式
    	
    	//T1: SM1SM0=10,8bit auto reload,波特率9600bps
    	TMOD = (TMOD & 0X0F) | (1 << 5);
    	TH1 = TL1 = 0XFD;
    	TR1 = 1;
    	
    	ES = 1;
    	EA = 1;
    	TI = 1; // start transmit if using putchar provided by c51 lib
    }
     
    void uart_isr(void) interrupt 4
    {
    	if(RI)
    	{
    		RI = 0;
    	}
    	if(TI)
    	{
    	}
    }
    
    /**
     * @file: uart.h
     */
    // 串口模块
    extern void uart_init(void);
    

    3.2 定时器驱动

    /**
     * @file: timer0.c
     * @brief: 产生10ms事件
     */
    #include <reg52.h>
    int systick;
    unsigned char flg_10ms;
    unsigned char flg_50ms;
    unsigned char flg_sec;
    void timer_init(unsigned char ms)
    {
    	TMOD = (TMOD & 0XF0);  // 模式0:13bit 定时器模式,最大计数值8192
    	TH0 = (8192 - ms * 1000) / 32; // TH0的8位保存13bit初值的高8bit
    	TL0 = (8192 - ms * 1000) % 32; // TL0的低5位用来存储13bit初值得低5bit
    	
    	TR0 = 1;
    	
    	ET0 = 1;
    	EA = 1;
    }
     
     
    void timer_isr(void) interrupt 1
    {
    	TR0 = 0;
    	timer_init(1);
    	
    	systick++;
    	if(systick % 10 == 0)
    	{
    		flg_10ms = 1;
    		if(systick % 50 == 0)
    		{
    			flg_50ms = 1;
    			if(systick % 1000 == 0)
    			{
    				flg_sec = 1;
    			}
    		}
    	}
    }
    
    /**
     * @file:timer0.h
     */
    // 定时器模块
    extern unsigned char flg_10ms;
    extern unsigned char flg_50ms;
    extern unsigned char flg_sec;
    extern void timer_init(unsigned char ms);
    

    3.3 编写应用

    1. 初始化串口、定时器、按键模块
    2. 10ms为周期扫描按键
    3. 监测按键事件并处理
    #include <reg52.h>
    #include <timer0.h>
    #include <uart.h>
    #include <key.h>
    #include <stdio.h>
    
    sbit button = P2^1;
     
    unsigned char kfn(void)
    {
    	return button? 1: 0;
    }
     
    void main(void)
    {
    	uart_init();
    	timer_init(1);
    	key_init(kfn);
    	while(1)
    	{
    		if(flg_10ms)
    		{
    			flg_10ms = 0;
    			key_scan(); // 10ms扫描1次
    		}
    		if(flg_down)
    		{
    			flg_down = 0;
    			printf("key pressed
    ");
    		}
    		if(flg_up)
    		{
    			flg_up = 0;
    			printf("key released
    ");
    		}
    	}
    }
    

    image

  • 相关阅读:
    ES6 新属性 Symbol
    box-shadow 属性详解
    在vue 中 使用 tinymce编辑器
    var let const 结合作用域 的探讨
    防抖和节流在vue中的应用
    分享几个按钮样式
    队列学习
    栈的学习
    Object—常用的遍历
    从零认识Java Package
  • 原文地址:https://www.cnblogs.com/uil2liu/p/14699669.html
Copyright © 2011-2022 走看看