2014-12-16 14:44:23 hengliwuyou 阅读数 3435
  • 单片机到底是个什么东西-1.2.第1季第2部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第2个课程,用通俗易懂的语言讲了很多和单片机有关的技术概念,如CPU、ROM、RAM、外设、电路板、软件硬件工作的差别等。目的是希望大家在轻松愉悦的氛围中对单片机加深认识。

    3039 人正在学习 去看看 朱有鹏

抖动是机械按键存在的现象,是必须要进行处理的。一般处理有两种方式:一种是硬件消抖,另一种是软件消抖。硬件消抖不仅会增加开发成本,而且有时不稳定。一般都会选择软件消抖。

  因为单片机最重要的是测试状态,而不是测试过程。一般不会选择通过状态延时来消抖,而是通过定时循环测试按键的状态来消抖.下面是针对51单片机的独立按键写的一个通过定时器来消抖的程序:

#include<reg52.h>

sbit  ADDR0 = P1^0;
sbit  ADDR1 = P1^1;
sbit  ADDR2 = P1^2;
sbit  ADDR3 = P1^3;
sbit  ENLED = P1^4;
sbit  KEY4 = P2^7;
//数码管的数字十六进制表示
unsigned char code LedChar[]=
{
 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
 0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E
};


bit KeySta = 1;


void main()
{
 bit backup = 1;
 unsigned char cnt = 0;

//打开总中断
 EA = 1;
 ENLED = 0;
 ADDR3 = 1;
 ADDR2 = 0;
 ADDR1 = 0;

 ADDR0 = 0;

//设置TMOD的状态

 TMOD = 0x01;

//定时为2ms

 TH0 = 0xF8;
 TL0 = 0xCD;
 ET0 = 1;
 TR0 = 1;
 P2  = 0xF7;
 P0 = LedChar[cnt];


 while(1)
 {
  if(KeySta !=backup)
   {
    if(backup==0)
{
cnt++;
if(cnt>=10)
{
 cnt=0;
}
P0 = LedChar[cnt];
}
backup=KeySta;
    
   }
 }


}


void InterruptTimer0() interrupt 1
{
  static unsigned char keybuf = 0xFF;
  TH0 = 0xF8;
  TL0 = 0xCD;
  keybuf = (keybuf<<1) |KEY4;
  if(keybuf == 0x00)
  {
   KeySta = 0;
  }
  else if(keybuf == 0xff)
  {
   KeySta = 1;
  }
  
}
2017-04-12 22:21:39 little_white__ 阅读数 3184
  • 单片机到底是个什么东西-1.2.第1季第2部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第2个课程,用通俗易懂的语言讲了很多和单片机有关的技术概念,如CPU、ROM、RAM、外设、电路板、软件硬件工作的差别等。目的是希望大家在轻松愉悦的氛围中对单片机加深认识。

    3039 人正在学习 去看看 朱有鹏

在单片机中会遇到识别按键的问题,常用的独立按键电路如下图:
这里写图片描述
由于机械式触点开关具有弹性,会发生抖动,所以要进行消抖。有两种消抖方式:硬件消抖和软件消抖。
硬件消抖电路:
这里写图片描述
软件消抖常采用延时消抖:按键被按下时,当检测到低电平时,延时一段时间,一般是5到10ms,然后再检测端口,若还是低电平,则表示检测到按键按下,按键弹起时同理可得。

独立按键的检测一般采用两种方法:中断检测和查询检测。
这里写图片描述

按键连接P3^2引脚,检测按键按下和弹起的次数。


1.查询检测:可以直接在主程序中循环检测,也可以通过定时器,每隔20ms检测一次。下面是直接在循环中检测的。

主函数:

#include <reg51.h>
#include <intrins.h>

#define uint unsigned int 

sbit in0 = P3^2 ;  //输入信号引脚

uint Press_flag = 1 ; //按键按下标志
uint count = 0 ;   //按键次数

void delay_ms(uint ms)
{
	uint i , j ;
	for(i = 0 ; i < ms ; i++)
	{
		for(j = 0 ; j < 333 ; j++)
		{
			_nop_() ;
		}
	}
}

