找回密码
 立即注册
楼主: lclbf

STC8H8K64U开发箱V9.62学习笔记

[复制链接]
  • 打卡等级:以坛为家I
  • 打卡总天数:396
  • 最近打卡:2025-05-08 07:41:36

45

主题

381

回帖

1595

积分

金牌会员

静坐常思己过,闲谈莫论人非

积分
1595
发表于 2024-1-4 12:31:14 | 显示全部楼层
你说的程序框架是本书吗?哪里可以买到?
处事要代人所想,读书需切己用功
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-4 15:19:19 | 显示全部楼层
本帖最后由 lclbf 于 2024-1-4 15:21 编辑
reng*** 发表于 2024-1-4 12:31
你说的程序框架是本书吗?哪里可以买到?

没有书,我有原版PDF格式的。没有对应的开发版,后面很多程序没有办法实验了。
上传了,你自己下载。

手把手教你单片机程序框架.pdf

3.96 MB, 下载次数: 121

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:396
  • 最近打卡:2025-05-08 07:41:36

45

主题

381

回帖

1595

积分

金牌会员

静坐常思己过,闲谈莫论人非

积分
1595
发表于 2024-1-4 16:55:04 | 显示全部楼层
lcl*** 发表于 2024-1-4 15:19
没有书,我有原版PDF格式的。没有对应的开发版,后面很多程序没有办法实验了。
上传了,你自己下载。
...

谢谢
处事要代人所想,读书需切己用功
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-5 08:29:38 | 显示全部楼层
第十一课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
上一节讲了类似电脑键盘组合按键触发的功能,这节要教会大家一个知识点:如何在上一节的基础上,略作
修改,就可以实现同一个按键短按与长按的区别触发。
具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。用矩阵键盘中的SW32和SW36号键作为独立按键,记得把输出线 P0.0
一直输出低电平,模拟独立按键的触发地 GND。
(2)实现功能:两个独立按键 S1 和 S5,按住其中一个按键,在短时间内松手,则认为是短按,触发蜂鸣器短
鸣一声。如果一直按住这个按键不松手,那么超过规定的长时间内,则认为是长按,触发蜂鸣器长鸣一声。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_voice_short 20                         //蜂鸣器短叫的持续时间
#define const_voice_long  140                 //蜂鸣器长叫的持续时间

/* 注释一:
* 调整抖动时间阀值的大小,可以更改按键的触发灵敏度。
* 去抖动的时间本质上等于累计定时中断次数的时间。
*/
#define const_key_time_short1 20  //短按的按键去抖动延时的时间
#define const_key_time_long1         400 //长按的按键去抖动延时的时间
#define const_key_time_short2 20  //短按的按键去抖动延时的时间
#define const_key_time_long2         400 //长按的按键去抖动延时的时间

void initial_myself();
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();                                                                                 //定时中断函数
void key_service();                                                                 //按键服务的应用程序
void key_scan();                                                                                 //按键扫描函数 放在定时中断里

sbit key_sr1=P0^6;                                                                         //STC8H8K64U开发箱9.62版 SW32键
sbit key_sr2=P0^7;                                                                         //STC8H8K64U开发箱9.62版 SW36键
sbit key_gnd_dr=P0^0;                                                         //模拟独立按键的地 GND,因此必须一直输出低电平
sbit beep_dr=P5^4;                                                                         //蜂鸣器的驱动 IO 口
sbit LEDonoff_dr=P4^0;                                                         //LED控制开关
sbit LED1_dr=P6^0;                                                                         //LED1灯
sbit LED2_dr=P6^2;                                                                         //LED2灯
sbit LED3_dr=P6^4;                                                                         //LED3灯
sbit LED4_dr=P6^6;                                                                         //LED4灯

unsigned char ucKeySec=0;                                         //被触发的按键编号
unsigned int         uiKeyTimeCnt1=0;                   //按键去抖动延时计数器
unsigned char ucKeyLock1=0;                                 //按键触发后自锁的变量标志
unsigned char ucShortTouchFlag1=0;         //短按的触发标志
unsigned int         uiKeyTimeCnt2=0;                         //按键去抖动延时计数器
unsigned char ucKeyLock2=0;                                 //按键触发后自锁的变量标志
unsigned char ucShortTouchFlag2=0;         //短按的触发标志
unsigned int        uiVoiceCnt=0;                                 //蜂鸣器鸣叫的持续时间计数器


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        while(1)
        {
                key_service(); //按键服务的应用程序
        }
}


void key_scan()    //按键扫描函数 放在定时中断里
{
/* 注释二:
* 长按与短按的按键扫描的详细过程:
* 第一步:平时只要按键没有被按下时,按键的自锁标志,去抖动延时计数器一直被清零。
* 第二步:一旦两个按键都被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到
* 阀值 const_key_time_short1 或者 const_key_time_long1 时,如果在这期间由于受外界干扰或者按键抖动,
而使
* IO 口突然瞬间触发成高电平,这个时候马上把延时计数器 uiKeyTimeCnt1
* 清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。这是我实战中摸索出来的。
* 以后凡是用到开关感应器的时候,都可以用类似这样的方法去干扰。
* 第三步:如果按键按下的时间超过了短按阀值 const_key_time_short1,则马上把短按标志 ucShortTouchFlag1=1;
* 如果还没有松手,一旦发现按下的时间超过长按阀值 const_key_time_long1 时,
* 先把短按标志 ucShortTouchFlag1 清零,然后触发长按。在这段程序里,把自锁标志 ucKeyLock1 置位,
* 是为了防止按住按键不松手后一直触发。
* 第四步:等按键松开后,自锁标志 ucKeyLock12 及时清零,为下一次自锁做准备。如果发现 ucShortTouchFlag1
等于 1,
* 说明短按有效,这时触发一次短按。
* 第五步:以上整个过程,就是识别按键 IO 口下降沿触发的过程。
*/
        if(key_sr1==1)                                                                        //IO 是高电平,说明两个按键没有全部被按下,这时要及时清零一些标志位
        {        
                ucKeyLock1=0;                                                                 //按键自锁标志清零
                uiKeyTimeCnt1=0;                                                        //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
               
                if(ucShortTouchFlag1==1)                         //短按触发标志
                {
                        ucShortTouchFlag1=0;                                 //短按触发标志复位
                        ucKeySec=1;                                                                 //触发一号键的短按
                }
        }
        
        else if(ucKeyLock1==0)                                //有按键按下,且是第一次被按下
        {        
                uiKeyTimeCnt1++;                                                 //累加定时中断次数
                if(uiKeyTimeCnt1>const_key_time_short1)
                {
                        ucShortTouchFlag1=1;                         //激活按键短按的有效标志
                }
                if(uiKeyTimeCnt1>const_key_time_long1)
                {
                        ucShortTouchFlag1=0;                         //清除按键短按的有效标志
                        uiKeyTimeCnt1=0;
                        ucKeyLock1=1;                                                 //自锁按键置位,避免一直触发
                        ucKeySec=2;                                                         //触发 1 号键的长按
                }
        }
               
        if(key_sr2==1)                                                                //IO 是高电平,说明两个按键没有全部被按下,这时要及时清零一些标志位
        {
                ucKeyLock2=0;                                                         //按键自锁标志清零
                uiKeyTimeCnt2=0;                                                //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
                if(ucShortTouchFlag2==1)          //短按触发标志
                {
                        ucShortTouchFlag2=0;                         //短按触发标志复位
                        ucKeySec=3;                                                          //触发 2 号键的短按
                }
        }
        
        else if(ucKeyLock2==0)                        //有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt2++;                                         //累加定时中断次数
                if(uiKeyTimeCnt2>const_key_time_short2)
                {
                        ucShortTouchFlag2=1;                 //激活按键短按的有效标志
                }
                if(uiKeyTimeCnt2>const_key_time_long2)
                {
                        ucShortTouchFlag2=0;                 //清除按键短按的有效标志
                        uiKeyTimeCnt2=0;
                        ucKeyLock2=1;                                                //自锁按键置位,避免一直触发
                        ucKeySec=4;                                                 //触发 2 号键的长按
                }
        }
}


void key_service()                                                                                 //第三区 按键服务的应用程序
{
        switch(ucKeySec)                                                                                 //按键服务状态切换
        {
                case 1:                                                                                                                // 1 号键的短按 对应STC8H8K64U开发箱9.62版 SW32键
                        uiVoiceCnt=const_voice_short;         //按键声音的短触发,滴一声就停。
                        LED1_dr = ~LED1_dr;                                                        //LED1点亮
                  ucKeySec=0;                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 2:                                                                                                                // 1 号键的长按 对应STC8H8K64U开发箱9.62版 SW32键
                        uiVoiceCnt=const_voice_long;                 //按键声音的长触发,滴一声就停。
                        LED2_dr = 0;                                                                                //LED2点亮
                        ucKeySec=0;                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 3:                                                                                                                // 2 号键的短按 对应STC8H8K64U开发箱9.62版 SW36键
                        uiVoiceCnt=const_voice_short;         //按键声音的短触发,滴一声就停。
                        P6 = 0X00;                                                                                        //P6口LED全部打开
                        ucKeySec=0;                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 4:                                                                                                                // 2 号键的长按 对应STC8H8K64U开发箱9.62版 SW36键
                        uiVoiceCnt=const_voice_long;                 //按键声音的长触发,滴一声就停。
//                        LED1_dr = 1;                                                                                //LED1关闭
//                        LED2_dr = 1;                                                                                //LED2关闭
                        P6 = 0XFF;                                                                                        //P6口LED全部关闭
                        ucKeySec=0;                                                                                  //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
        }
}


void T0_time() interrupt 1
{
        TF0=0;                                                                         //清除中断标志
        TR0=0;                                                                         //关中断
        
        key_scan();                                                 //按键扫描函数
        
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--;                                 //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0;                                          //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        else
        {
                ;                                                                                 //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1;                                                 //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        
        TH0=0xf8;                                                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1;                                                                         //开中断
}



void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)                         //内嵌循环的空指令数量
                {
                        ;                                                                                 //一个分号相当于执行一条空语句
                }
        }
}

void initial_myself()                         //第一区 初始化单片机
{
/* 注释三:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把 key_gnd_dr 输出低电平。
* STC8H8K64U开发箱9.62版 SW32键SW36键两个按键就是本程序中用到的两个独立按键。
*/
        P0M0 = 0X00;                                                                //P0口设置为准双向口
        P0M1 = 0X00;
        
        P4M0 = 0X00;                                                                //P40口设置为准双向口,其他设置为高阻输入
        P4M1 = 0XFE;
        
        P5M0 = 0X10;                                                                //P54口设置为开漏输出,其他口设置为高阻输入
        P5M1 = 0XFF;
        
        P6M0 = 0X00;                                                                //P6口设置为准双向口
        P6M1 = 0X00;
        
        LEDonoff_dr = 0;            //LED控制开关开
        key_gnd_dr=0;                                                         //模拟独立按键的地 GND,因此必须一直输出低电平
        beep_dr=1;                                                                         //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        
        TMOD=0x01;                                                                         //设置定时器 0 为工作方式 1
        TH0=0xf8;                                                                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;         //开总中断
        ET0=1;         //允许定时中断
        TR0=1;         //启动定时中断
}

