分享

吴坚鸿单片机程序风格赏析(二)

 BlackBluePLC 2014-05-16
      吴坚鸿开技术新帖啦!快点去他的帖子里去关注吧!原帖地址:http://bbs.elecfans.com/jishu_288846_1_1.html
    感兴趣的电子发烧友快去他的帖子中去与他讨论吧!
    目前吴坚鸿已经更新到了第三节,小编把这三节的内容都转发过来了,真的是很值得学习的原创资源,发烧友们一定要好好的跟着吴坚鸿学哟~~另也非常感谢吴坚鸿与发烧友们分享!

第一节 按键行列扫描与蜂鸣器

(1)技术体会:在行列式扫描结构的薄膜按键里,干扰很大,按键扫描程序非常讲究,尤其是去抖动的处理。
(2)功能需求:每按一个按键,蜂鸣器就响一次。
(3)硬件原理:
(a)用4个IO来做2X2按键行列扫描,其中作为输入的2个IO口必须接上拉电阻20K左右。
(b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。而无源蜂鸣器是要靠断断续续的开关信号来驱动才能响,就是要频率来驱动。
(4)源码适合的单片机:PIC18F4620,晶振为22.1184MHz
(5)源代码讲解如下:
#include<pic18.h>         //包含芯片相关头文件

//补充说明:吴坚鸿程序风格是这样的,凡是输出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_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 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;
                    delay1(40);    //小延时,等待列输入的信号稳定,但不是传统的
//去抖动延时
                if(key_sr1==1&&key_sr2==1)
                {   //如果没有按键按下,则2个IO输入都是高电平
                        key_step++; //如果没有按键按下,下一个中断扫描下2个
//按键
key_lock1=0;  //按键自锁标志清零
                         delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙        

                  }
Else   if(key_sr1==0&&key_sr2==1&&key_lock1==0) 
                 {     // key_lock1按键自锁,避免按键一直触发,下降沿有效
                        ++delay_cnt1;  //延时计数器
//补充说明:有按键触发之后,不要马上响应,要延时一段时间去抖动,此处本人设计非常
//巧妙,很多人仅仅知道按键延时的时候要保证还能去处理别的程序,这样是还不够的,
//在延时去抖动的时候,还必须要监控延时这段时间里,按键IO输入口是否会由于受到某//种干扰突然由低变成高,如果一旦变成高,那么延时计数器delay_cnt1必须重新清零
//我当年就是因为这样处理,把卖给富士康100台受干扰死机的设备修好了,老板马上
//给我加薪1000元。
                        if(delay_cnt1>cnt_delay_cnt1)  //延时计数器超过一定的数值
                        {
                             delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发,只有松开按键,
//此标志位才会被清零
                                       key_sec=1;         //触发1号键
                        }
                                        
                }
                else if(key_sr1==1&&key_sr2==0&&key_lock1==0)
                {
                        ++delay_cnt1;
                         if(delay_cnt1>cnt_delay_cnt1)
                         {
                                 delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                                          key_sec=2;         //触发2号键
                                                
                        }        
                     }
             break;
        
        case 2:       //扫描//扫描3号键,4号键
                key_dr1=1;     
                key_dr2=0;   //按键行扫描输出第二行低电平
                    delay1(40);    
                if(key_sr1==1&&key_sr2==1)
                {   
                        key_step++; 
key_lock1=0;  
                         delay_cnt1=0;

                  }
Else   if(key_sr1==0&&key_sr2==1&&key_lock1==0) 
                 {     
                        ++delay_cnt1; 
                        if(delay_cnt1>cnt_delay_cnt1) 
                        {
                             delay_cnt1=0;
key_lock1=1;  
                                       key_sec=3;         //触发3号键
                        }
                                        
                }
                else if(key_sr1==1&&key_sr2==0&&key_lock1==0)
                {
                        ++delay_cnt1;
                         if(delay_cnt1>cnt_delay_cnt1)
                         {
                                 delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                                          key_sec=4;         //触发4号键
                                                
                        }        
                     }
             break;
     }
if(key_step>2)   //第1组按键与第2组按键反复轮流扫描
{
        key_step=1;        
}
}

