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


系列文章目录

第一章 HT单片机环境搭建

第二章 点亮第一个LED灯

第三章 按键

第四章 定时器

第五章 PWM

第六章 时基中断

第七章 LCD


前言

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

一、新建工程

请参照第二章第一节新建工程
——》第二章 点亮第一个LED灯

二、按键实现

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。
  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值