//总结陈词:
//在很多需要人机交互的项目中,需要用按键来快速加减某个数值,这个时候如果按住一个按键不松手,这个
//数值要有节奏地快速往上加或者快速往下减。要现实这种功能,我们该怎么写程序?欲知详情,请听下回分解-----
//按住一个独立按键不松手的连续步进触发。

11 同一个按键短按与长按的区别触发.rar

50.61 KB, 下载次数: 108

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-5 08:31:00 | 显示全部楼层
本帖最后由 lclbf 于 2024-1-5 08:32 编辑

第十二课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
上一节讲了同一个按键短按与长按的区别触发功能,这节要教会大家两个知识点:
第一个知识点:如何在上一节的基础上,略作修改,就可以实现按住一个独立按键不松手的连续步进触发。
第二个知识点:在单片机的 C 语言编译器中,当无符号数据 0 减去 1 时,就会溢出,变成这个类型数据的最大
值。比如是 unsigned int 类型的 0 减去 1 就等于 65535(0xffff),unsigned char 类型的 0 减去 1 就等于 255(0xff)。这
个常识经常要用在判断数据临界点的地方。比如一个数最大值是 20,最小值是 0。这个数据一直往下减,当我们
发现它突然大于 20 的时候,就知道它溢出了,这个时候要及时把它赋值成 0 就达到我们的目的。
具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。用矩阵键盘中的SW32和SW36号键作为独立按键,记得把输出线 P0.0
一直输出低电平,模拟独立按键的触发地 GND。
(2)实现功能:两个独立按键 S1 和 S5,S1 键作为加键。S5 键做为减键。每按一次 S1 键则被设置参数 uiSetNumber
自加 1。如果按住 S1 键不松手超过 1 秒钟,被设置参数 uiSetNumber 以每 0.25 秒的时间间隔往上自加 1,一直
加到 20 为止。每按一次 S5 键则被设置参数 uiSetNumber 自减 1。如果按住 S5 键不松手超过 1 秒钟,被设置参
数 uiSetNumber 以每 0.25 秒的时间间隔往下自减 1,一直减到 0 为止。当被设置参数 uiSetNumber 小于 10 的时
候,LED 灯灭;当大于或者等于 10 的时候,LED 灯亮。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"
#define const_voice_short 40   //蜂鸣器短叫的持续时间
#define const_key_time1   20   //按键去抖动延时的时间
#define const_key_time2   20   //按键去抖动延时的时间
#define const_time_0_25s  111  //0.25 秒钟的时间需要的定时中断次数
#define const_time_1s     444  //1 秒钟的时间需要的定时中断次数

void initial_myself();
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();                                         //定时中断函数
void key_service();                         //按键服务的应用程序
void key_scan();                                         //按键扫描函数 放在定时中断里
void led_run();                                         //led 灯的应用程序

sbit key_sr1=P0^6;                                 //STC8H8K64U开发箱9.62版 SW32键
sbit key_sr2=P0^7;                                 //STC8H8K64U开发箱9.62版 SW36键
sbit key_gnd_dr=P0^0;                 //模拟独立按键的地 GND,因此必须一直输出低电平
sbit beep_dr=P5^4;                                 //蜂鸣器的驱动 IO 口
sbit ledonoff_dr=P4^0;                 //LED 控制开关 IO 口
sbit led_dr=P6^0;                                 //LED 的驱动 IO 口

unsigned char ucKeySec=0;               //被触发的按键编号
unsigned int  uiKeyTimeCnt1=0;                 //按键去抖动延时计数器
unsigned int  uiKeyCtntyCnt1=0;         //按键连续触发的间隔延时计数器
unsigned char ucKeyLock1=0;                         //按键触发后自锁的变量标志
unsigned int  uiKeyTimeCnt2=0;                //按键去抖动延时计数器
unsigned int  uiKeyCtntyCnt2=0;         //按键连续触发的间隔延时计数器
unsigned char ucKeyLock2=0;                         //按键触发后自锁的变量标志
unsigned int  uiVoiceCnt=0;                         //蜂鸣器鸣叫的持续时间计数器
unsigned int  uiSetNumber=0;                         //设置的数据


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        
        while(1)
        {
                key_service();         //按键服务的应用程序
                led_run();                         //led 灯的应用程序
        }
}


void led_run()                                 //led 灯的应用程序
{
        if(uiSetNumber<10) //如果被设置的参数 uiSetNumber 小于 10,LED 灯则灭。否则亮。
        {
                led_dr=1;                                 //灭
        }
        else
        {
                led_dr=0;                                 //亮
        }
}


void key_scan()//按键扫描函数 放在定时中断里
{
/* 注释一:
* 独立按键扫描的详细过程:
* 第一步:平时没有按键被触发时,按键的自锁标志,去抖动延时计数器,以及时间间隔延时计数器一直被清零。
* 第二步:一旦有按键被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到
* 阀值 const_key_time1 时,如果在这期间由于受外界干扰或者按键抖动,而使
* IO 口突然瞬间触发成高电平,这个时候马上把延时计数器 uiKeyTimeCnt1
* 清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。这是我实战中摸索出来的。
* 以后凡是用到开关感应器的时候,都可以用类似这样的方法去干扰。
* 第三步:如果按键按下的时间超过了阀值 const_key_time1,则触发按键,把编号 ucKeySec 赋值。
* 同时,马上把自锁标志 ucKeyLock1 置位,防止按住按键不松手后一直触发。
* 第四步:如果此时触发了一次按键后,一直不松手,去抖动延时计时器继续累加,直到超过了 1 秒钟。进入连
续触发模式的程序
* 第五步:在连续触发模式的程序中,连续累加延时计数器开始累加,每 0.25 秒就触发一次。
* 第六步:等按键松开后,自锁标志 ucKeyLock1 和两个延时计时器及时清零,为下一次自锁做准备。
*/
        if(key_sr1==1)                                //IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock1=0;                         //按键自锁标志清零
                uiKeyTimeCnt1=0;                //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
                uiKeyCtntyCnt1=0;         //连续累加的时间间隔延时计数器清零
        }
        
        else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt1++;                 //累加定时中断次数
                if(uiKeyTimeCnt1>const_key_time1)
                {
                        uiKeyTimeCnt1=0;
                        ucKeyLock1=1;                 //自锁按键置位,避免一直触发
                        ucKeySec=1;                         //触发 1 号键
                }
        }
        
        else if(uiKeyTimeCnt1<const_time_1s) //按住累加到 1 秒
        {
                uiKeyTimeCnt1++;
        }
        
        else                                                                         //按住累加到 1 秒后仍然不放手,这个时候进入有节奏的连续触发
        {
                uiKeyCtntyCnt1++;         //连续触发延时计数器累加
                if(uiKeyCtntyCnt1>const_time_0_25s) //按住没松手,每 0.25 秒就触发一次
                {
                        uiKeyCtntyCnt1=0; //
                        ucKeySec=1;                         //触发 1 号键
                }
        }
        
        
        if(key_sr2==1)                                //IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock2=0;                         //按键自锁标志清零
                uiKeyTimeCnt2=0;                //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
                uiKeyCtntyCnt2=0;         //连续累加的时间间隔延时计数器清零
        }
        
        else if(ucKeyLock2==0)        //有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt2++;                         //累加定时中断次数
                if(uiKeyTimeCnt2>const_key_time2)
                {
                        uiKeyTimeCnt2=0;
                        ucKeyLock2=1;                         //自锁按键置位,避免一直触发
                        ucKeySec=2;                                 //触发 2 号键
                }
        }
        
        else if(uiKeyTimeCnt2<const_time_1s) //按住累加到 1 秒
        {
                uiKeyTimeCnt2++;
        }
        
        else                                                                         //按住累加到 1 秒后仍然不放手,这个时候进入有节奏的连续触发
        {
                uiKeyCtntyCnt2++;         //连续触发延时计数器累加
                if(uiKeyCtntyCnt2>const_time_0_25s) //按住没松手,每 0.25 秒就触发一次
                {
                        uiKeyCtntyCnt2=0;
                        ucKeySec=2; //触发 2 号键
                }
        }
}



void key_service()                                 //第三区 按键服务的应用程序
{
        switch(ucKeySec)                                 //按键服务状态切换
        {
                case 1:                                                                // 1 号键 连续加键 对应STC8H8K64U开发箱9.62版 SW32键
                        uiSetNumber++;                         //被设置的参数连续往上加
                        if(uiSetNumber>20)         //最大是 20
                        {
                                uiSetNumber=20;
                        }
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                        ucKeySec=0;                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                        
                case 2:                                                                // 2 号键 连续减键 对应STC8H8K64U开发箱9.62版 SW36键
                /* 注释二:
                * 在单片机的 C 语言编译器中,当无符号数据 0 减去 1 时,就会溢出,变成这个类型数据的最大值。
                * 比如是 unsigned int 的 0 减去 1 就等于 65535(0xffff),unsigned char 的 0 减去 1 就等于 255(0xff)
                */
                        uiSetNumber--;                         //被设置的参数连续往下减
                        if(uiSetNumber>20)  //最小是 0.为什么这里用 20?因为 0 减去 1 就是溢出变成了 65535(0xffff)
                        {
                                uiSetNumber=0;
                        }
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                        ucKeySec=0;                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
        }
}


void T0_time() interrupt 1
{
        TF0=0;                                                                                                 //清除中断标志
        TR0=0;                                                                                                 //关中断
        key_scan();                                                                         //按键扫描函数
        
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--;                                                         //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0;                                                                         //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        
        else
        {
                ;                                                                                                         //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1;                                                                         //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        
        TH0=0xf8;                                                                                 //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1;                                                                                                 //开中断
}



void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)                         //内嵌循环的空指令数量
                {
                        ;                                                                                         //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
/* 注释三:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把 key_gnd_dr 输出低电平。
* STC8H8K64U开发箱9.62版 SW32键SW36键 两个按键就是本程序中用到的两个独立按键。
*/
        P0M0 = 0X00;                        //P0口设置为准双向口
        P0M1 = 0X00;
        
        P4M0 = 0X00;                        //P4口设置为准双向口
        P4M1 = 0X00;
        
        P5M0 = 0X00;                        //P5口设置为准双向口
        P5M1 = 0X00;
        
        P6M0 = 0X00;                        //P6口设置为准双向口
        P6M1 = 0X00;
        
        ledonoff_dr = 0;         //LED控制开关打开
        key_gnd_dr=0;                 //模拟独立按键的地 GND,因此必须一直输出低电平
        beep_dr=1;                                 //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        led_dr=0;                                 //LED 灯灭
        TMOD=0x01;                                 //设置定时器 0 为工作方式 1
        TH0=0xf8;                                 //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;         //开总中断
        ET0=1;         //允许定时中断
        TR0=1;         //启动定时中断
}

//总结陈词:
// 本程序可以有节奏地快速往上加或者快速往下减。假如被设置数据的范围不是 20,而是 1000。如果按 0.25
//秒的节奏往上加,那不是累死人了?如果直接把 0.25 秒的节奏调快到 0.01 秒,那么到达 999 的时候,还来不及松
//手就很容易超过头,不好微调。有没有完整的方案解决这个问题?当然有。欲知详情,请听下回分解-----按住一
//个独立按键不松手的加速匀速触发。
        

