打印
[其它]

HT合泰单片机入门教程(第三章 按键)

[复制链接]
935|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
妇女半边天|  楼主 | 2022-7-21 09:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 eltonchang2001 于 2022-7-21 11:56 编辑

前言
在机械按键的触点闭合和断开时,都会产生抖动,为了保证系统能正确识别按键的开关,就必须对按键的抖动进行处理。 按键的抖动对于人类来说是感觉不到的,但对单片机来说,则是完全可以感应到的,而且还是一个很“漫长”的过程,因为单片机处理的速度在“微秒”级,而按键抖动的时间至少在“毫秒”级。 单片机如果在触点抖动期间检测按键的通断状态,则可能导致判断出错,即按键一次按下或释放被错误地认为是多次操作,从而引起误处理。因此,为了确保单片机对一次按键动作只作—次响应,就必须考虑如何消除按键抖动的影响。

一、按键实现
1.硬件电路
我的开发板按键一端连接到MCU,另一端是连接到地。所以当检测到低电平时,代表按键按下。为了防止电平不稳定,一般连接MCU的I/O口我们会选择上拉。
假如按键一端是连接Vdd的时候,此时I/O口就不再需要上拉。(ps:记得加限流电阻!防止电流过大,烧毁MCU)。


2.简单版(遍历)

这是简单的按键检测,无脑遍历。只能实现按键单击功能。下文有稍微复杂版。程序功能:按下按键LED灯状态发生改变。

#include "HT66F0185.h"

/*******************************************************************************

* @fn                delayMs        

* @brief        延时函数

* @param        延时时间 单位为ms

* @return        无

*******************************************************************************/

void delayMs(unsigned long int ms){

        

        while(ms--)

                GCC_DELAY(2000);//主频8Mhz,执行一条指令为0.5us。一条指令周期等于四条机器周期——》 1/8Mhz * 4 = 0.5us

}


/*******************************************************************************

* @fn                keyInit        

* @brief        按键初始化函数

* @param        无

* @return        无

*******************************************************************************/

void keyInit(void){

        /*配置PC0*/

        _pcc0=1;         //设置为输入

        _pcpu0=1;        //引脚上拉

        

        /*配置PC1*/

        _pcc1=1;        //设置为输入

        _pcpu1=1;        //引脚上拉

        

        /*配置PC3*/

        _pcc3=1;        //设置为输入

        _pcpu3=1;        //引脚上拉

        

        /*配置PC4*/

        _pcc4=1;        //设置为输入

        _pcpu4=1;        //引脚上拉

        

}

/*******************************************************************************

* @fn                main        

* @brief        主函数

* @param        无

* @return        无

*******************************************************************************/

void main(void)

{

        _wdtc = 0b10101000;//关闭看门狗。直接配置看门狗寄存器,0b代表二进制。


        /*按键初始化*/

        keyInit();

        

        /*LED设置*/

        _pac3 = 0;//设置PA3口为输出

        _pa3 = 1; //开机时灯亮

        _cos=1;//设置pa3管脚为IO,而不是比较器输出

        while(1){

               

                /*按键检测*/

                if(_pc0 == 0){

                        delayMs(100);//延时100ms

                        if(_pc0 == 0) _pa3 = ~_pa3;//取反

                }        

               

                /*按键检测*/

                if(_pc1 == 0){

                        delayMs(100);//延时100ms

                        if(_pc1 == 0) _pa3 = ~_pa3;//取反

                }

               

                /*按键检测*/

                if(_pc3 == 0){

                        delayMs(100);//延时100ms

                        if(_pc3 == 0) _pa3 = ~_pa3;//取反

                }

               

                /*按键检测*/

                if(_pc4 == 0){

                        delayMs(100);//延时100ms

                        if(_pc4 == 0) _pa3 = ~_pa3;//取反

                }

               

        }

}


3.稍复杂版(状态机)

以下是用状态机思想实现的按键检测,可以检测到单击和长按。推荐裸机采用这种方式。当然这个也是有缺点的:太依赖遍历,程序过多的话就会导致按键检测不灵敏。怎么解决呢?答案是加入定时器和中断。这个会在后面的章节提到。程序功能:长按LED灯亮,短按LED灯灭。

#include "HT66F0185.h"


#define WaitStatus     0     //等待状态

#define PressStatus    1         //按下状态

#define ReleaseStatus  2         //等待释放状态

#define IDEStatus      3         //空闲状态

#define KeyTime       600    //长按超时时间        


#define ShortPress 1        //短按

#define LongPress  2        //长按


#define KEY1 0x01

#define KEY2 0x02

#define KEY3 0x04

#define KEY4 0x08



unsigned char KeyStatus = 0;  //按键状态

unsigned char KeyVal = 0;          //按键值

unsigned char press = 0;          //按键按下状态 1单击,2长按

unsigned int KeyCnt = 0;          //长按计时


/*******************************************************************************

* @fn                delayMs        

* @brief        延时函数

* @param        延时时间 单位为ms

* @return        无

*******************************************************************************/

void delayMs(unsigned long int ms){

        

        while(ms--)

                GCC_DELAY(2000);//主频8Mhz,执行一条指令为0.5us。一条指令周期等于四条机器周期——》 1/8Mhz * 4 = 0.5us

}


/*******************************************************************************

* @fn                keyInit        

* @brief        按键初始化函数

* @param        无

* @return        无

*******************************************************************************/