void key()
{
	   if(in0 == 0)	 //检测到0
		{
			delay_ms(10) ;	 //	延时消抖
			if(in0 == 0)	//再次检测到0
			{
				Press_flag = 0 ;  //代表按键被按下
			}
		}
		
		if(Press_flag == 0)	   //如果按键被按下,再检测按键弹起
		{
			if(in0 == 1)
			{
				delay_ms(10) ;
				if(in0 == 1)
				{
					count ++ ;	  //表示按键按下并弹起的次数
					Press_flag = 1 ;	  //复位按键按下标志
				}
			}
		}
			
}

void main(void)
{ 
	while(1)
	{
		  key() ;
	}
}

信号函数:


signal void test(unsigned int cc) 
{
	unsigned int i , j ;
	for(i = 0 ; i < cc ; i++)
	{
		for(j = 0 ; j < 2 ; j++)
		{
			port3 &= ~(1<<2) ;
			swatch(0.002) ;
			port3 |= 1<<2 ;
			swatch(0.002);
		}
	    port3 &= ~(1<<2) ;
		swatch(0.002) ;

		swatch(0.01) ;

		for(j = 0 ; j < 2 ; j++)
		{
			port3 |= 1<<2 ;
			swatch(0.002) ;
			port3 &= ~(1<<2) ;
			swatch(0.002) ;
		}	

		port3 |= 1<<2 ;
		swatch(0.02) ;

		swatch(0.1) ;
	}
	_break_ = 1 ;
}


信号函数波形图:
这里写图片描述
可以看到count的值为4
这里写图片描述

2.中断检测

主程序:

#include <reg51.h>
#include <intrins.h>

#define uint unsigned int 

sbit in0 = P3^2 ;
uint count = 0 ;	
uint press_flag = 1 ;

void delay_ms(uint ms)
{
	uint i , j ;
	for(i = 0 ; i < ms ; i++)
	{
		for(j = 0 ; j < 333 ; j++)
		{
			_nop_() ;
		}
	}
}


void Init_INT(void)
{
    EA = 1 ; //开总中断
    EX0 = 1 ; //开外部中断0
    IT0 =  1 ; //下降沿触发中断
}

void Int0 (void) interrupt 0
{
	EX0 = 0 ; //关闭外部中断0,防止频繁触发中断
	delay_ms(10) ;
    if(in0 == 0)
	{
		press_flag = 0 ;//按键按下标志
	}
	if(press_flag == 0 && in0 == 1)//检测按键弹起
	{
		delay_ms(10) ;
		if(in0 == 1)
		{
			count++ ;
			press_flag = 1 ;
		}
	}
	EX0 = 1 ;  //开启外部中断
}

void main(void)
{
	Init_INT() ; // 中断初始化

	while(1)
	{
		
	}
}

新手小白,请多指教。

2017-09-29 20:43:20 define_me_freedom 阅读数 4882
  • 单片机到底是个什么东西-1.2.第1季第2部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第2个课程,用通俗易懂的语言讲了很多和单片机有关的技术概念,如CPU、ROM、RAM、外设、电路板、软件硬件工作的差别等。目的是希望大家在轻松愉悦的氛围中对单片机加深认识。

    3039 人正在学习 去看看 朱有鹏

按键软件消抖自我接触单片机开始就已经存在这个问题了,网上的办法无非是延时消抖和定时轮询。对于写裸机的我来说这两种方法都不可避免的会有资源浪费掉,今天突然有了灵感,想到了一种相对高效的办法来解决消抖问题。

硬件平台:STM32F103RCT6开发板

开发环境:WIN7-64bit+MDK5+STD库

按键消抖的必要性在此我就不多说了。直接步入正题。

在使用本方法前请注意,本方法需要一个全局时间戳的支持。

第一步:初始化全局时间戳的定时器,一般采用SysTick定时器来产生,每ms一次tick即可。

第二步:初始化按键对应的IO,复用为边沿触发的外部中断。

第三步:在外部中断函数中添加按键事件处理函数。

代码如下:

typedef struct _Key_t
{
    u32 last_time;
    enum
    {
        May_Press,
        Release,
    }private_state;
    enum
    {
        No_Press,
        Short_Press,
        Long_Press,
    }state;
}Key_t;

#define Is_ShortPress_Threshold   1500
简单定义一个按键状态的结构体,用于管理每个按键的状态。顺便再定义一个长短按的识别阈值,用于区分按键的长短按。