12 按住一个独立按键不松手的连续步进触发.rar

52.13 KB, 下载次数: 105

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-5 08:33:00 | 显示全部楼层
第十三课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
上一节讲了按住一个独立按键不松手的连续步进触发功能,这节要教会大家如何在上一节的基础上,略作修
改,就可以实现按键的加速匀速触发。
具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。用矩阵键盘中的SW32和SW36号键作为独立按键,记得把输出线 P0.0
一直输出低电平,模拟独立按键的触发地 GND。
(2)实现功能:两个独立按键 SW32和SW36,SW32 键作为加键。SW36 键做为减键。每按一次 SW32 键则被设置参数 uiSetNumber
自加 1。如果按住 SW32 键不松手超过 1 秒钟,被设置参数 uiSetNumber 以不断变快的时间间隔往上自加 1,这个称
为加速触发的功能,直到到达极限值,则以固定的速度加 1,这个过程叫匀速。SW36 作为减法按键,每触发一次,
uiSetNumber 就减 1,其加速和匀速触发功能跟 SW32 按键一样。当被设置参数 uiSetNumber 小于 500 的时候,LED
灯灭;当大于或者等于 500 的时候,LED 灯亮。需要注意的是:
第一步:每次按下去触发一次单击按键,如果按下去到松手的时间不超过 1 秒,则不会进入连续加速触发模式。
第二步:如果按下去不松手的时间超过 1 秒,则进入连续加速触发模式。按键触发节奏不断加快,蜂鸣器鸣叫的
节奏也不断加快。直到它们都到达一个极限值,然后以此极限值间隔匀速触发。在刚开始加速的时候,按键触发
与蜂鸣器触发的步骤是一致的,等它们任意一个达到极限值的时候,急促的声音跟按键的触发不一致,并不是蜂
鸣器每叫一次,按键就触发一次。实际上加速到最后,按键触发的速度远远比蜂鸣器的触发速度快。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_voice_short         40         //蜂鸣器短叫的持续时间
#define const_key_time1                 20         //按键去抖动延时的时间
#define const_key_time2                 20         //按键去抖动延时的时间
#define const_time_1s                         444 //1 秒钟的时间需要的定时中断次数
#define const_initial_set         160 //连续触发模式的时候,按键刚开始的间隔触发时间
#define const_min_level                 30         //连续触发模式的时候,按键经过加速后,如果一旦发现
                                                                                                                                //小于这个值,则直接变到最后的间隔触发时间
#define const_sub_dt                                 10         //按键的"加速度",相当于按键间隔时间每次的变化量
#define const_last_min_set         5         //连续触发模式的时候,按键经过加速后,最后的间隔触发时间
#define const_syn_min_level 45         //产生同步声音的最小阀值 这个时间必须要比蜂鸣器的时间略长一点。

void initial_myself();
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();                                         //定时中断函数
void key_service();                         //按键服务的应用程序
void key_scan();                                         //按键扫描函数 放在定时中断里
void led_run();                                         //led 灯的应用程序

sbit key_sr1=P0^6;                                 //对应STC8H8K64U开发箱9.62版 SW32键
sbit key_sr2=P0^7;                                 //对应STC8H8K64U开发箱9.62版 SW36键
sbit key_gnd_dr=P0^0;                 //模拟独立按键的地 GND,因此必须一直输出低电平
sbit beep_dr=P5^4;                                 //蜂鸣器的驱动 IO 口
sbit ledonoff_dr=P4^0;                 //LED 控制 IO 口
sbit led_dr=P6^0;                                 //LED 的驱动 IO 口


unsigned char ucKeySec=0; //被触发的按键编号
unsigned int  uiKeyTimeCnt1=0;         //按键去抖动延时计数器
unsigned int  uiKeyCtntyCnt1=0; //按键连续触发的间隔延时计数器
unsigned char ucKeyLock1=0;                 //按键触发后自锁的变量标志
unsigned int  uiSynCtntyCnt1=0; //产生按键同步声音的计数器
unsigned int  uiCtntyTimeSet1=const_initial_set; //按键每次触发的时间间隔,这数值不断变小,导致速度不断加快
unsigned int  uiCtntySynSet1=const_initial_set;                //同步声音的时间间隔,这数值不断变小,导致速度不断加快
unsigned char ucCtntyFlag1=0;         //是否处于连续加速触发模式的标志位
unsigned int  uiKeyTimeCnt2=0;         //按键去抖动延时计数器
unsigned int  uiKeyCtntyCnt2=0; //按键连续触发的间隔延时计数器
unsigned char ucKeyLock2=0;                 //按键触发后自锁的变量标志
unsigned int  uiSynCtntyCnt2=0; //产生按键同步声音的计数器
unsigned int  uiCtntyTimeSet2=const_initial_set; //按键每次触发的时间间隔,这数值不断变小,导致速度不断加快
unsigned int  uiCtntySynSet2=const_initial_set;         //同步声音的时间间隔,这数值不断变小,导致速度不断加快
unsigned char ucCtntyFlag2=0;         //是否处于连续加速触发模式的标志位
unsigned int  uiVoiceCnt=0;                 //蜂鸣器鸣叫的持续时间计数器
unsigned int  uiSetNumber=0;                 //设置的数据


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        
        while(1)
        {
                key_service();                 //按键服务的应用程序
                led_run();                                //led 灯的应用程序
        }
}


void led_run()                                 //led 灯的应用程序
{
        if(uiSetNumber<500) //如果被设置的参数 uiSetNumber 小于 500,LED 灯则灭。否则亮。
        {
                led_dr=1;                                 //灭
        }
        else
        {
                led_dr=0;                                 //亮
        }
}



void key_scan()//按键扫描函数 放在定时中断里
{
/* 注释一:
* 独立按键连续加速扫描的过程:
* 第一步:每次按下去触发一次单击按键,如果按下去到松手的时间不超过 1 秒,则不会进入连续加速触发模式。
* 第二步:如果按下去不松手的时间超过 1 秒,则进入连续加速触发模式。按键触发节奏不断加快,蜂鸣器鸣叫
  的节奏
* 也不断加快。直到它们都到达一个极限值,然后以此极限值间隔匀速触发。在刚开始加速的时候,按键
  触发与
* 蜂鸣器触发的步骤是一致的,等它们任意一个达到极限值的时候,急促的声音跟按键的触发不一致,并
  不是
* 蜂鸣器每叫一次,按键就触发一次。实际上加速到最后,按键触发的速度远远比蜂鸣器的触发速度快。
*/
        if(key_sr1==1)                                //IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock1=0;                         //按键自锁标志清零
                uiKeyTimeCnt1=0;                //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
                uiKeyCtntyCnt1=0;         //按键连续加速的时间间隔延时计数器清零
                uiSynCtntyCnt1=0;         //蜂鸣器连续加速的时间间隔延时计数器清零
                uiCtntyTimeSet1=const_initial_set;         //按键每次触发的时间间隔初始值,这数值不断变小,导致速度不断加快
                uiCtntySynSet1=const_initial_set;         //同步声音的时间间隔初始值,这数值不断变小,导致鸣叫的节奏不断加快
        }
        
        else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt1++;                 //累加定时中断次数
                if(uiKeyTimeCnt1>const_key_time1)
                {
                        uiKeyTimeCnt1=0;
                        ucKeyLock1=1;                 //自锁按键置位,避免一直触发
                        ucCtntyFlag1=0;         //连续加速触发模式标志位 0 代表单击 1 代表连续加速触发
                        ucKeySec=1;                         //触发 1 号键
                }
        }
        
        else if(uiKeyTimeCnt1<const_time_1s) //按住累加到 1 秒
        {
                uiKeyTimeCnt1++;
        }
        
        else                                                                         //按住累加到 1 秒后仍然不放手,这个时候进入有节奏的连续加速触发
        {
                uiKeyCtntyCnt1++;         //按键连续触发延时计数器累加
                                                                                                //按住没松手,每隔一段 uiCtntyTimeSet1 时间按键就触发一次,
                                                                                                //而且 uiCtntyTimeSet1 不断减小,速度就越来越快
                if(uiKeyCtntyCnt1>uiCtntyTimeSet1)
                {
                        if(uiCtntyTimeSet1>const_min_level)
                        {
                                uiCtntyTimeSet1=uiCtntyTimeSet1-const_sub_dt; //uiCtntyTimeSet1 不断减小,速度就越来越快
                        }
                        else
                        {
                                uiCtntyTimeSet1=const_last_min_set;                                         //uiCtntyTimeSet1 不断减小,到达一个极限值
                        }
                        uiKeyCtntyCnt1=0;
                        ucCtntyFlag1=1;                                                                                                                                 //进入连续加速触发模式
                        ucKeySec=1;                                                                                                                                                 //触发 1 号键
                }
                uiSynCtntyCnt1++;                                 //蜂鸣器连续触发延时计数器累加
                                                                                                                        //按住没松手,每隔一段 uiCtntySynSet1 时间蜂鸣器就触发一次,
                                                                                                                        //而且 uiCtntySynSet1 不断减小,鸣叫的节奏就越来越快
                if(uiSynCtntyCnt1>uiCtntySynSet1)
                {
                        uiCtntySynSet1=uiCtntySynSet1-const_sub_dt; //uiCtntySynSet1 不断减小,鸣叫的节奏就越来越快
                        if(uiCtntySynSet1<const_syn_min_level)
                        {
                                uiCtntySynSet1=const_syn_min_level;                         //uiCtntySynSet1 不断减小,达到一个极限值
                        }
                        uiVoiceCnt=const_voice_short;                                                         //按键声音触发,滴一声就停。
                        uiSynCtntyCnt1=0;
                }
        }
        
        
        if(key_sr2==1)
        {
                ucKeyLock2=0;
                uiKeyTimeCnt2=0;
                uiKeyCtntyCnt2=0;
                uiSynCtntyCnt2=0;
                uiCtntyTimeSet2=const_initial_set;
                uiCtntySynSet2=const_initial_set;
        }
        
        else if(ucKeyLock2==0)
        {
                uiKeyTimeCnt2++;
                if(uiKeyTimeCnt2>const_key_time2)
                {
                        uiKeyTimeCnt2=0;
                        ucKeyLock2=1;
                        ucCtntyFlag2=0;
                        ucKeySec=2;
                }
        }
        
        else if(uiKeyTimeCnt2<const_time_1s)
        {
                uiKeyTimeCnt2++;
        }
        
        else
        {
                uiKeyCtntyCnt2++;
                if(uiKeyCtntyCnt2>uiCtntyTimeSet2)
                {
                        if(uiCtntyTimeSet2>const_min_level)
                        {
                                uiCtntyTimeSet2=uiCtntyTimeSet2-const_sub_dt;
                        }
                        else
                        {
                                uiCtntyTimeSet2=const_last_min_set;
                        }
                        uiKeyCtntyCnt2=0;
                        ucCtntyFlag2=1;
                        ucKeySec=2;
                }
                uiSynCtntyCnt2++;
                if(uiSynCtntyCnt2>uiCtntySynSet2)
                {
                        uiCtntySynSet2=uiCtntySynSet2-const_sub_dt;
                        if(uiCtntySynSet2<const_syn_min_level)
                        {
                                uiCtntySynSet2=const_syn_min_level;
                        }
                        uiVoiceCnt=const_voice_short;
                        uiSynCtntyCnt2=0;
                }
        }
}


