按键的种类 基于Mega16

2019-04-15 16:43发布

单片机的按键种类

基于Megae16单片机 -【Leep_H】

AD按键

利用AD按键是利用不同的电压大小区分相对应的按键,优点呢节省io口,但是缺点也很明显,一旦电路负载的开关过多,期间的差值就很小,分辨不出。主要用于电视机的按键,MP3,MP4等等。 本电路图如下
这里写图片描述 代码如下: /********************************************* File name : 7seg.c Chip type : ATmega16 Program type : Application Clock frequency : 4.000000 MHz Memory model : Small External SRAM size : 0 *********************************************/ #include #include #define CLR_SHCLK() PORTB&=~(1<<1) //移位时钟 SCLK #define SET_SHCLK() PORTB|=(1<<1) //移位时钟 SCLK,上升沿 #define CLR_STCLK() PORTB&=~(1<<0) //锁存时钟 RCLK #define SET_STCLK() PORTB|=(1<<0) //拉高锁存时钟,上升沿 #define CLR_DS() PORTA&=~(1<<0) //清零 #define SET_DS() PORTA|=(1<<0) //置位 #define U8 unsigned char unsigned char led_7[16] ={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07, 0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71}; int disp_buff[4]={0,0,0,0}; int count=0,time_100=0; float adcread; int adcc,flag; int a;int b; static int readi=0; unsigned char posit = 0; void adc_init(void) { ADMUX=0X67; ADCSR=0X87; } void Hc595send(unsigned char SndData) { U8 i; for(i=0;i<8;i++) { if(SndData&(1<<(7-i))){ SET_DS(); }else{ CLR_DS(); } CLR_SHCLK(); //移位时钟 SCLK SET_SHCLK(); //移位时钟 SCLK,上升沿 } CLR_STCLK(); SET_STCLK(); //拉高锁存时钟,上升沿 } void port_init(void) { PORTA = 0x00; DDRA = 0x01; PORTB = 0x00; DDRB = 0x03; PORTC = 0x00; //m103 output only DDRC = 0x00; PORTD = 0x08; DDRD = 0xF0; } //TIMER0 initialize - prescale:64 // WGM: CTC // desired value: 1000Hz // actual value: 993.103Hz (-0.7%) void timer0_init(void) { TCCR0 = 0x00; //stop TCNT0 = 0x8D; //set count OCR0 = 0x73; //set compare TCCR0 = 0x0B; //start timer } #pragma interrupt_handler timer0_comp_isr:iv_TIM0_COMP void timer0_comp_isr(void) { if(++count>=100) { count=0; time_100=1; } PORTD |= 0xF0; Hc595send(~led_7[disp_buff[posit]]); PORTD &= ~(1<<(7-posit)); if (++posit >=4 ) posit = 0; } unsigned int AD_GetData() //采用左对齐的方式 { ADCSRA |= (1 <<6); //开始AD转换 while(!(ADCSRA&(1<<4))); //等待转换完成 ADCSRA |= (1 << 4); //清零ADC中断标志位 return ADC; //返回ADCH值,只取高8位 } //call this routine to initialize all peripherals void init_devices(void) { //stop errant interrupts until set up CLI(); //disable all interrupts port_init(); timer0_init(); adc_init(); MCUCR = 0x00; GICR = 0x00; TIMSK = 0x02; //timer interrupt sources SEI(); //re-enable interrupts //all peripherals are now initialized } int main(void) { unsigned char i; init_devices(); while (1) { if(time_100==1) { time_100=0; adcread=AD_GetData(); adcc=adcread/1023*3300; //其中adc是10位的精度。 } if(adcc>=0 && adcc<829adcc!=3300) disp_buff[3]=1; if(adcc>1 && adcc<=830 && adcc!=3300) disp_buff[3]=2; if(adcc>830 && adcc<=1322 && adcc!=3300) disp_buff[3]=4; if(adcc>1322 && adcc<=1655 && adcc!=3300) disp_buff[3]=3; if(adcc>1655 && adcc<=1887 && adcc!=3300) disp_buff[3]=6; if(adcc>1887 && adcc<=2161 && adcc!=3300) disp_buff[3]=5; } return 0; }

行列式扫描按键

