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\r\n");
}
if(flg_up)
{
flg_up = 0;
printf("key released\r\n");
}
}
}

MCU软件最佳实践——独立按键的更多相关文章

  1. MCU软件最佳实践——矩阵键盘驱动

    1.矩阵键盘vs独立按键 在mcu应用开发过程中,独立按键比较常见,但是在需要的按键数比较多时,使用矩阵键盘则可以减少io占用,提高系统资源利用率.例如,某mcu项目要求有16个按钮,如果采用独立按键 ...

  2. MCU软件最佳实践——使用printf打印数据

    在mcu上开发应用时,使用串口打印调试信息是最常用的调试手段之一.printf是c标准库提供的函数,可以方便输出格式化的信息.但针对不同的mcu芯片,printf函数要能正常工作,需要做一些移植和适配 ...

  3. 敏捷遇上UML-需求分析及软件设计最佳实践(郑州站 2014-6-7)

      邀请函: 尊敬的阁下:我们将在郑州为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实 ...

  4. Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全

    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全 1. #====提升抽象层次1 2. #----使用通用单词1 3. #===使用术语..1 4.  ...

  5. 基于开源软件在Azure平台建立大规模系统的最佳实践

    作者 王枫 发布于2014年5月28日 前言 Microsoft Azure 是微软公有云的唯一解决方案.借助这一平台,用户可以以多种方式部署和发布自己的应用. 这是一个开放的平台,除了对于Windo ...

  6. 移动App測试实战:顶级互联网企业软件測试和质量提升最佳实践

    这篇是计算机类的优质预售推荐>>>><移动App測试实战:顶级互联网企业软件測试和质量提升最佳实践> 国内顶级互联网公司測试实战经验总结.阿里.腾讯.京东.携程.百 ...

  7. Atitit.软件开发概念说明--io系统区--特殊文件名称保存最佳实践文件名称编码...filenameEncode

    Atitit.软件开发概念说明--io系统区--特殊文件名称保存最佳实践文件名称编码...filenameEncode 不个网页title保存成个个文件的时候儿有无效字符的问题... 通常两个处理方式 ...

  8. 读《软件需求最佳实践》YOUGAN

    这几天在看<软件需求最佳实践>作者徐锋老师的软件需求培训,三天的课程,虽然原来对需求也关注了很多,自己也做过需求分析和开发的工作,但是这次培训感觉收获还是很多.三天的培训先做个记录,后续多 ...

  9. MCU 51-4 独立按键&编码按键

    独立按键: 按键的按下与释放是通过机械触点的闭合与断开来实现的,因机械触点的弹性作用,在闭合与断开的瞬间均有一个抖动的过程,抖动必须清除. 按键按下一次,数码管数值加1: #include<re ...

随机推荐

  1. mysql联合索引阻碍修改列数据类型:BLOB/TEXT column 'name' used in key specification without a key length

    今天在项目中mysql表中有一个字段数据类型为varchar,长度不够需要换为text类型 当时表是已经存在的表, CREATE TABLE `table_aaa` ( `id` int NOT NU ...

  2. LuoguP6553 Strings of Monody 题解

    Content 给定一个长度为 \(n\) 的字符串 \(s\)(仅包含 \(1,4,5\) 三种字符,\(n\) 在本题中无需输入),有 \(m\) 个操作,每次操作给定两个整数 \(l,r\),再 ...

  3. Pointcut 表达式

    AOP 概念篇 今天介绍 Pointcut 的表达式 通配符 常见的通配符如下 .. 含义一:方法表达式中.代表任意数量的参数 @Service public class HelloService { ...

  4. AcWing 1204. 错误票据

    题目: 某涉密单位下发了某种票据,并要在年终全部收回. 每张票据有唯一的ID号. 全年所有票据的ID号是连续的,但ID的开始数码是随机选定的. 因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成 ...

  5. 再谈多线程模型之生产者消费者(总结)(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...

  6. 【LeetCode】264. Ugly Number II 解题报告(Java & Python)

    标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ https://leetcode.com/prob ...

  7. 【LeetCode】72. Edit Distance 编辑距离(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 记忆化搜索 动态规划 日期 题目地址:http ...

  8. 第四十五个知识点:描述一些对抗RSA侧信道攻击的基础防御方法

    第四十五个知识点:描述一些对抗RSA侧信道攻击的基础防御方法 原文地址:http://bristolcrypto.blogspot.com/2015/08/52-things-number-45-de ...

  9. 编写Java程序,以树形结构显示国家-直辖市/省/州信息

    返回本章节 返回作业目录 需求说明: 以树形结构显示国家-直辖市/省/州信息 实现思路: 创建显示树形结构的类Tree,在该类中定义Map类型的全局实例属性countryMap,该Map集合用于存放所 ...

  10. IdentityServer4 综合应用实战系列 (一)登录

    这篇文章主要说登录,这里抛开IdentityServer4的各种模式,这里只说登录 我们要分别实现 4中登录方式来说明,  IdentityServer4本地登陆 . Windows账户登录(本地的电 ...