void key_service()                                                                         //第三区 按键服务的应用程序
{
        switch(ucKeySec)                                                                         //按键服务状态切换
        {
                case 1:                                                                                                        // 1 号键 连续加键 对应STC8H8K64U开发箱9.62版 SW32键
                uiSetNumber++;                                                                         //被设置的参数连续往上加
                if(uiSetNumber>1000)                                                 //最大是 1000
                {
                        uiSetNumber=1000;
                }
                if(ucCtntyFlag1==0)                                                 //如果是在单击按键的情况下,则蜂鸣器鸣叫,否则
                                                                                                                                                //蜂鸣器在按键扫描key_scan 里鸣叫
                {
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                }
                ucKeySec=0;                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
               
                case 2:                                                                                                        // 2 号键 连续减键 对应STC8H8K64U开发箱9.62版 SW36键
                /* 注释二:
                * 在单片机的 C 语言编译器中,当无符号数据 0 减去 1 时,就会溢出,变成这个类型数据的最大值。
                * 比如是 unsigned int 的 0 减去 1 就等于 65535(0xffff),unsigned char 的 0 减去 1 就等于 255(0xff)
                */
                uiSetNumber--;                                                                         //被设置的参数连续往下减
                if(uiSetNumber>1000)                                                 //最小是 0.为什么这里用 1000?因为 0 减去 1 就是溢出变成了 65535(0xffff)
                {
                        uiSetNumber=0;
                }
                if(ucCtntyFlag2==0)                                                 //如果是在单击按键的情况下,则蜂鸣器鸣叫,否则
                                                                                                                                                //蜂鸣器在按键扫描key_scan 里鸣叫
                {
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                }
                ucKeySec=0;                                                                                  //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        }
}


void T0_time() interrupt 1
{
        TF0=0;                                                                         //清除中断标志
        TR0=0;                                                                         //关中断
        key_scan();                                                 //按键扫描函数
        
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--;                                 //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0;                                                 //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        
        else
        {
                ;                                                                                 //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1;                                                 //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        
        TH0=0xf8;                                                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1;                                                                         //开中断
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)         //内嵌循环的空指令数量
                {
                        ;                                                                  //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
/* 注释三:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把 key_gnd_dr 输出低电平。
* STC8H8K64U开发箱9.62版 SW32键SW36键 两个按键就是本程序中用到的两个独立按键。
*/
        P0M0 = 0X00;                //设置P0口为准双向口
        P0M1 = 0X00;

        P4M0 = 0X00;                //设置P4口为准双向口
        P4M1 = 0X00;

        P5M0 = 0X00;                //设置P5口为准双向口
        P5M1 = 0X00;

        P6M0 = 0X00;                //设置P6口为准双向口
        P6M1 = 0X00;
        
        key_gnd_dr=0;         //模拟独立按键的地 GND,因此必须一直输出低电平
        beep_dr=1;                         //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        led_dr=0;                         //LED 灯灭
        
        TMOD=0x01;                         //设置定时器 0 为工作方式 1
        TH0=0xf8;                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;         //开总中断
        ET0=1;         //允许定时中断
        TR0=1;         //启动定时中断
}


//总结陈词:
//到目前为止,前面一共花了 8 节内容仔细讲解了独立按键的扫描程序,如果是矩阵键盘,我们该怎么写程序?
//欲知详情,请听下回分解-----矩阵键盘的单个触发。

13 按住一个独立按键不松手的加速匀速触发.rar

55.2 KB, 下载次数: 103

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-6 09:20:59 | 显示全部楼层
第十四课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
上一节讲了按键的加速匀速触发。这节开始讲矩阵键盘的单个触发。
具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。
(2)实现功能:8个按键中,每按一个按键都能触发一次蜂鸣器发出“滴”的一声,
                同时控制对应的LED。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_voice_short         40 //蜂鸣器短叫的持续时间
#define const_key_time                         20 //按键去抖动延时的时间

void initial_myself();
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();                         //定时中断函数
void key_service();         //按键服务的应用程序
void key_scan();                         //按键扫描函数 放在定时中断里

sbit key_sr1=P0^6; //第一行输入
sbit key_sr2=P0^7; //第二行输入
sbit key_dr1=P0^0; //第一列输出
sbit key_dr2=P0^1; //第二列输出
sbit key_dr3=P0^2; //第三列输出
sbit key_dr4=P0^3; //第四列输出

sbit beep_dr=P5^4; //蜂鸣器的驱动 IO 口

sbit LEDonoff_sr1=P4^0; //LED控制开关
sbit LED1_dr=P6^0;                         //LED1灯
sbit LED2_dr=P6^1;                         //LED2灯
sbit LED3_dr=P6^2;                         //LED3灯
sbit LED4_dr=P6^3;                         //LED4灯
sbit LED5_dr=P6^4;                         //LED5灯
sbit LED6_dr=P6^5;                         //LED6灯
sbit LED7_dr=P6^6;                         //LED7灯
sbit LED8_dr=P6^7;                         //LED8灯

unsigned char ucKeyStep=1;                         //按键扫描步骤变量
unsigned char ucKeySec=0;                         //被触发的按键编号
unsigned int  uiKeyTimeCnt=0;          //按键去抖动延时计数器
unsigned char ucKeyLock=0;                         //按键触发后自锁的变量标志
unsigned int  uiVoiceCnt=0;                  //蜂鸣器鸣叫的持续时间计数器


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        
        while(1)
        {
                key_service(); //按键服务的应用程序
        }
}


void key_scan()//按键扫描函数 放在定时中断里
{
/* 注释一:
* 矩阵按键扫描的详细过程:
* 先输出某一列低电平,其它三列输出高电平,这个时候再分别判断输入的四行,
* 如果发现哪一行是低电平,就说明对应的某个按键被触发。依次分别输出另外三列
* 中的某一列为低电平,再分别判断输入的四行,就可以检测完8个按键。内部详细的
* 去抖动处理方法跟我前面讲的独立按键去抖动方法是一样的。
*/
        switch(ucKeyStep)
        {
                case 1: //按键扫描输出第一列低电平
                        key_dr1=0;
                        key_dr2=1;
                        key_dr3=1;
                        key_dr4=1;
                        uiKeyTimeCnt=0; //延时计数器清零
                        ucKeyStep++; //切换到下一个运行步骤
                        break;
               
                case 2: //此处的小延时用来等待刚才列输出信号稳定,再判断输入信号。不是去抖动延时。
                        uiKeyTimeCnt++;
                        if(uiKeyTimeCnt>1)
                        {
                                uiKeyTimeCnt=0;
                                ucKeyStep++; //切换到下一个运行步骤
                        }
                        break;
                        
                case 3:
                        if(key_sr1==1&&key_sr2==1)
                        {
                                ucKeyStep++; //如果没有按键按下,切换到下一个运行步骤
                                ucKeyLock=0; //按键自锁标志清零
                                uiKeyTimeCnt=0; //按键去抖动延时计数器清零,此行非常巧妙
                        }
                        
                        else if(ucKeyLock==0) //有按键按下,且是第一次触发
                        {
                                if(key_sr1==0&&key_sr2==1)
                                {
                                        uiKeyTimeCnt++; //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                ucKeySec=1; //触发 0 号键 对应STC8H8K64U开发箱9.62版 SW32键
                                        }
                                }
                                
                                else if(key_sr1==1&&key_sr2==0)
                                {
                                        uiKeyTimeCnt++; //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                ucKeySec=5; //触发 4 号键 对应STC8H8K64U开发箱9.62版 SW36键
                                        }
                                }        
                        }
                        break;
                        
                case 4: //按键扫描输出第二列低电平
                        key_dr1=1;
                        key_dr2=0;
                        key_dr3=1;
                        key_dr4=1;
                        uiKeyTimeCnt=0; //延时计数器清零
                        ucKeyStep++; //切换到下一个运行步骤
                        break;
               
                case 5: //此处的小延时用来等待刚才列输出信号稳定,再判断输入信号。不是去抖动延时。
                        uiKeyTimeCnt++;
                        if(uiKeyTimeCnt>1)
                        {
                                uiKeyTimeCnt=0;
                                ucKeyStep++; //切换到下一个运行步骤
                        }
                        break;
                        
                case 6:
                        if(key_sr1==1&&key_sr2==1)
                        {
                                ucKeyStep++; //如果没有按键按下,切换到下一个运行步骤
                                ucKeyLock=0; //按键自锁标志清零
                                uiKeyTimeCnt=0; //按键去抖动延时计数器清零,此行非常巧妙
                        }
                        
                        else if(ucKeyLock==0) //有按键按下,且是第一次触发
                        {
                                if(key_sr1==0&&key_sr2==1)
                                {
                                        uiKeyTimeCnt++; //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                ucKeySec=2; //触发 1 号键 对应STC8H8K64U开发箱9.62版 SW33键
                                        }
                                }
                                
                                else if(key_sr1==1&&key_sr2==0)
                                {
                                        uiKeyTimeCnt++; //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                ucKeySec=6; //触发 5 号键 对应STC8H8K64U开发箱9.62版 SW37键
                                        }
                                }
                                                
                        }
                        break;
                        
                case 7: //按键扫描输出第三列低电平
                        key_dr1=1;
                        key_dr2=1;
                        key_dr3=0;
                        key_dr4=1;
                        uiKeyTimeCnt=0; //延时计数器清零
                        ucKeyStep++; //切换到下一个运行步骤
                        break;
               
                case 8: //此处的小延时用来等待刚才列输出信号稳定,再判断输入信号。不是去抖动延时。
                        uiKeyTimeCnt++;
                        if(uiKeyTimeCnt>1)
                        {
                                uiKeyTimeCnt=0;
                                ucKeyStep++; //切换到下一个运行步骤
                        }
                        break;
                        
                case 9:
                        if(key_sr1==1&&key_sr2==1)
                        {
                                ucKeyStep++; //如果没有按键按下,切换到下一个运行步骤
                                ucKeyLock=0; //按键自锁标志清零
                                uiKeyTimeCnt=0; //按键去抖动延时计数器清零,此行非常巧妙
                        }
                        
                        else if(ucKeyLock==0) //有按键按下,且是第一次触发
                        {
                                if(key_sr1==0&&key_sr2==1)
                                {
                                        uiKeyTimeCnt++; //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                ucKeySec=3; //触发 2 号键 对应STC8H8K64U开发箱9.62版 SW34键
                                        }
                                }
                                
                                else if(key_sr1==1&&key_sr2==0)
                                {
                                        uiKeyTimeCnt++; //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                ucKeySec=7; //触发 6 号键 对应STC8H8K64U开发箱9.62版 SW38键
                                        }
                                }
                        
                        }
                        break;
                        
                case 10: //按键扫描输出第四列低电平
                        key_dr1=1;
                        key_dr2=1;
                        key_dr3=1;
                        key_dr4=0;
                        uiKeyTimeCnt=0; //延时计数器清零
                        ucKeyStep++; //切换到下一个运行步骤
                        break;
               
                case 11: //此处的小延时用来等待刚才列输出信号稳定,再判断输入信号。不是去抖动延时。
                        uiKeyTimeCnt++;
                        if(uiKeyTimeCnt>1)
                        {
                                uiKeyTimeCnt=0;
                                ucKeyStep++; //切换到下一个运行步骤
                        }
                        break;
                        
                case 12:
                        if(key_sr1==1&&key_sr2==1)
                        {
                                ucKeyStep=1; //如果没有按键按下,返回到第一步,重新开始扫描
                                ucKeyLock=0; //按键自锁标志清零
                                uiKeyTimeCnt=0; //按键去抖动延时计数器清零,此行非常巧妙
                        }
                        
                        else if(ucKeyLock==0) //有按键按下,且是第一次触发
                        {
                                if(key_sr1==0&&key_sr2==1)
                                {
                                        uiKeyTimeCnt++; //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                ucKeySec=4; //触发 3 号键 对应STC8H8K64U开发箱9.62版 SW35键
                                        }
                                }
                                
                                else if(key_sr1==1&&key_sr2==0)
                                {
                                        uiKeyTimeCnt++; //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;//自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                ucKeySec=8; //触发 7 号键 对应STC8H8K64U开发箱9.62版 SW39键
                                        }
                                }
                                                               
                        }
                        break;
                        
                default:
                        break;
                }

}