if(key_state.private_state==Release)                
{
    if(KEY==0)
    {
        key_state.private_state=May_Press;
        key_state.last_time=course_ms();
    }
}
else if(key_state.private_state==May_Press)
{
    if(KEY==1)
    {
        if((course_ms()-key_state.last_time>10)&&(course_ms()-key_state.last_time<Is_ShortPress_Threshold))
        {
            key_state.state=Short_Press;
            key_state.private_state=Release;
        }
        else if(course_ms()-key_state.last_time>Is_ShortPress_Threshold)
        {
            key_state.state=Long_Press;
            key_state.private_state=Release;
        }
        else
            key_state.private_state=Release;
    }
}
以上为需要添加到中断处理函数的按键事件处理函数,算法的核心是一个状态机。在本例中,按键被默认上拉,按下接地。course_ms()为获取全局时间戳的函数。

思路解释如下:按键状态结构体有一个用于识别的状态位,默认处于Release,也就是释放的状态。一旦按键被按下,中断触发,此时检查是否是Relase状态,如果是就检查按键是否被拉低,如果是,此时进入May_Press状态,也就是可能是按下的,并且记录此时的时间戳,这一步是消抖的关键。当按键被释放,由于是边沿触发,会再次进行处理,此时检查和上一次触发之间的时间戳之差,如果小于10ms我们就认为是抖动,此时不会对按键输出状态进行修改,而是直接将按键状态置回Relase状态,反之检查差值和长短按阈值之间的关系,将state置位为对应的状态。消抖的核心在于记录时间戳,而这只是一个简单的赋值操作,并不耗费时间。

效率上来说,延时消抖花费时间在无意义延时上,而相对较好的定时轮询还是不可避免的在轮询,而现在这种方式完全是中断性质的。唯一多出的开销(全局时间戳)并不是只可以用于按键消抖,另外在HAL库中存在直接获取tick的函数,这样实现就更方便了。经实际测试,消抖效果可以达到其他两种消抖算法的水平。




2018-05-11 00:41:06 qq_40642465 阅读数 9532
  • 单片机到底是个什么东西-1.2.第1季第2部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第2个课程,用通俗易懂的语言讲了很多和单片机有关的技术概念,如CPU、ROM、RAM、外设、电路板、软件硬件工作的差别等。目的是希望大家在轻松愉悦的氛围中对单片机加深认识。

    3039 人正在学习 去看看 朱有鹏

我这按键是机械弹性按键,所以具有弹性,按键在按下和松开的瞬间均伴随有一连串的抖动,键抖动会引起一次按键被误

读多次,然后为了保证cpu对键的一次闭合仅作一次处理,所以我们必须去除抖动。去除抖动有2种,硬件消抖和软件消抖。
其中一软件消抖最方便。所以我就以软件消抖为例。



     独立按键的原理图和led的原理图如上,8个led的阳极都接了vcc(vcc认为是电源正极,gnd认为是电源负极),所以只需要led的阴极接的p2i(i取值0到7)口输入低电平(即0v)对应的led就可以发亮了.

接下来看看独立按键的原理图,首先K1是和rxd管脚相连的,然后rxd管脚又是和P3^1口连接的,所以可以P3^1口是和k1开关连接的,可以知道当k1按键按下时电路就导通了,因为k1右边接了gnd,所以P3^1这个io口的输出电平就被拉低变0电平了。(p31io口电平能被拉低是因为P3口是准双向io口),所以我们可以通过判断P3^1io口的电平来获知按键的状态,P3^1==1,则按键k1没有按下,P3^1==0,则按键k1按下了。
接下来展示一个通过独立键盘里的按键k1来控制led1的亮灭的程序

#include<reg52.h>
sbit led1=P2^0;//因为led1由p2^0口控制
sbit k1=P3^1;//P31口的输出电平由按键k1控制
void delay(int i)
{
	while(i--);
}
void keyproc()
{
	if(k1==0)
	{
		delay(1000);//延时消抖
		if(k1==0)
		{
			led1=~led1;////led1状态反转,亮变灭,灭变亮
		}
		while(!k1) ;
		
	}
}
void main()
{
	while(1)
	{
		keyproc();
	}
}

把这代码烧到单片机后,我们一按k1,led1就会亮,再按一次就会灭,一直这样下去