void keyInit(void){

        /*配置PC0*/

        _pcc0=1;         //设置为输入

        _pcpu0=1;        //引脚上拉

        

        /*配置PC1*/

        _pcc1=1;        //设置为输入

        _pcpu1=1;        //引脚上拉

        

        /*配置PC3*/

        _pcc3=1;        //设置为输入

        _pcpu3=1;        //引脚上拉

        

        /*配置PC4*/

        _pcc4=1;        //设置为输入

        _pcpu4=1;        //引脚上拉

        

}


/*******************************************************************************

* @fn                getkey        

* @brief        获取按键值

* @param        无

* @return        按键值

*******************************************************************************/

unsigned char getkey(void){

        unsigned char temp=0;

        

        /*获取PC4状态*/

        if(_pc4==0)temp|=0x01;else temp&=~(0x01);

        

        /*获取PC3状态*/

        if(_pc3==0)temp|=0x02;else temp&=~(0x02);

        

        /*获取PC1状态*/

        if(_pc1==0)temp|=0x04;else temp&=~(0x04);

        

        /*获取PC0状态*/

        if(_pc0==0)temp|=0x08;else temp&=~(0x08);

        

        /*返回按键值*/

        return temp;

}


/*******************************************************************************

* @fn                keyscan        

* @brief        按键扫描

* @param        无

* @return        无

*******************************************************************************/

void keyscan(void){

        static unsigned char i=0;//消抖计时

        unsigned char temp;//按键值

        

        /*获取按键值*/

        temp = getkey();

        

        /*判断按键状态*/

        switch(KeyStatus){

               

                /*等待状态*/

                case WaitStatus:

                         if(temp != 0x00){

                                 if(++i >= 3){//按键按下后 第一段消抖 防止误触发

                                         i = 0;

                                         KeyStatus = PressStatus; //改变按键状态为按下状态

                                 }

                         }else{

                                 i = 0;//清空消抖计时

                         }

                        break;

               

                /*按下状态*/

            case PressStatus:

                        if(temp != 0x00){

                                KeyVal = temp;//获取是哪个按键按下

                                KeyStatus = ReleaseStatus;//改变按键状态为等待释放状态

                        }else{

                                KeyStatus = WaitStatus;//改变按键状态为等待状态

                        }

                        break;

                        

                /*等待释放状态*/        

                case ReleaseStatus:         

                        if(temp != 0x00){

                                if(++KeyCnt == KeyTime) {//长按检测

                                        press = LongPress;

                                        KeyStatus = IDEStatus;//改变按键状态为空闲状态

                                        KeyCnt = 0;//清空长按计时

                                }

                        }else{

                                KeyCnt = 0;//清空长按计时

                                KeyStatus = WaitStatus;//改变按键状态为等待状态

                                press = ShortPress;//按键短按标志位

                        }

                        break;

                        

                /*空闲状态*/

                case IDEStatus:

                        if(temp == 0x00){

                                KeyStatus = WaitStatus;//改变按键状态为等待状态

                        }

                        break;

               

                /*为保持switch语句完整*/        

                default:

                        ;//空语句 什么都不执行

                        

        }

}


/*******************************************************************************

* @fn                main        

* @brief        主函数

* @param        无

* @return        无

*******************************************************************************/

void main(void)

{

        _wdtc = 0b10101000;//关闭看门狗。直接配置看门狗寄存器,0b代表二进制。


        /*按键初始化*/

        keyInit();

        

        /*LED设置*/

        _pac3 = 0;//设置PA3口为输出

        _pa3 = 1; //开机时灯亮

        _cos = 1; //设置pa3管脚为IO,而不是比较器输出


        while(1){

                /*按键扫描*/

                keyscan();        

               

                /*延时3ms*/

                delayMs(3);

               

                /*检测到长按开灯*/

                if(press == LongPress) _pa3 = 1;

               

                /*检测到短按关灯*/                        

                if(press == ShortPress) _pa3 = 0;

               

        }

}


总结
一个按键实现的方式有很多,我是一步一步学过来了的。除了上面说的状态机实现,其实还有更高级的实现方式,能判断长按、短按、双击、单击等等。当然这个就需要RTOS(操作系统)和更高位的处理器,是一种面向对象的实现方式。后面我会单独开一篇文章介绍,实验的环境为:搭载FreeRTOS的STM32。


———————————————
版权声明:本文为CSDN博主「猫想飞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

  

使用特权

评论回复

相关帖子

沙发
cyclefly| | 2022-8-1 20:05 | 只看该作者
IO的应用

使用特权

评论回复
板凳
caigang13| | 2022-8-2 19:28 | 只看该作者
谢谢分享经验

使用特权

评论回复
地板
chenjun89| | 2022-8-3 08:04 | 只看该作者
怎样实现矩阵键盘扫码?

使用特权

评论回复
5
daichaodai| | 2022-8-4 08:39 | 只看该作者
又见此账号

使用特权

评论回复
6
littlelida| | 2022-8-14 14:30 | 只看该作者
入门应用

使用特权

评论回复
7
sdlls| | 2022-8-16 20:30 | 只看该作者
需要上拉电阻吗   

使用特权

评论回复
8
elsaflower| | 2022-8-16 22:21 | 只看该作者
如何消抖?   

使用特权

评论回复
9
soodesyt| | 2022-8-17 16:05 | 只看该作者
单片机驱动电流是多大呢  

使用特权

评论回复
10
jonas222| | 2022-8-17 16:51 | 只看该作者
—次响应需要while吗  

使用特权

评论回复
11
klbyf| | 2022-8-18 17:33 | 只看该作者

使用特权

评论回复
12
jtracy3| | 2022-8-18 17:51 | 只看该作者
毫秒”级按键抖动能起作用吗  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

78

主题

124

帖子

0

粉丝