void key_service() //第三区 按键服务的应用程序
{
        switch(ucKeySec) //按键服务状态切换
        {
                case 1:// 0 号键 对应STC8H8K64U开发箱9.62版 SW32键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
//                        LED1_dr = 0;//点亮LED1
                        LED1_dr = ~LED1_dr;//LED1翻转
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 2:// 1 号键 对应STC8H8K64U开发箱9.62版 SW33键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
//                        LED2_dr = 0;//点亮LED2
                        LED2_dr = ~LED2_dr;//LED2翻转
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 3:// 2 号键 对应STC8H8K64U开发箱9.62版 SW34键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
//                        LED3_dr = 0;//点亮LED3
                        LED3_dr = ~LED3_dr;//LED3翻转
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 4:// 3 号键 对应STC8H8K64U开发箱9.62版 SW35键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
//                        LED4_dr = 0;//点亮LED4
                        LED4_dr = ~LED4_dr;//LED4翻转
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 5:// 4 号键 对应STC8H8K64U开发箱9.62版 SW36键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
//                        LED5_dr = 0;//点亮LED5        
                        LED5_dr = ~LED5_dr;//LED5翻转               
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 6:// 5 号键 对应STC8H8K64U开发箱9.62版 SW37键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
//                        LED6_dr = 0;//点亮LED6
                        LED6_dr = ~LED6_dr;//LED6翻转
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 7:// 6 号键 对应STC8H8K64U开发箱9.62版 SW38键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
//                        LED7_dr = 0;//点亮LED7
                        LED7_dr = ~LED7_dr;//LED7翻转
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                case 8:// 7 号键 对应STC8H8K64U开发箱9.62版 SW39键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
//                        LED8_dr = 0;//点亮LED8
                        LED8_dr = ~LED8_dr;//LED8翻转
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
               
                default:
                        break;
        }
}


void T0_time() interrupt 1
{
        TF0=0; //清除中断标志
        TR0=0; //关中断
        key_scan(); //按键扫描函数
        
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--; //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0; //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        else
        {
                ; //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1; //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1; //开中断
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++) //内嵌循环的空指令数量
                {
                        ; //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
        P0M0 = 0X00;                           //P0口设置为准双向口
        P0M1 = 0X00;
        
        P4M0 = 0X00;                           //P5口设置为准双向口
        P4M1 = 0X00;
               
        P5M0 = 0X00;                           //P5口设置为准双向口
        P5M1 = 0X00;
        
        P6M0 = 0X00;                           //P5口设置为准双向口
        P6M1 = 0X00;
               
        beep_dr=1;                                   //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        LEDonoff_sr1 = 0;         //打开LED控制开关
        
        TMOD=0x01;                                         //设置定时器 0 为工作方式 1
        TH0=0xf8;                                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;         //开总中断
        ET0=1;         //允许定时中断
        TR0=1;         //启动定时中断
}


//总结陈词:
//在这一节中,有的人咋看我的按键扫描代码,会觉得代码太多了。我一直认为,只要单片机容量够,代码多
//一点少一点并不重要,只要不影响运行效率就行。而且有时候,代码写多一点,可读性非常强,修改起来也非常
//方便。如果一味的追求压缩代码,就会刻意用很多循环,数组等元素,代码虽然紧凑了,但是可分离性,可改性,
//可读性就没那么强。我说那么多并不是因为我技术有限而不懂压缩,就找个借口敷衍大家,不信?我下一节把这
//节的代码压缩一下分享给大家。凡是相似度高的那部分代码都可以压缩,具体怎么压缩?欲知详情,请听下回分
//解-----矩阵键盘单个触发的压缩代码编程。

14 矩阵键盘的单个触发.rar

52.68 KB, 下载次数: 105

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-6 09:22:31 | 显示全部楼层
第十五课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
上一节讲了矩阵键盘的单个触发。这节要教会大家在不改变其它任何性能的情况下,
把上一节的按键扫描程序压缩一下容量。经过压缩后,把原来 1014 个字节压缩到
714 个字节的程序容量。
具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。
(2)实现功能:8个按键中,每按一个按键都能触发一次蜂鸣器发出“滴”的一声,
                        点亮对应的LED。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_voice_short 40 //蜂鸣器短叫的持续时间
#define const_key_time                 20 //按键去抖动延时的时间

void initial_myself();
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();                                                 //定时中断函数
void key_service();                                 //按键服务的应用程序
void key_scan();                                                 //按键扫描函数 放在定时中断里

sbit key_sr1=P0^6; //第一行输入
sbit key_sr2=P0^7; //第二行输入
sbit key_dr1=P0^0; //第一列输出
sbit key_dr2=P0^1; //第二列输出
sbit key_dr3=P0^2; //第三列输出
sbit key_dr4=P0^3; //第四列输出

sbit beep_dr=P5^4; //蜂鸣器的驱动 IO 口

sbit LEDonoff_dr=P4^0; //LED开关控制驱动 IO 口
sbit LED1_dr=P6^0; //LED1 驱动 IO 口
sbit LED2_dr=P6^1; //LED2 驱动 IO 口
sbit LED3_dr=P6^2; //LED3 驱动 IO 口
sbit LED4_dr=P6^3; //LED4 驱动 IO 口
sbit LED5_dr=P6^4; //LED5 驱动 IO 口
sbit LED6_dr=P6^5; //LED6 驱动 IO 口
sbit LED7_dr=P6^6; //LED7 驱动 IO 口
sbit LED8_dr=P6^7; //LED8 驱动 IO 口


unsigned char ucKeyStep=1;                                 //按键扫描步骤变量
unsigned char ucKeySec=0;                                 //被触发的按键编号
unsigned int         uiKeyTimeCnt=0;                 //按键去抖动延时计数器
unsigned char ucKeyLock=0;                                 //按键触发后自锁的变量标志
unsigned char ucRowRecord=1;                         //记录当前扫描到第几列了
unsigned int         uiVoiceCnt=0;                         //蜂鸣器鸣叫的持续时间计数器

void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        while(1)
        {
                key_service();                                                                         //按键服务的应用程序
        }
}


void key_scan()//按键扫描函数 放在定时中断里
{
/* 注释一:
* 矩阵按键扫描的详细过程:
* 先输出某一列低电平,其它三列输出高电平,这个时候再分别判断输入的四行,
* 如果发现哪一行是低电平,就说明对应的某个按键被触发。依次分别输出另外三列
* 中的某一列为低电平,再分别判断输入的四行,就可以检测完8个按键。内部详细的
* 去抖动处理方法跟我前面讲的独立按键去抖动方法是一样的。
*/
        switch(ucKeyStep)
        {
                case 1:                                                                                 //按键扫描输出第 ucRowRecord 列低电平
                        if(ucRowRecord==1)                                 //第一列输出低电平
                        {
                        key_dr1=0;
                        key_dr2=1;
                        key_dr3=1;
                        key_dr4=1;
                        }
                        else if(ucRowRecord==2)         //第二列输出低电平
                        {
                        key_dr1=1;
                        key_dr2=0;
                        key_dr3=1;
                        key_dr4=1;
                        }
                        else if(ucRowRecord==3)         //第三列输出低电平
                        {
                        key_dr1=1;
                        key_dr2=1;
                        key_dr3=0;
                        key_dr4=1;
                        }
                        else                                                                                         //第四列输出低电平
                        {
                        key_dr1=1;
                        key_dr2=1;
                        key_dr3=1;
                        key_dr4=0;
                        }
                        uiKeyTimeCnt=0;                                         //延时计数器清零
                        ucKeyStep++;                                                         //切换到下一个运行步骤
                        break;
                        
                case 2:                                                                                 //此处的小延时用来等待刚才列输出信号稳定,再判断输入信号。不是去抖动延时。
                        uiKeyTimeCnt++;
                        if(uiKeyTimeCnt>1)
                        {
                        uiKeyTimeCnt=0;
                        ucKeyStep++;                                                         //切换到下一个运行步骤
                        }
                        break;
                        
                case 3:
                        if(key_sr1==1&&key_sr2==1)
                        {
                                ucKeyStep=1;                                                                 //如果没有按键按下,返回到第一个运行步骤重新开始扫描
                                ucKeyLock=0;                                                                 //按键自锁标志清零
                                uiKeyTimeCnt=0;                                                 //按键去抖动延时计数器清零,此行非常巧妙
                                ucRowRecord++;                                                         //输出下一列
                                if(ucRowRecord>4)
                                {
                                        ucRowRecord=1;                                                 //依次输出完四列之后,继续从第一列开始输出低电平
                                }
                        }
                        else if(ucKeyLock==0)                                 //有按键按下,且是第一次触发
                        {
                                if(key_sr1==0&&key_sr2==1)
                                {
                                        uiKeyTimeCnt++;                                         //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;                                                //自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                if(ucRowRecord==1)                         //第一列输出低电平
                                                {
                                                        ucKeySec=1;                                         //触发 0 号键 对应STC8H8K64U开发箱9.62版 SW32键
                                                }
                                                else if(ucRowRecord==2) //第二列输出低电平
                                                {
                                                        ucKeySec=2;                                         //触发 1 号键 对应STC8H8K64U开发箱9.62版 SW33键
                                                }
                                                else if(ucRowRecord==3) //第三列输出低电平
                                                {
                                                        ucKeySec=3;                                         //触发 2 号键 对应STC8H8K64U开发箱9.62版 SW34键
                                                }
                                                else                                                                                 //第四列输出低电平
                                                {
                                                        ucKeySec=4;                                         //触发 3 号键 对应STC8H8K64U开发箱9.62版 SW35键
                                                }
                                        }
                                }
                                else if(key_sr1==1&&key_sr2==0)
                                {
                                        uiKeyTimeCnt++;                                         //去抖动延时计数器
                                        if(uiKeyTimeCnt>const_key_time)
                                        {
                                                uiKeyTimeCnt=0;
                                                ucKeyLock=1;                                                //自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                                                if(ucRowRecord==1)                         //第一列输出低电平
                                                {
                                                        ucKeySec=5;                                         //触发 4 号键 对应STC8H8K64U开发箱9.62版 SW36键
                                                }
                                                else if(ucRowRecord==2) //第二列输出低电平
                                                {
                                                        ucKeySec=6;                                         //触发 5 号键 对应STC8H8K64U开发箱9.62版 SW37键
                                                }
                                                else if(ucRowRecord==3) //第三列输出低电平
                                                {
                                                        ucKeySec=7;                                         //触发 6 号键 对应STC8H8K64U开发箱9.62版 SW38键
                                                }
                                                else                                                                                 //第四列输出低电平
                                                {
                                                        ucKeySec=8;                                         //触发 7号键 对应STC8H8K64U开发箱9.62版 SW39键
                                                }
                                        }
                                }
                                                
                        }
                        break;
                default:
                        break;
        }
}