2019-10-30 14:12:43 weixin_42359978 阅读数 167
  • 单片机到底是个什么东西-1.2.第1季第2部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第2个课程,用通俗易懂的语言讲了很多和单片机有关的技术概念,如CPU、ROM、RAM、外设、电路板、软件硬件工作的差别等。目的是希望大家在轻松愉悦的氛围中对单片机加深认识。

    3039 人正在学习 去看看 朱有鹏

按键的抖动

按键在按下过程中,触点接触和断开瞬间都会产生机械抖动,如果不进行处理,每一次按键就会产生若干次的响应。在单片机中一般使用delay函数来去除抖动,那么硬件去抖动该如何实现呢?

按键去抖动电路

在这里插入图片描述

原理

该按键消抖电路主要由四个移位寄存器构成,在按键按下时,每个时钟上升沿都会对key_in进行检测,只有当连续四个时钟上升沿都检测到键值为1时,key

Verilog 结构描述

源代码

module xiaodou_structure(key_in,clk,key_out);

input key_in,clk;
output key_out;
reg q0,q1,q2,q3;

always @(posedge clk)
begin 
 q3 <= q2;
 q2 <= q1;
 q1 <= q0;
 q0 <= key_in;
end

assign key_out=q0&q1&q2&q3;

endmodule

我一直对阻塞赋值和非阻塞赋值有疑惑,如果不能确定这样写是否会综合为移位寄存器,我们也可以这样写:

module xiaodou_structure2(key_in,clk,key_out);

input key_in,clk;
output key_out;
reg [3:0] q;

always @(posedge clk)
begin 
 q={q[2:0],key_in};
end

assign key_out=(q===4'b1111)?1:0;
endmodule

检查一下,综合器综合后生成的网表,确实是一个4位的移位寄存器。

Technology Map Viewer

在这里插入图片描述
再看看仿真的结果,也确实达到了消抖的效果

仿真波形

在这里插入图片描述

Verilog 行为描述

源代码

module xiaodou_action(key_in,clk,key_out);

input key_in,clk;
output reg key_out;
reg [1:0] state;

parameter S0='d0,S1='d1,S2='d2,S3='d3;

always @(posedge clk)
begin
 case(state)
  S0: begin 
    if(key_in) begin key_out<=0; state<=S1; end
    else begin key_out<=0; state<=S0; end
    end 
  S1: begin 
    if(key_in) begin key_out<=0; state<=S2; end
    else begin key_out<=0; state<=S0; end
    end 
  S2: begin 
    if(key_in) begin key_out<=0; state<=S3; end
    else begin key_out<=0; state<=S0; end
    end 
  S3: begin 
    if(key_in) begin key_out<=1; state<=S0; end  //S3改为S0
    else begin key_out<=0; state<=S0; end
    end 
  default state<=S0;
 endcase
end

endmodule

这段代码其实就是用状态机设计的1111序列检测器,这里我做了微微的改动,在日常经验中我们知道,如果长按某个按键,按键按下且稳定时,每隔一段时间,就会有一个按键有效脉冲,这样就可以实现对某个参数的连续调整。所以这里我将S3下一个状态设定为S0,而不是保持S3,这样在按键长按的情况下,每4个时钟就会有一个按键有效脉冲输出。

RTL

在这里插入图片描述

仿真结果

在这里插入图片描述
从图中可以看到,在第二次按键按下时,一共输出了3个按键有效脉冲。

Testbench

以上仿真所使用的testbench主要代码均相同,具体如下:

initial                                                
begin                                                  
clk=0;
key_in=0;
//按下
#20 key_in=1;
#3 key_in=0;
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;
#2 key_in=1;
//稳定
#90 key_in=0;
//松手
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;

//按下
#150 key_in=1;
#3 key_in=0;
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;
#2 key_in=1;
//稳定
#280 key_in=0;
//松手
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;

#100 $stop;                      
$display("Running testbench");                       
end 

always                                                               
begin                                                  
#10 clk=~clk;                                
end                                                    
endmodule

注意

最后需要注意的是时钟clk的选取,不能过大,一般取100Hz以下,但如果clk过小,也有可能得不到按键有效脉冲。

若文档有任何错误或不足,欢迎在博客下方留言指正;

按键“消抖”处理

阅读数 745

c51独立按键

阅读数 2795

没有更多推荐了,返回首页