void key_service()                //按键服务函数
{
        switch(key_sec)                //按键服务状态切换
        {
                case 1:// 1号键


// 补充说明:voice_time_cnt只要不为0蜂鸣器就会响,中断里判断voice_time_cnt不为0
//时,会不断自减,一直到它为0时,自动把蜂鸣器关闭
                                  voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                                                                                          
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                case 2:// 2号键
                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                case 3://3号键

                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                case 4://4号键

                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        



                        
        }                
}




//中断
void interrupt timer1rbint(void)
{
    if(TMR1IE==1&&TMR1IF==1)    //定时中断
        {
         
               TMR1IF=0;     //定时中断标志位关闭
                TMR1ON=0;    //定时中断开关关闭

                 key_scan();                    //按键扫描函数
      if(voice_time)                       //控制蜂鸣器声音的长短
                 {
                        beep_dr=1;         //蜂鸣器响
                      --voice_time_cnt;        //蜂鸣器响的声音长短的计数延时
                 }
                else 
                {
beep_dr=0;      //蜂鸣器停止
                }
      TMR1H=0xF5;     //重新设置定时时间间隔
      TMR1L=0x5F;
      TMR1ON=1;        //定时中断开关打开
    }
}




void delay1(unsigned int de)
{
        unsigned int t;
        for(t=0;t<de;t++);
}


(6)小结:
  以上是我常用的编程结构。后续我做的所有项目基本上是这样一种编程结构。这一节技术上要特别重视按键扫描。有按键触发之后,不要马上响应,要延时一段时间去抖动,此处本人设计非常巧妙,很多人仅仅知道按键延时的时候要保证还能去处理别的程序,这样是还不够的,在延时去抖动的时候,还必须要监控延时这段时间里,按键IO输入口是否会由于受到某种干扰突然由低变成高,如果一旦变成高,那么延时计数器delay_cnt1必须重新清零,我当年就是因为这样处理,把卖给富士康100台受干扰死机的设备修好了,老板马上给我加薪1000元。