void key_service()                                                                                                 //第三区 按键服务的应用程序
{
        switch(ucKeySec)                                                                                                 //按键服务状态切换
        {
        case 1:                                                                                                                                        // 0 号键 对应STC8H8K64U开发箱9.62版 SW32键
                uiVoiceCnt=const_voice_short;                                 //按键声音触发,滴一声就停。
                LED1_dr = ~LED1_dr;                                                                                //LED1翻转
                ucKeySec=0;                                                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        case 2:                                                                                                                                        // 1 号键 STC8H8K64U开发箱9.62版 SW33键
                uiVoiceCnt=const_voice_short;                                 //按键声音触发,滴一声就停。
                LED2_dr = ~LED2_dr;                                                                                //LED2翻转
                ucKeySec=0;                                                                                                          //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        case 3:                                                                                                                                        // 2 号键 对应STC8H8K64U开发箱9.62版 SW34键
                uiVoiceCnt=const_voice_short;                                 //按键声音触发,滴一声就停。
                LED3_dr = ~LED3_dr;                                                                                //LED3翻转
                ucKeySec=0;                                                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        case 4:                                                                                                                                        // 3 号键 对应STC8H8K64U开发箱9.62版 SW35键
                uiVoiceCnt=const_voice_short;                                 //按键声音触发,滴一声就停。
                LED4_dr = ~LED4_dr;                                                                                //LED4翻转
                ucKeySec=0;                                                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        case 5:                                                                                                                                        // 4 号键 对应STC8H8K64U开发箱9.62版 SW36键
                uiVoiceCnt=const_voice_short;                                 //按键声音触发,滴一声就停。
                LED5_dr = ~LED5_dr;                                                                                //LED5翻转
                ucKeySec=0;                                                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        case 6:                                                                                                                                        // 5 号键 对应STC8H8K64U开发箱9.62版 SW37键
                uiVoiceCnt=const_voice_short;                                  //按键声音触发,滴一声就停。
                LED6_dr = ~LED6_dr;                                                                                //LED6翻转
                ucKeySec=0;                                                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        case 7:                                                                                                                                        // 6 号键 对应STC8H8K64U开发箱9.62版 SW38键
                uiVoiceCnt=const_voice_short;                                 //按键声音触发,滴一声就停。
                LED7_dr = ~LED7_dr;                                                                                //LED7翻转
                ucKeySec=0;                                                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        case 8:                                                                                                                                        // 7 号键 对应STC8H8K64U开发箱9.62版 SW39键
                uiVoiceCnt=const_voice_short;                                 //按键声音触发,滴一声就停。
                LED8_dr = ~LED8_dr;                                                                                //LED8翻转
                ucKeySec=0;                                                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        default:
        break;
        
        }
}


void T0_time() interrupt 1
{
        TF0=0;                                                                                 //清除中断标志
        TR0=0;                                                                                 //关中断
        key_scan();                                                         //按键扫描函数
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--;                                         //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0;                                                         //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        else
        {
                ;                                                                                         //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1;                                                         //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        TH0=0xf8;                                                                 //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1;                                                                         //开中断
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)                         //内嵌循环的空指令数量
                {
                        ;                                                                                 //一个分号相当于执行一条空语句
                }
        }
}

void initial_myself()                         //第一区 初始化单片机
{
        P0M0 = 0X00;                                                        //P0口设置为准双向口
        P0M1 = 0X00;
        
        P4M0 = 0X00;                                                        //P4口设置为准双向口
        P4M1 = 0X00;
        
        P5M0 = 0X00;                                                        //P5口设置为准双向口
        P5M1 = 0X00;
        
        P6M0 = 0X00;                                                        //P6口设置为准双向口
        P6M1 = 0X00;
        
        LEDonoff_dr = 0;                                         //LED控制开关打开
        
        beep_dr=1;                                                                 //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        TMOD=0x01;                                                                 //设置定时器 0 为工作方式 1
        TH0=0xf8;                                                                 //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral()         //第二区 初始化外围
{
        EA=1;                                                                                 //开总中断
        ET0=1;                                                                                 //允许定时中断
        TR0=1;                                                                                 //启动定时中断
}


//总结陈词:
//已经花了两节讲矩阵键盘的单个触发程序。那么,矩阵键盘可不可以实现类似独立按键的组合按键功能?当
//然可以,但是也有一些附加限制条件。欲知详情,请听下回分解-----矩阵键盘的组合按键触发。

15 矩阵键盘单个触发的压缩代码编程.rar

51.13 KB, 下载次数: 101

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-9 08:40:47 | 显示全部楼层
第十六课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
上一节讲了矩阵键盘单个触发的压缩代码编程。这节讲矩阵键盘的组合按键触发。要教会大家三个知识点:
第一点:如何把矩阵键盘翻译成独立按盘的处理方式。然后按独立按键的方式来实现组合按键的功能。
第二点:要提醒大家在设计矩阵键盘时,很容易犯的一个错误。任意两个组合按键不能处于同一行,否则触发性
                                能大打折扣。在做产品的时候,硬件电路设计中,除了四路行输入的要加上拉电阻,四路列输出也应该串入一个
                                300 欧左右的限流电阻,否则当同一行的两个按键同时按下时,很容易烧坏单片机 IO 口。为什么?大家仔细想
                                想原因。因为如果没有限流电阻,同一行的两个按键同时按下时,在某一瞬间,输出的两路高低电平将会直接短
                                接在一起,引起短路。在STC8H8K64U开发箱9.62版,SW32、SW33、SW34、SW35 是同一行,SW36、SW37、SW38、SW39
                                是同一行。
第三点:在鸿哥矩阵键盘的组合按键处理程序中,组合按键的去抖动延时 const_key_time_comb 千万不能等于单
                                击按键的去抖动延时 const_key_time,否则组合按键会覆盖单击按键的触发。
具体内容,请看源代码讲解。
(1)硬件平台:基于STC8H8K64U开发箱9.62版。
(2)实现功能:8个按键中,每按一个按键都能触发一次蜂鸣器发出“滴”的一声。在同时按下 SW32 和 SW39 按键
时,将会点亮一个 LED 灯。在同时按下 SW34 和 SW35 按键时,将会熄灭一个 LED 灯。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_voice_short 40 //蜂鸣器短叫的持续时间
/* 注释一:
* 注意:组合按键的去抖动延时 const_key_time_comb 千万不能等于单击按键
* 的去抖动延时 const_key_time,否则组合按键会覆盖单击按键的触发。
*/
#define const_key_time                                 12         //按键去抖动延时的时间
#define const_key_time_comb         14         //组合按键去抖动延时的时间

void initial_myself();
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();                                                         //定时中断函数
void key_service();                                         //按键服务的应用程序
void key_scan();                                                         //按键扫描函数 放在定时中断里

/* 注释二:
* 注意:任意两个组合按键不能处于同一行,否则触发性能大打折扣。
* 在做产品的时候,硬件电路设计中,除了四路行输入的要加上拉电阻,
* 四路列输出也应该串入一个 300 欧左右的限流电阻,否则当同一行的两个
* 按键同时按下时,很容易烧坏单片机 IO 口。为什么?大家仔细想想原因。
* 因为如果没有限流电阻,同一行的两个按键同时按下时,在某一瞬间,
* 输出的两路高低电平将会直接短接在一起,引起短路。
* 在STC8H8K64U开发箱9.62版中,SW32、SW33、SW34、SW35 是同一行,SW36、SW37、SW38、SW39 是同一行
*/
sbit key_sr1=P0^6;                                 //第一行输入
sbit key_sr2=P0^7;                                 //第二行输入
sbit key_dr1=P0^0;                                //第一列输出
sbit key_dr2=P0^1;                                 //第二列输出
sbit key_dr3=P0^2;                                 //第三列输出
sbit key_dr4=P0^3;                                 //第四列输出

sbit ledonff_dr=P4^0;                 //LED灯控制输出
sbit led1_dr=P6^0;                                 //LED1灯输出
sbit led2_dr=P6^1;                                 //LED2灯输出
sbit led3_dr=P6^2;                                 //LED3灯输出
sbit led4_dr=P6^3;                                 //LED4灯输出
sbit led5_dr=P6^4;                                 //LED5灯输出
sbit led6_dr=P6^5;                                 //LED6灯输出
sbit led7_dr=P6^6;                                 //LED7灯输出
sbit led8_dr=P6^7;                                 //LED8灯输出

sbit beep_dr=P5^4;                                 //蜂鸣器的驱动 IO 口

unsigned char ucKeyStep=1;                                         //按键扫描步骤变量
unsigned char ucKeySec=0;                                          //被触发的按键编号
unsigned int         uiKeyTimeCnt[8]=0;         //8 个按键去抖动延时计数器
unsigned char ucKeyLock[8]=0;                         //8 个按键触发后自锁的变量标志
unsigned int         uiKeyTimeCnt_01_08=0; //SW32 和 SSW39 组合按键去抖动延时计数器
unsigned char ucKeyLock_01_08=0;                 //SW32 和 SSW39 组合按键触发后自锁的变量标志
unsigned int         uiKeyTimeCnt_04_05=0; //SW35 和 SSW36 组合按键去抖动延时计数器
unsigned char ucKeyLock_04_05=0;                 //SW35 和 SSW36 组合按键触发后自锁的变量标志
unsigned char ucRowRecord=1;                                 //记录当前扫描到第几列了
unsigned int         uiVoiceCnt=0;                                 //蜂鸣器鸣叫的持续时间计数器
unsigned int         uiKeyStatus=0xffff;         //此变量每一位代表一个按键的状态,共8个
                                                                                                                                                //按键。1 代表没有被按下,0 代表被按下。
void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        while(1)
        {
                key_service(); //按键服务的应用程序
        }
}


