一)按键行列扫描与蜂鸣器
(1)技术体会:在行列式扫描结构的薄膜按键里,干扰很大,按键扫描程序非常讲究,尤其是去抖动的处理。
(2)功能需求:每按一个按键,蜂鸣器就响一次。
(3)硬件原理:
(a)用4个IO来做2X2按键行列扫描,其中作为输入的2个IO口必须接上拉电阻20K左右。
(b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。而无源蜂鸣器是要靠断断续续的开关信号来驱动才能响,就是要频率来驱动。
(4)源码适合的单片机:PIC18F4620,晶振为22.1184MHz
(5)源代码讲解如下:
#include //包含芯片相关头文件
//补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr
#define beep_dr LATA1 //蜂鸣器输出
#define key_dr1 LATB3 //2X2按键行输出
#define key_dr2 LATB4 //2X2按键行输出
#define key_sr1 RB6 //2X2按键行输入
#define key_sr2 RB7 //2X2按键行输入
//补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量
//前缀都用cnt_表示。
#define cnt_delay_cnt1 25 //按键去抖动延时阀值
#define cnt_delay_cnt2 5 //按键行输出信号稳定的小延时阀值
#define cnt_voice_time 60 //蜂鸣器响的声音长短的延时阀值
void delay1(unsigned int de) ;//小延时程序,时间不宜太长,因为内部没有喂看门狗
//补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中
//断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。
void key_scan(); //按键扫描函数,放在定时中断里
void key_service(); //按键服务函数,放在main函数循环里
//补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名
//后缀都用_step表示。
unsigned char key_step=1; //按键扫描步骤变量,在switch()语句的括号里
//补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名
//后缀都用_lock表示。
unsigned char key_lock1=0; //按键自锁标志
//补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量
//后缀都用_cnt表示。
unsigned int delay_cnt1=0; //延时计数器的变量
unsigned int delay_cnt2=0; //延时计数器的变量
unsigned int voice_time_cnt; //蜂鸣器响的声音长短的计数延时
//补充说明:吴坚鸿程序风格是这样的,凡是做类型的变量的分类
//后缀都用_sec表示。
Unsigned char key_sec=0; //哪个按键被触发
//主程序
main()
{
ADCON0=0x00;
ADCON1=0x0f; //全部为数字信号
ADCON2=0xa1; //右对齐
RBPU=0; //上拉电阻
SSPEN=0; //决定RA5不作为串口
TRISB3=0; //配置按键行扫描IO为输出
TRISB4=0; //配置按键行扫描IO为输出
TRISB6=1; //配置按键列扫描IO为输入
TRISB7=1; //配置按键列扫描IO为输入
T1CON=0x24; //定时器中断配置
TMR1H=0xF5;
TMR1L=0x5F;
TMR1IF=0;
TMR1IE=1;
TMR1ON=1;
TMR1IE=1;
//补充说明,以上的内容为寄存器配置,每种不同的单片机会有点差异,
//大家不用过度关注以上寄存器的配置,只要知道有这么一回事即可
beep_dr=0; //关蜂鸣器,上电初始化IO
while(1)
{
CLRWDT(); //喂看门狗,大家不用过度关注此行
key_service(); //按键服务
}
}
void key_scan() //按键扫描函数
{
//补充说明:如果中断一次就把所有的按键都扫描完,中断占用的时间片就会太多,势//必会影响main函数里其他子程序的运行,为了避免一口气把所//的按键都扫描完,此
//处用switch语句把4个按键分成2等分,一次中断只扫描2个按键
switch(key_step) //按键扫描步骤,
{
case 1: //扫描1号键,2号键
key_dr1=0; //按键行扫描输出第一行低电平
key_dr2=1;
delay_cnt2=0; //延时计数器清零
key_step++; //切换到下一个运行步骤
break;
case 2:
delay_cnt2++;
if(delay_cnt2>cnt_delay_cnt2) //小延时,但不是去抖动延时,替代一直受网友争议的delay1(40)
{
delay_cnt2=0;
key_step++; //切换到下一个运行步骤
}
break;
case 3:
if(key_sr1==1&&key_sr2==1)
{ //如果没有按键按下,则2个IO输入都是高电平
key_step++; //如果没有按键按下,下一个中断扫描下2个