为了应付IO口的紧缺,还引入了行列式按键扫描的方式。确定代码较为复杂,这里使用了状态机,稍后会有状态机的说明,并附有文档,看不懂的可以现看状态机的原理。
图例题这里写图片描述 类似预电话机 代码如下: #include #include #define INPUT ((PIND&(1<<0))|(PIND&(1<<1))|(PIND&(1<<2))) #define key1_1 0x0e #define key1_2 0x0d #define key1_3 0x0b #define key2_1 0x16 #define key2_2 0x15 #define key2_3 0x13 #define key3_1 0x26 #define key3_2 0x25 #define key3_3 0x23 #define key4_1 0x46 #define key4_2 0x45 #define key4_3 0x43 #define key_mask 0x07 int key; int j=0;int point=0; int count=0,flag=0; unsigned char led_7[16] ={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07, 0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71}; int a[6]={0,0,0,0,0,0}; void port_init(void) { PORTA = 0x00; DDRA = 0xFF; PORTB = 0x00; DDRB = 0x00; PORTC = 0xff; //m103 output only DDRC = 0xff; PORTD = 0xff; DDRD = 0xF8; } //TIMER0 initialize - prescale:64 // WGM: CTC // desired value: 1mSec // actual value: 1.007mSec (-0.7%) void timer0_init(void) { TCCR0 = 0x00; //stop TCNT0 = 0x8D; //set count OCR0 = 0x73; //set compare TCCR0 = 0x0B; //start timer } #pragma interrupt_handler timer0_comp_isr:iv_TIM0_COMP void timer0_comp_isr(void) { if(++count>=10) { count=0; flag=1; //10毫秒 } PORTC=0XFF; PORTA=led_7[a[j]]; //显示按键 PORTC&=~(1<if(j>=7) j=0; } //call this routine to initialize all peripherals void init_devices(void) { //stop errant interrupts until set up CLI(); //disable all interrupts port_init(); timer0_init(); MCUCR = 0x00; GICR = 0x00; TIMSK = 0x02; //timer interrupt sources SEI(); //re-enable interrupts //all peripherals are now initialized } int readkey() { static int key_state=0, line,key_v; int key_return=255,i; switch(key_state) { case 0: line=0x08; //00001000 行1开始扫描 for(i=1;i<=4;i++) { PORTD=~line; //两次确认,要不然会显示不正确 11110111 PORTD=~line; //行线输出低电平,若检测到列线有低电平则有按键按下 key_v=key_mask&INPUT; //00000111 判断有没有列线变为0 if((key_v)!=key_mask) //列线输入改变 { key_state=1; //进入状态1 break; } else line<<=1; //一直依次发送低电平 } break; case 1: if(key_v==(INPUT&key_mask)) //再次读列电平 { switch(line|key_v) { case key1_1: //0 0001 110 第1行1列 key_return=1; break; case key1_2: //0 0001 101 第1行2列 key_return=2; break; case key1_3: key_return=3; break; case key2_1: key_return=4; break; case key2_2: key_return=5; break; case key2_3: key_return=6; break; case key3_1: key_return=7; break; case key3_2: key_return=8; break; case key3_3: key_return=9; break; case key4_1: key_return=10; break; case key4_2: key_return=0; break; case key4_3: key_return=12; break; } key_state=2; } else key_state=0; //抖动回状态0 break; case 2: PORTD=key_mask;//0 0000 111 全部行为低电平 if((key_mask&INPUT)==0x07)//列线全部为高电平 key_state=0; //没有按键按下 break; } return key_return; } int main() { init_devices(); while(1) { if(flag) //10ms检测状态 { flag=0; key=readkey();//读出具体按键 if(key!=255) { for(point=5;point>0;point--) a[point]=a[point-1]; a[0]=key; } } }; return 0; }

状态机

顾名思义,状态机就是分析按键的状态。实体按键会产生抖动,而消抖成为重要的事情。
1.硬件消抖,采用R-S触发器或者RC积分电路,但是这个方法不实际,大大的增加的成本。
2.软件消抖,对按键进行两次确认,即在首次检测到按键按下之后间隔10ms再次确认按键的状态。
状态机结合定时器来处理按键十分好用。 这里写图片描述 1/0: ——-1表示按键未按下(按键按下低电平),右边的0表示输出值为0;(设置定时器每隔10ms检测状态机)
状态0: 均为(1/0),按键未按下,输出为0的状态。检测但当按键按下时变为(0/0). 状态1: 均为(0/0),此时有两种可能 1 隔了10ms后,发现是一次抖动,返回0状态(1/0) 2 隔了10ms后,发现确实是一次按键按下的状态,(0/1)此时进入状态2 ***注意!此时是所有状态里面唯一输出1的状态,输出1则说明按键按下,这是代码的主要判断。*** 状态2: 若按键依旧按着,则直至按键释放,回到(0/0)状态0。 还是不好理解的话,下面为代码部分加深: 这里写图片描述
足够用就不用进阶了
状态机进阶: 这里写图片描述 引入状态机3,目的为了使得同一个按键有不同的功能,或者有按键的连击(类似长按电视音量加) 图中的时间可自己设计。
这里写图片描述
2016/10/20 个人难免狭隘,不足之处,请指出。