void key_scan()//按键扫描函数 放在定时中断里
{
/* 注释三:
* 第一步:先把8个按键翻译成独立按键。
* 第二步: 再按独立按键的去抖动方式进行按键识别。
* 第三步: 本程序把矩阵键盘翻译成独立按键的处理方式后,大家可以按独立按键的方式
* 来实现组合按键,双击,长按和短按,按住连续触发等功能。
* 我本人不再详细介绍这方面的内容。有兴趣的朋友,可以参考一下我前面章节讲的独立按键。
*/
        switch(ucKeyStep)
        {
                case 1: //把8个按键的状态快速记录在 uiKeyStatus 变量的每一位中,相当于把矩阵键盘翻译成独立按键。
                for(ucRowRecord=1;ucRowRecord<5;ucRowRecord++)
                {
                        if(ucRowRecord==1) //第一列输出低电平
                        {
                                key_dr1=0;
                                key_dr2=1;
                                key_dr3=1;
                                key_dr4=1;
                                //如果是薄膜按键或者走线比较长的按键,此处应该加几个空延时,
                                //等待列输出信号稳定再判断输入的状态
                                if(key_sr1==0)
                                {
                                        uiKeyStatus=uiKeyStatus&0xfffe; //对应STC8H8K64U开发箱9.62版 SW32键被按下
                                }
                                if(key_sr2==0)
                                {
                                uiKeyStatus=uiKeyStatus&0xffef; //对应STC8H8K64U开发箱9.62版 SW36键被按下
                                }
                                
                        }
                        else if(ucRowRecord==2) //第二列输出低电平
                        {
                                key_dr1=1;
                                key_dr2=0;
                                key_dr3=1;
                                key_dr4=1;
                                //如果是薄膜按键或者走线比较长的按键,此处应该加几个空延时,
                                        //等待列输出信号稳定再判断输入的状态
                                if(key_sr1==0)
                                {
                                        uiKeyStatus=uiKeyStatus&0xfffd; //对应STC8H8K64U开发箱9.62版 SW33键被按下
                                }
                                if(key_sr2==0)
                                {
                                        uiKeyStatus=uiKeyStatus&0xffdf; //对应STC8H8K64U开发箱9.62版 SW37键被按下
                                }
                                
                        }
                        else if(ucRowRecord==3) //第三列输出低电平
                        {
                                key_dr1=1;
                                key_dr2=1;
                                key_dr3=0;
                                key_dr4=1;
                                //如果是薄膜按键或者走线比较长的按键,此处应该加几个空延时,等待
                                        //列输出信号稳定再判断输入的状态
                                if(key_sr1==0)
                                {
                                        uiKeyStatus=uiKeyStatus&0xfffb; //对应STC8H8K64U开发箱9.62版 SW34键被按下
                                }
                                if(key_sr2==0)
                                {
                                        uiKeyStatus=uiKeyStatus&0xffbf; //对应STC8H8K64U开发箱9.62版 SW38键被按下
                                }
                                
                        }
                        else //第四列输出低电平
                        {
                                key_dr1=1;
                                key_dr2=1;
                                key_dr3=1;
                                key_dr4=0;
                        //如果是薄膜按键或者走线比较长的按键,此处应该加几个空延时,等待列
                        //        输出信号稳定再判断输入的状态
                        if(key_sr1==0)
                        {
                                uiKeyStatus=uiKeyStatus&0xfff7; //对应STC8H8K64U开发箱9.62版 SW35键被按下
                        }
                                if(key_sr2==0)
                        {
                        uiKeyStatus=uiKeyStatus&0xff7f; //对应STC8H8K64U开发箱9.62版 SW39键被按下
                        }
                        
                        }
                }
                ucKeyStep=2; //切换到下一个运行步骤
                break;
               
                case 2: //像独立按键一样进行去抖动和翻译。以下代码相似度很高,大家
                                                //有兴趣的话还可以加 for 循环来压缩代码
                if((uiKeyStatus&0x0001)==0x0001) //说明 0 号键没有被按下来
                {
                        uiKeyTimeCnt[0]=0;
                        ucKeyLock[0]=0;
                }
                else if(ucKeyLock[0]==0)
                {
                        uiKeyTimeCnt[0]++;
                        if(uiKeyTimeCnt[0]>const_key_time)
                        {
                                uiKeyTimeCnt[0]=0;
                                ucKeyLock[0]=1; //自锁按键,防止不断触发
                                ucKeySec=1; //被触发 0 号键
                        }
                }
                if((uiKeyStatus&0x0002)==0x0002) //说明 1 号键没有被按下来
                {
                        uiKeyTimeCnt[1]=0;
                        ucKeyLock[1]=0;
                }
                else if(ucKeyLock[1]==0)
                {
                        uiKeyTimeCnt[1]++;
                        if(uiKeyTimeCnt[1]>const_key_time)
                        {
                                uiKeyTimeCnt[1]=0;
                                ucKeyLock[1]=1; //自锁按键,防止不断触发
                                ucKeySec=2; //被触发 1 号键
                        }
                }
                if((uiKeyStatus&0x0004)==0x0004) //说明 2 号键没有被按下来
                {
                        uiKeyTimeCnt[2]=0;
                        ucKeyLock[2]=0;
                }
                else if(ucKeyLock[2]==0)
                {
                        uiKeyTimeCnt[2]++;
                        if(uiKeyTimeCnt[2]>const_key_time)
                        {
                                uiKeyTimeCnt[2]=0;
                                ucKeyLock[2]=1; //自锁按键,防止不断触发
                                ucKeySec=3; //被触发 2 号键
                        }
                }
                if((uiKeyStatus&0x0008)==0x0008) //说明 3 号键没有被按下来
                {
                        uiKeyTimeCnt[3]=0;
                        ucKeyLock[3]=0;
                }
                else if(ucKeyLock[3]==0)
                {
                        uiKeyTimeCnt[3]++;
                        if(uiKeyTimeCnt[3]>const_key_time)
                        {
                                uiKeyTimeCnt[3]=0;
                                ucKeyLock[3]=1; //自锁按键,防止不断触发
                                ucKeySec=4; //被触发 3 号键
                        }
                }
                if((uiKeyStatus&0x0010)==0x0010) //说明 4 号键没有被按下来
                {
                        uiKeyTimeCnt[4]=0;
                        ucKeyLock[4]=0;
                }
                else if(ucKeyLock[4]==0)
                {
                        uiKeyTimeCnt[4]++;
                        if(uiKeyTimeCnt[4]>const_key_time)
                        {
                                uiKeyTimeCnt[4]=0;
                                ucKeyLock[4]=1; //自锁按键,防止不断触发
                                ucKeySec=5; //被触发 4 号键
                        }
                }
                if((uiKeyStatus&0x0020)==0x0020) //说明 5 号键没有被按下来
                {
                        uiKeyTimeCnt[5]=0;
                        ucKeyLock[5]=0;
                }
                else if(ucKeyLock[5]==0)
                {
                        uiKeyTimeCnt[5]++;
                        if(uiKeyTimeCnt[5]>const_key_time)
                        {
                                uiKeyTimeCnt[5]=0;
                                ucKeyLock[5]=1; //自锁按键,防止不断触发
                                ucKeySec=6; //被触发 5 号键
                        }
                }
                if((uiKeyStatus&0x0040)==0x0040) //说明 6 号键没有被按下来
                {
                        uiKeyTimeCnt[6]=0;
                        ucKeyLock[6]=0;
                }
                else if(ucKeyLock[6]==0)
                {
                        uiKeyTimeCnt[6]++;
                        if(uiKeyTimeCnt[6]>const_key_time)
                        {
                                uiKeyTimeCnt[6]=0;
                                ucKeyLock[6]=1; //自锁按键,防止不断触发
                                ucKeySec=7; //被触发 6 号键
                        }
                }
                if((uiKeyStatus&0x0080)==0x0080) //说明 7 号键没有被按下来
                {
                        uiKeyTimeCnt[7]=0;
                        ucKeyLock[7]=0;
                }
                else if(ucKeyLock[7]==0)
                {
                        uiKeyTimeCnt[7]++;
                        if(uiKeyTimeCnt[7]>const_key_time)
                        {
                                uiKeyTimeCnt[7]=0;
                                ucKeyLock[7]=1; //自锁按键,防止不断触发
                                ucKeySec=8; //被触发 7 号键
                        }
                }
               
                if((uiKeyStatus&0x0081)==0x0000) //SW32 和 SW39 的组合键盘被按下。
                {
                        if(ucKeyLock_01_08==0)
                        {
                                uiKeyTimeCnt_01_08++;
                                if(uiKeyTimeCnt_01_08>const_key_time_comb)
                                {
                                        uiKeyTimeCnt_01_08=0;
                                        ucKeyLock_01_08=1;
                                        ucKeySec=9;                         //被触发 8 号组合键
                                }
                        }
                }
                else
                {
                        uiKeyTimeCnt_01_08=0; //SW32 和 SW39 组合按键去抖动延时计数器
                        ucKeyLock_01_08=0;                 //SW32 和 SW39  组合按键触发后自锁的变量标志
                }
                if((uiKeyStatus&0x0018)==0x0000) //SW32 和 SW39  的组合键盘被按下。
                {
                        if(ucKeyLock_04_05==0)
                        {
                                uiKeyTimeCnt_04_05++;
                                if(uiKeyTimeCnt_04_05>const_key_time_comb)
                                {
                                        uiKeyTimeCnt_04_05=0;
                                        ucKeyLock_04_05=1;
                                        ucKeySec=10;                                 //被触发 9 号组合键
                                }
                        }
                }
                else
                {
                        uiKeyTimeCnt_04_05=0;         //SW35 和 SW36  组合按键去抖动延时计数器
                        ucKeyLock_04_05=0;                         //SW35 和 SW36 组合按键触发后自锁的变量标志
                }
                uiKeyStatus=0xff;                         //及时恢复状态,方便下一次扫描
                ucKeyStep=1;                                                         //返回到第一个运行步骤重新开始扫描
                break;
               
                default:
                        break;
        }
}