第二节:独立按键扫描与蜂鸣器
(1)学习目标:利用上一节的程序框架,把按键行列扫描方式改成独立按键扫描方式。通过这节的练习,加深熟悉吴坚鸿的程序框架,以及独立按键扫描的编程方式。
(2)功能需求:每按一个按键,蜂鸣器就响一次。
(3)硬件原理:
(a)用4个IO来做独立按键扫描, 4个IO口都必须接上拉电阻20K左右。
(b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。
(4)源码适合的单片机:PIC18F4620,晶振为22.1184MHz
(5)源代码讲解如下:
#include<pic18.h>         //包含芯片相关头文件

//补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr

#define  beep_dr  LATA1  //蜂鸣器输出


#define key_sr1 RB6    //独立按键输入
#define key_sr2 RB7   //独立按键输入

#define key_sr3 RB3  //独立按键输入
#define key_sr4 RB4 //独立按键输入


//补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量
//前缀都用cnt_表示。凡是延时常量,都应该根据上机实际情况来调整出最佳的数值
#define cnt_delay_cnt1   25   //按键去抖动延时阀值
#define cnt_voice_time   60  //蜂鸣器响的声音长短的延时阀值

//补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中
//断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。
void key_scan();                         //按键扫描函数,放在定时中断里
void key_service();                            //按键服务函数,放在main函数循环里


//补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名
//后缀都用_step表示。

unsigned char key_step=1;      //按键扫描步骤变量,在switch()语句的括号里
//补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名
//后缀都用_lock表示。
unsigned char key_lock1=0;   //按键自锁标志
unsigned char key_lock2=0;   //按键自锁标志
unsigned char key_lock3=0;   //按键自锁标志
unsigned char key_lock4=0;   //按键自锁标志

//补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量
//后缀都用_cnt表示。
unsigned int  delay_cnt1=0;     //延时计数器的变量
unsigned int  delay_cnt2=0;     //延时计数器的变量
unsigned int  delay_cnt3=0;     //延时计数器的变量
unsigned int  delay_cnt4=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=1;            //配置独立按键IO为输入
    TRISB4=1;           //配置独立按键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()                                //按键扫描函数
{  
    //补充说明:因为独立按键扫描没有了行列按键扫描的delay1(40)延时,所以速度很
//快,可以一次中断扫描完所有的按键,不用switch(key_step)语句来分段
  If(key_sr1==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
key_lock1=0;  //按键自锁标志清零
delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
Else if(key_lock1==0)  //有按键按下,且是第一次被按下
{
++delay_cnt1;  //延时计数器
If(delay_cnt1>cnt_delay_cnt1)
{
delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                  key_sec=1;         //触发1号键
}
}

  If(key_sr2==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
key_lock2=0;  //按键自锁标志清零
delay_cnt2=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
Else if(key_lock2==0)  //有按键按下,且是第一次被按下
{
++delay_cnt2;  //延时计数器
If(delay_cnt2>cnt_delay_cnt1)
{
delay_cnt2=0;
key_lock2=1;  //自锁按键置位,避免一直触发
                  key_sec=2;         //触发2号键
}
}


  If(key_sr3==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
key_lock3=0;  //按键自锁标志清零
delay_cnt3=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
Else if(key_lock3==0)  //有按键按下,且是第一次被按下
{
++delay_cnt3;  //延时计数器
If(delay_cnt3>cnt_delay_cnt1)
{
delay_cnt3=0;
key_lock3=1;  //自锁按键置位,避免一直触发
                  key_sec=3;         //触发3号键
}
}


  If(key_sr4==1)    //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
key_lock4=0;  //按键自锁标志清零
delay_cnt4=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
Else if(key_lock4==0)  //有按键按下,且是第一次被按下
{
++delay_cnt4;  //延时计数器
If(delay_cnt4>cnt_delay_cnt1)
{
delay_cnt4=0;
key_lock4=1;  //自锁按键置位,避免一直触发
                  key_sec=4;         //触发4号键
}
}

}

void key_service()                //按键服务函数
{
        switch(key_sec)                //按键服务状态切换
        {
                case 1:// 1号键


// 补充说明:voice_time_cnt只要不为0蜂鸣器就会响,中断里判断voice_time_cnt不为0
//时,会不断自减,一直到它为0时,自动把蜂鸣器关闭
                                  voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                                                                                          
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                case 2:// 2号键
                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                case 3://3号键

                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                case 4://4号键

                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        



                        
        }                
}




//中断
void interrupt timer1rbint(void)
{
    if(TMR1IE==1&&TMR1IF==1)    //定时中断
        {
         
               TMR1IF=0;     //定时中断标志位关闭
                TMR1ON=0;    //定时中断开关关闭

                 key_scan();                    //按键扫描函数
      if(voice_time)                       //控制蜂鸣器声音的长短
                 {
                        beep_dr=1;         //蜂鸣器响
                      --voice_time_cnt;        //蜂鸣器响的声音长短的计数延时
                 }
                else 
                {
beep_dr=0;      //蜂鸣器停止
                }
      TMR1H=0xF5;     //重新设置定时时间间隔
      TMR1L=0x5F;
      TMR1ON=1;        //定时中断开关打开
    }
}


(6)小结:
利用上一节的程序框架,把行列扫描改成独立按键扫描的速度非常快,体现了吴坚鸿程序框架的优越性。通过此节的学习,让大家加深熟悉吴坚鸿程序框架,同时也掌握了独立按键的编程方式。

第三节:AD按键扫描与蜂鸣器

(1)        开场白:
AD按键可以实现一个IO口驱动几个按键的目的,以鸿哥的经验,一个IO口控制的按键数量最好不要超过3个。从实际应用来考虑,独立按键的方式最好,其次是行列按键,最次是AD按键。
(2)功能需求:每按一个按键,蜂鸣器就响一次。
(3)硬件原理:
(a)用4个电阻竖着串联起来,最上端接5V,最下端接地。从上往下,最上端接5V的算第一个节点,最下端接地算最后一个节点,共5个节点。用一个带AD的IO口连接到第2个节点上,此节点上连接第一个按键,按键的另一端接地。以此方式,第二个按键连接到第3个节点,第三个按键连接到第4个节点。这4个电阻的目的主要是用来分压,靠不同的电压来识别不同的按键。
(b)用1个IO经过8050三极管来驱动有源蜂鸣器,有源蜂鸣器通电就一直响,断电就停止。
(4)源码适合的单片机:PIC16f73,晶振为4MHz
(5)源代码讲解如下:
#include<pic.h>         //包含芯片相关头文件

//补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr

#define  beep_dr  RB0  //蜂鸣器输出

//补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量
//前缀都用cnt_表示。凡是延时常量,都应该根据上机实际情况来调整出最佳的数值
#define cnt_delay_cnt1   40  //按键去抖动延时阀值
#define cnt_voice_time   75 //蜂鸣器响的声音长短的延时阀值
#define cnt_key_nc  185   //没有按键按下时电压对应的AD数值
#define cnt_key1_up      185   //1号按键按下时电压对应的AD数值上限
#define cnt_key2_down  117  //2号按键按下时电压对应的AD数值下限
#define cnt_key2_up      137  //2号按键按下时电压对应的AD数值上限
#define cnt_key3_down  160  //3号按键按下时电压对应的AD数值下限
#define cnt_key3_up      175  //3号按键按下时电压对应的AD数值上限




//补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中
//断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。
void key_scan();                         //按键扫描函数,放在定时中断里
void key_service();                            //按键服务函数,放在main函数循环里
void ad_samping();                         //AD采样, 放在main函数循环里
void delay100();    //小延时

//补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名
//后缀都用_step表示。

unsigned char ad_step=0;     //AD扫描步骤变量,
//补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名
//后缀都用_lock表示。
unsigned char key_lock1=0;   //按键自锁标志

//补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量
//后缀都用_cnt表示。
unsigned int  delay_cnt1=0;     //延时计数器的变量
unsigned int voice_time_cnt;        //蜂鸣器响的声音长短的计数延时

//补充说明:吴坚鸿程序风格是这样的,凡是做类型的变量的分类
//后缀都用_sec表示。
Unsigned char key_sec=0;  //哪个按键被触发

//补充说明:吴坚鸿程序风格是这样的,凡是只有两种状态(0或者1)的变量,
//后缀都用_flag表示。
Unsigned char AD_Flag=0;   //用来指示单片机内部硬件AD处理完成的标志

//跟AD有关的变量
Unsigned char key_value=0;     //跟电压成比例关系的AD数值

//主程序
main()
{

            ADCON0=0x40;  //设置AD模式
        ADCON1=0x04;  // AD输入通道
        TRISA0=1;
        TRISA1=1;
        TRISA3=1;

        TRISB0=0;   //配置蜂鸣器输出

        

    

        T1CON=0x24;//定时中断
          TMR1H=0xFE;
          TMR1L=0xEF;
        INTCON=0xC0;
        TMR1IF=0;
        TMR1IE=1;

              PIE1=0X00;
               PIE2=0X00;
            ADIF=0;                                //A/D转换中断允许
            ADIE=1;                                //A/D转换中断允许
              PEIE=1;                                //外围中断允许
          GIE=1;
          TMR1ON=1;   //开定时中断
//补充说明,以上的内容为寄存器配置,每种不同的单片机会有点差异,
//大家不用过度关注以上寄存器的配置,只要知道有这么一回事即可

    beep_dr=0;                               //关蜂鸣器,上电初始化IO

   while(1)    
   {
                     CLRWDT(); //喂看门狗,大家不用过度关注此行
ad_samping();   //AD采样
                key_service();        //按键服务
}

}
       

void key_scan()                                //按键扫描函数
{  
  if(key_value>cnt_key_nc) //空闲,没有按下
  {
key_lock1=0;  //按键自锁标志清零
delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙        
}
  else if(key_lock1==0) //有按键按下,且是第一次被按下
  {
       if(key_value<cnt_key1_up)   //K1按下
       {
++delay_cnt1;  //延时计数器
    If(delay_cnt1>cnt_delay_cnt1)
               {
delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                        key_sec=1;         //触发1号键
                }
        }            
        else if(key_value> cnt_key2_down&&key_value< cnt_key2_up)   //K2按下
       {
++delay_cnt1;  //延时计数器
    If(delay_cnt1>cnt_delay_cnt1)
               {
delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                        key_sec=2;         //触发2号键
                }
         }            
      else if(key_value> cnt_key3_down&&key_value< cnt_key3_up)   //K3按下                              {
++delay_cnt1;  //延时计数器
    If(delay_cnt1>cnt_delay_cnt1)
               {
delay_cnt1=0;
key_lock1=1;  //自锁按键置位,避免一直触发
                        key_sec=3;         //触发3号键
                }
       }
}     
void key_service()                //按键服务函数
{
        switch(key_sec)                //按键服务状态切换
        {
                case 1:// 1号键


// 补充说明:voice_time_cnt只要不为0蜂鸣器就会响,中断里判断voice_time_cnt不为0
//时,会不断自减,一直到它为0时,自动把蜂鸣器关闭
                                  voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                                                                                          
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                case 2:// 2号键
                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                case 3://3号键

                                 voice_time_cnt= cnt_voice_time;    //蜂鸣器响“滴”一声就停
                        key_sec=0;   //相应完按键处理程序之后,把按键选择变量清零,
//避免一直触发
                        break;        
                        
        }                
}

void ad_samping()    //AD采样函数,放在main循环里
{

switch(ad_step)    
{     
case 0:
      ADCON0=0x59;        //切换直流输入通道AD采样AN3
      delay100();      //此处为无厘头一个。如果有两个以上的AD通道进行切换,必须把这//个延时加上,否则不好使。这个完全是我的实战经验,这样的事情我经常会遇到,这种
//事情完全靠经验,我当年第一次遇到的时候也被折腾了好久才发现。当然这个项目只有
//1个AD通道,所以也许可以不用。
      ADIF=0;
      CLRWDT();
      ADGO=1;                //启动AD 
      ad_step=1;            //下一次循环进入下一个步骤,用switch来切换流程,一直是我
//的最爱,尤其是通道多的时候,它的优越性更加能发挥得淋漓尽致。
      break;
case 1:
        if(AD_Flag==1)                        // AD采样完成
         {
             AD_Flag=0;
            key_value=ADRES;       //采集按键电压的AD数值,
            ADIF=0;
            CLRWDT();
            ADGO=1;                //启动AD 

            ad_step=0;     //下次循环切换回最前面那个步骤,这种控制方式我最喜欢


         }
        break;
}
}

//小延时
void delay100()
{
  unsigned char k;
    for(k=0;k<100;k++);
}


//中断
void interrupt timer1rbint(void)
{

if(ADIF==1&&ADIE==1)                //AD转换完成中断
{
              TMR1IE=0;   //禁止定时中断,避免两个中断相互扯淡
           ADIF=0;                                //清除中断标志
           AD_Flag=1;                        //置AD转换完成标志
              TMR1IE=1;  //允许定时中断
}

    if(TMR1IE==1&&TMR1IF==1)    //定时中断
        {
         
                   ADIE=0;        
         
               TMR1IF=0;
                TMR1ON=0; 

                 key_scan();                    //按键扫描函数
      if(voice_time)                       //控制蜂鸣器声音的长短
                 {
                        beep_dr=1;         //蜂鸣器响
                      --voice_time_cnt;        //蜂鸣器响的声音长短的计数延时
                 }
                else 
                {
beep_dr=0;      //蜂鸣器停止
                }

           TMR1H=0xFF;
           TMR1L=0xC8;
           TMR1ON=1;
            ADIE=1;        
    }
}


(6)小结:
有两路AD通道进行切换时,必须加一个小延时delay100(),否则会出现无厘头现象。“无厘头现象“是鸿哥发明的一个新词,专门用来表示那些莫名其妙的,用理论不好解释的现象。 (未完待续下一节)

相关文章:    

  吴坚鸿谈学单片机有前途还是嵌入式系统有前途(原文论坛版)

  吴坚鸿谈学单片机有前途还是嵌入式系统有前途(博文转发版)

  《谈学单片机有前途还是嵌入式系统有前途》一文吴坚鸿回复整理(1) 

  《谈学单片机有前途还是嵌入式系统有前途》一文吴坚鸿回复整理(2)

                                        吴坚鸿系列文章为电子发烧友原创,如若转载,请注明出处!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多