void key_service()                                                                                                 //第三区 按键服务的应用程序
{
        switch(ucKeySec)                                                                                                 //按键服务状态切换
        {
                case 1:                                                                                                                                // 0 号键 对应STC8H8K64U开发箱9.62版 SW32键
                        uiVoiceCnt=const_voice_short;                         //按键声音触发,滴一声就停。
                        led1_dr = 0;                                                                                                //点亮LED1
                        ucKeySec=0;                                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                case 2:                                                                                                                                // 1 号键 对应STC8H8K64U开发箱9.62版 SW33键
                        uiVoiceCnt=const_voice_short;                                 //按键声音触发,滴一声就停。
                        led2_dr = 0;                                                                                                //点亮LED2
                        ucKeySec=0;                                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                case 3:                                                                                                                                // 2 号键 对应STC8H8K64U开发箱9.62版 SW34键
                        uiVoiceCnt=const_voice_short;                         //按键声音触发,滴一声就停。
                        led3_dr = 0;                                                                                                //点亮LED3                        
                        ucKeySec=0;                                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                case 4:                                                                                                                                // 3 号键 对应STC8H8K64U开发箱9.62版 SW35键
                        uiVoiceCnt=const_voice_short;                         //按键声音触发,滴一声就停。
                        led4_dr = 0;                                                                                                //点亮LED4        
                        ucKeySec=0;                                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                case 5:                                                                                                                                // 4 号键 对应STC8H8K64U开发箱9.62版 SW36键
                        uiVoiceCnt=const_voice_short;                         //按键声音触发,滴一声就停。
                        led5_dr = 0;                                                                                                //点亮LED5        
                        ucKeySec=0;                                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                case 6:                                                                                                                                // 5 号键 对应STC8H8K64U开发箱9.62版 SW37键
                        uiVoiceCnt=const_voice_short;                          //按键声音触发,滴一声就停。
                        led6_dr = 0;                                                                                                //点亮LED6
                        ucKeySec=0;                                                                                                  //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                case 7:                                                                                                                                // 6 号键 对应STC8H8K64U开发箱9.62版 SW38键
                        uiVoiceCnt=const_voice_short;                         //按键声音触发,滴一声就停。
                        led7_dr = 0;                                                                                                //点亮LED7
                        ucKeySec=0;                                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                case 8:                                                                                                                                // 7 号键 对应STC8H8K64U开发箱9.62版 SW39键
                        uiVoiceCnt=const_voice_short;                         //按键声音触发,滴一声就停。        
                        led8_dr = 0;                                                                                                //点亮LED8
                        ucKeySec=0;                                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
        
                case 9:                                                                                                                                // 8 号组合键 对应STC8H8K64U开发箱9.62版 SW32 和 SW39 的组合按键
                        uiVoiceCnt=const_voice_short;                         //按键声音触发,滴一声就停。
                        P6 = 0X0F;                                                                                                        //点亮低4位LED
                        ucKeySec=0;                                                                                                 //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                case 10:                                                                                                                        // 10 号组合键 对应STC8H8K64U开发箱9.62版 SW35 和 SW36 的组合按键
                        uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                        P6 = 0XFF;                                                                                                        //熄灭全部LED
                        ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                        break;
                default:
                        break;
        }
}


void T0_time() interrupt 1
{
        TF0=0;                                                         //清除中断标志
        TR0=0;                                                         //关中断
        key_scan();                                 //按键扫描函数
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--;                 //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0;                                 //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        else
        {
                ;                                                                 //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1;                                 //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        TH0=0xf8;                                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1;                                                         //开中断
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)                 //内嵌循环的空指令数量
                {
                        ;                                                                                 //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
        P0M0 = 0X00;                        //P0口设置为准双向口
        P0M1 = 0X00;

        P4M0 = 0X00;                        //P4口设置为准双向口
        P4M1 = 0X00;
        
        P5M0 = 0X00;                        //P5口设置为准双向口
        P5M1 = 0X00;
        
        P6M0 = 0X00;                        //P6口设置为准双向口
        P6M1 = 0X00;
        
        ledonff_dr = 0;         //LED 灯控制开关打开
        
        beep_dr=1;                                 //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        TMOD=0x01;                                 //设置定时器 0 为工作方式 1
        TH0=0xf8;                                 //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;                 //开总中断
        ET0=1;                 //允许定时中断
        TR0=1;                 //启动定时中断
}

//总结陈词:
//这节讲了如何把矩阵键盘翻译成独立按键的处理方式,然后像独立按键一样实现组合按键的功能,关于矩阵
//按键的双击,长按和短按,按键连续触发等功能我不再详细介绍,有兴趣的朋友可以参考我前面章节讲的独立按
//键。在实际的项目中,按键可以控制很多外设。为了以后进一步讲按键控制外设等功能,接下来我会讲哪些新内
//容呢?欲知详情,请听下回分解-----两片联级 74HC595 驱动 16 个 LED 灯的基本驱动程序。

16 矩阵键盘的组合按键触发.rar

56.52 KB, 下载次数: 113

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-10 08:06:45 | 显示全部楼层
本帖最后由 lclbf 于 2024-1-10 08:23 编辑

第十七课:

本集用到的电路开发箱上没有。先上原理图如下:


1111111.png

控制用实验箱的P60,P61,P62口。J5的DB0,DB1,DB2。SH(SCLK)接P60口,ST(RCLK)接P61口,DS(DSI)接P62口。

后面用到的74HC595驱动LED都是用的这个电路和同样的接法,不再说明。
/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!


开场白:
上一节讲了如何把矩阵键盘翻译成独立按键的处理方式。这节讲 74HC595 的驱动程序。要教会大家两个知
识点:
第一点:STC8H8K64U开发箱9.62版。用 74HC595 控制 LED,因此可以直接把 595 的 OE 引脚接地。如果在工控
                        中,用来控制继电器,那么此芯片的片选脚 OE 不要为了省一个 IO 口而直接接地,否则会引起上电瞬间
                        继电器莫名其妙地动作。为了解决这个问题,OE 脚应该用一个 IO 口单独驱动,并且千万要记住,此 IO
                        必须接一个 15K 左右的上拉电阻,然后在程序刚上电运行时,先把 OE 置高,并且尽快把所有的 74HC595
                        输出口置低,然后再把 OE 置低.当然还有另外一种解决办法,就是用一个 10uF 的电解电容跟一个 100K的
                        下拉电阻,组成跟 51 单片机外围复位电路原理一样的电路,连接到 OE 口,这样确保上电瞬间 OE 口有一
                        小段时间是处于高电平状态,在此期间,尽快通过软件把 74hc595 的所有输出口置低。
第二点:两个联级 74HC595 的工作过程:每个 74HC595 内部都有一个 8 位的寄存器,两个联级起来就有两个寄
      存器。ST 引脚就相当于一个刷新信号引脚,当 ST 引脚产生一个上升沿信号时,就会把寄存器的数值输出
                        到74HC595 的输出引脚并且锁存起来,DS 是数据引脚,SH 是把新数据送入寄存器的时钟信号。也就是说,
                        SH 引脚负责把数据送入到寄存器里,ST 引脚负责把寄存器的数据更新输出到 74HC595 的输出引脚上并且
                        锁存起来。具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。
(2)实现功能:两片联级的 74HC595 驱动的 16 个 LED 灯交叉闪烁。比如,先是第 1,3,5,7,9,11,13,15 八个
                        灯亮,其它的灯都灭。然后再反过来,原来亮的就灭,原来灭的就亮。交替闪烁。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_time_level 200

void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker();
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void T0_time();     //定时中断函数

/* 注释一:
* 基于STC8H8K64U开发箱9.62版。用 74HC595 控制 LED,因此可以直接把 595 的 OE 引脚接地。如果在工
* 控中,用来控制继电器,
* 那么此芯片的片选脚 OE 不要为了省一个 IO 口而直接接地,否则会引起上电瞬间继电器莫名其妙地动作。
* 为了解决这个问题,OE 脚应该用一个 IO 口单独驱动,并且千万要记住,此 IO 必须接一个 15K 左右的
* 上拉电阻,然后在程序刚上电运行时,先把 OE 置高,并且尽快把所有的 74HC595 输出口置低,然后再把 OE
        置低.
* 当然还有另外一种解决办法,就是用一个 10uF 的电解电容跟一个 100K 的下拉电阻,组成跟 51 单片机外围复
        位电路原理
* 一样的电路,连接到 OE 口,这样确保上电瞬间 OE 口有一小段时间是处于高电平状态,在此 期间,
* 尽快通过软件把 74hc595 的所有输出口置低。
*
*/
sbit hc595_sh_dr=P6^0;            //J5口DB0
sbit hc595_st_dr=P6^1;                 //J5口DB1
sbit hc595_ds_dr=P6^2;                 //J5口DB2

unsigned char ucLedStep=0; //步骤变量
unsigned int  uiTimeCnt=0; //统计定时中断次数的延时计数器

void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        
        while(1)
        {
                led_flicker();
        }
}


/* 注释二:
* 两个联级 74HC595 的工作过程:
* 每个 74HC595 内部都有一个 8 位的寄存器,两个联级起来就有两个寄存器。ST 引脚就相当于一个刷新
* 信号引脚,当 ST 引脚产生一个上升沿信号时,就会把寄存器的数值输出到 74HC595 的输出引脚并且锁存起来,
* DS 是数据引脚,SH 是把新数据送入寄存器的时钟信号。也就是说,SH 引脚负责把数据送入到寄存器里,ST
引脚
* 负责把寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来。
*/

void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
        unsigned char i;
        unsigned char ucTempData;
        
        hc595_sh_dr=0;        //初始化74HC595
        hc595_st_dr=0;        //初始化74HC595
        hc595_ds_dr=0;        //初始化74HC595
        
        ucTempData=ucLedStatusTemp16_09; //先送高 8 位 写入高8位数据
        
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
               
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        ucTempData=ucLedStatusTemp08_01; //再先送低 8 位 写入低8位数据
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
               
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        hc595_st_dr=0; //ST 引脚把两个寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来
        delay_short(15);
        hc595_st_dr=1;
        delay_short(15);
        hc595_sh_dr=0; //拉低,抗干扰就增强
        hc595_st_dr=0;
        hc595_ds_dr=0;
}


void led_flicker() ////第三区 LED 闪烁应用程序
{
        switch(ucLedStep)
        {
                case 0:
                if(uiTimeCnt>=const_time_level) //时间到
                {
                        uiTimeCnt=0; //时间计数器清零
                        hc595_drive(0x55,0x55);
                        ucLedStep=1; //切换到下一个步骤
                }
                break;
               
                case 1:
                if(uiTimeCnt>=const_time_level) //时间到
                {
                        uiTimeCnt=0; //时间计数器清零
                        hc595_drive(0xaa,0xaa);
                        ucLedStep=0; //返回到上一个步骤
                }
                break;
        }
}


void T0_time() interrupt 1
{
        TF0=0; //清除中断标志
        TR0=0; //关中断
        if(uiTimeCnt<0xffff) //设定这个条件,防止 uiTimeCnt 超范围。
        {
                uiTimeCnt++; //累加定时中断的次数,
        }
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1; //开中断
}


void delay_short(unsigned int uiDelayShort)
{
        unsigned int i;
        for(i=0;i<uiDelayShort;i++)
        {
                ; //一个分号相当于执行一条空语句
        }
}

void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++) //内嵌循环的空指令数量
                {
                        ; //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
        P6M0 = 0X00;
        P6M1 = 0X00;
        
        TMOD=0x01; //设置定时器 0 为工作方式 1
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}

void initial_peripheral() //第二区 初始化外围
{
        EA=1; //开总中断
        ET0=1; //允许定时中断
        TR0=1; //启动定时中断
}

//总结陈词:
// 这节讲了 74HC595 的驱动程序,它是一次控制 16 个 LED 同时亮灭的,在实际中应用不太方便,如果我们
//想要像单片机 IO 口直接控制 LED 那样方便,我们该怎么编写程序呢?欲知详情,请听下回分解-----把 74HC595
//驱动程序翻译成类似单片机 IO 口直接驱动的方式。

17 两片联级 74HC595 驱动 16 个 LED 灯的基本驱动程序.rar

48.62 KB, 下载次数: 112

回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-9 03:40 , Processed in 0.133374 second(s), 104 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表