数码之家

 找回密码
 立即注册
搜索
查看: 2272|回复: 13

[C51] 小白自学C51单片机编程,4位数码管时钟分钟,增加按键

[复制链接]
发表于 2023-12-22 09:57:40 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册

x
谢谢大家的指导,在昨天的基础上增加了按键,但实践中发现按一下KEY4 本意是要时钟加1,实际加了好几个1

以下为代码
  1. /*程序说明:4位数码管显示小时分钟,4个LED灯亮一秒停一秒,按KEY1加5分,按KEY4加1小时*/

  2. #include <reg52.h>
  3. typedef unsigned char uchar;    //类型定义
  4. typedef unsigned int uint;

  5. sbit led_a=P1^0;    //数码管个位
  6. sbit led_b=P1^1;    //十位
  7. sbit led_c=P1^2;    //百位
  8. sbit led_d=P1^3;    //千位        
  9. sbit led5=P1^4;        //4个LED灯
  10. sbit led6=P1^5;
  11. sbit led7=P1^6;
  12. sbit led8=P1^7;
  13. sbit key4=P3^7;        //用于调时

  14. uchar code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x7f};

  15. uint time=0;  // 秒钟
  16. uint fen=0;      // 分钟
  17. uint shi=0;   // 时钟
  18. uint xms;       //延时ms
  19. uchar dingshi=0; //定时次数

  20. void display(time);          //声明显示子函数
  21. void delay(xms);          //声明延时子函数
  22. void key_scan();          //声明按键子函数

  23. void main ()
  24. {
  25.      EA=1;        //开总中断
  26.     ET0=1;        //开定时器0        
  27.     TR0=1;        //定时器0开始
  28.     TMOD=0X01;    // 定时器模式1
  29.     TH0=(65536-50000)/256;      //50ms初值高8位
  30.     TL0=(65536-50000)%256;      //50ms初值低8位
  31.     EX1=1;        //开外部中断1
  32.     IT1=0;        //低电平有效
  33.     while(1)
  34.     {
  35.         key_scan();    //检测是否有按键
  36.         display(time);     //显示
  37.     }
  38. }

  39. void display(time)
  40. {
  41.       P0=table[fen%10];      //送出段选信号
  42.      led_a=0;                 //打开个位位选
  43.      delay(5);
  44.      led_a=1;

  45.      P0=table[fen/10%10];      //送出段选信号
  46.      led_b=0;                   //打开十位位选
  47.      delay(5);
  48.      led_b=1;

  49.       P0=table[shi%10];      
  50.      led_c=0;
  51.      delay(5);
  52.      led_c=1;

  53.      P0=table[shi/10%10];     
  54.      led_d=0;
  55.      delay(5);
  56.      led_d=1;
  57.      
  58.      P0=table[10];        //显示中间的点
  59.      led_c=0;
  60.      delay(5);
  61.      led_c=1;
  62. }
  63. void delay(xms)        //延时函数
  64. {
  65.      uchar i;
  66.     while(xms--)
  67.     {
  68.          for(i=0;i<113;i++);
  69.     }
  70. }

  71. void key_scan()
  72. {
  73.      if(key4==0)
  74.     {
  75.          delay(5);
  76.         if(key4==0)
  77.         {
  78.              shi+=1;      //实际情况是按一下key4时钟加了好几个1
  79.             if(shi>=24)
  80.             {
  81.              shi=shi-24;
  82.             }
  83.         }
  84.     }
  85. }

  86. void jishu ()interrupt 1    //定时器中断函数
  87. {   
  88.     TH0=(65536-45872)/256;
  89.     TL0=(65536-45872)%256;
  90.    
  91.      if(dingshi==20)      //如果定时满了20次
  92.     {
  93.          dingshi=0;     //次数清零,循环
  94.         time++;        //秒加一次
  95.         if(time==60)
  96.         {
  97.              time=0;
  98.             fen++;      //到了60秒,分加一次
  99.             if(fen==60)
  100.             {
  101.                  fen=0;
  102.                 shi++;     ////到了60分,时加一次
  103.                 if(shi==24)
  104.                 {
  105.                      shi=0;
  106.                 }

  107.             }
  108.         }      
  109.         led5=~led5;      //亮一秒灭一秒
  110.         led6=~led6;
  111.         led7=~led7;
  112.         led8=~led8;
  113.     }            
  114.     dingshi++;         //如果定时没满20次,就加一次
  115. }

  116. void key_int()interrupt 2  //外部中断1
  117. {
  118.     delay(300);     //如果不延时,按一下按钮可能加了好几个5
  119.     fen+=5;    //加5分,可以理解为fen=fen+5
  120.     if(fen>=60)      
  121.     {
  122.     fen=fen-60;
  123.     }
  124. }
复制代码




打赏

参与人数 1家元 +50 收起 理由
家睦 + 50

查看全部打赏

 楼主| 发表于 2023-12-22 09:58:59 | 显示全部楼层
用手机拍的视频很闪,实际人眼看不闪



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

发表于 2023-12-22 10:45:05 | 显示全部楼层
key_scan() 函数  要加按键去抖动功能,,


还有按键速度和晶振有很大关系,
晶振频率选小了,像按不动一样,选大了,又像双击连击了
回复 支持 反对

使用道具 举报

发表于 2023-12-22 10:51:19 | 显示全部楼层
可以,棒棒的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-12-22 10:56:25 | 显示全部楼层
谢谢大家,这个问题要怎样解决:但实践中发现按一下KEY4 本意是要时钟加1,实际加了好几个1
回复 支持 反对

使用道具 举报

发表于 2023-12-22 10:58:49 | 显示全部楼层
厉害了,佩服,一直想学,迈不动腿
回复 支持 反对

使用道具 举报

发表于 2023-12-22 11:13:18 | 显示全部楼层
网上找的,仅供参考,,好不好用要自己试了













#define uchar unsigned char
#define uint unsigned int
#define const_key_time  2    //按键去抖动延时的时间
#define const_time_0_25s  10   //0.25秒钟的时间需要的定时中断次数
#define const_time_1s     70 //1秒钟的时间需要的定时中断次数

sbit KEYS = P3 ^ 3;        //调整键,按一次设置状态位加1,轮流切换到设置或转换显示状态等.
sbit KEYA = P3 ^ 4;        //加一键,在设置状态时按一次相应设置位加1
sbit KEYD = P3 ^ 5;        //减一键,在设置状态时按一次相应设置位减1


extern uchar keyscan();     //键盘读取函数,返回键值1,2,3



static unsigned int  uiKeyTimeCnt1 = 0; //按键1去抖动延时计数器
static unsigned int  uiKeyCtntyCnt1 = 0; //按键1连续触发的间隔延时计数器
static unsigned char ucKeyLock1 = 0; //按键1触发后自锁的变量标志
static unsigned char ucShortTouchFlag1 = 0; //按键1短按的触发标志


static unsigned int  uiKeyTimeCnt2 = 0; //按键2去抖动延时计数器
static unsigned int  uiKeyCtntyCnt2 = 0; //按键2连续触发的间隔延时计数器
static unsigned char ucKeyLock2 = 0; //按键2触发后自锁的变量标志

static unsigned int  uiKeyTimeCnt3 = 0; //按键3去抖动延时计数器
static unsigned int  uiKeyCtntyCnt3 = 0; //按键3连续触发的间隔延时计数器
static unsigned char ucKeyLock3 = 0; //按键3触发后自锁的变量标志




//键扫函数
uchar keyscan()

{
    /* 以下按键程序来源于吴坚鸿(鸿哥),有延时去抖,按住连发功能,长按短按识别,值得借用。
    * 独立按键扫描的详细过程:
    * 第一步:平时没有按键被触发时,按键的自锁标志,去抖动延时计数器,以及时间间隔延时计数器一直被清零。
    * 第二步:一旦有按键被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到
    *         阀值const_key_time1时,如果在这期间由于受外界干扰或者按键抖动,而使
    *         IO口突然瞬间触发成高电平,这个时候马上把延时计数器uiKeyTimeCnt1
    *         清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。这是我实战中摸索出来的。
    *         以后凡是用到开关感应器的时候,都可以用类似这样的方法去干扰。
    * 第三步:如果按键按下的时间超过了阀值const_key_time1,则触发按键,把编号赋值。
    *         同时,马上把自锁标志ucKeyLock1置位,防止按住按键不松手后一直触发。
    * 第四步:如果此时触发了一次按键后,一直不松手,去抖动延时计时器继续累加,直到超过了1秒钟。进入连续触发模式的程序
    * 第五步:在连续触发模式的程序中,连续累加延时计数器开始累加,每0.25秒就触发一次。
    * 第六步:等按键松开后,自锁标志ucKeyLock1和两个延时计时器及时清零,为下一次自锁做准备。
    */

    //-----------------KEYS带短按长按识别,短按键码=1,长按,键码=4--------------------

    if(KEYS == 1) //IO是高电平,说明两个按键没有全部被按下,这时要及时清零一些标志位
    {
        ucKeyLock1 = 0; //按键自锁标志清零
        uiKeyTimeCnt1 = 0; //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。

        if(ucShortTouchFlag1 == 1) //短按触发标志
        {
            ucShortTouchFlag1 = 0;
            return 1 ;
        }
    }
    else if(ucKeyLock1 == 0) //有按键按下,且是第一次被按下
    {
        uiKeyTimeCnt1++; //累加定时中断次数

        if(uiKeyTimeCnt1 > const_key_time)
        {
            ucShortTouchFlag1 = 1; //激活按键短按的有效标志
        }

        if(uiKeyTimeCnt1 > const_time_1s)
        {
            ucShortTouchFlag1 = 0; //清除按键短按的有效标志

            uiKeyTimeCnt1 = 0;
            ucKeyLock1 = 1; //自锁按键置位,避免一直触发

            return 4 ;
        }

    }


//    //-----------------KEYS键不带连发,键码=1,一般是模式或设置键---------------------
//    if (KEYS)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
//    {
//        ucKeyLock1=0; //按键自锁标志清零
//        uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
//        uiKeyCtntyCnt1=0; //连续累加的时间间隔延时计数器清零
//    }
//    else if (ucKeyLock1==0)//有按键按下,且是第一次被按下
//    {
//        uiKeyTimeCnt1++; //累加定时中断次数
//        if (uiKeyTimeCnt1>const_key_time)
//        {
//            uiKeyTimeCnt1=0;
//            ucKeyLock1=1;  //自锁按键置位,避免一直触发
//                  //触发1号键
//            return 1 ;
//        }
//    }
//-----------------------上键,有连发,键码=2-----------------------
    if (KEYA)
    {
        ucKeyLock2 = 0;
        uiKeyTimeCnt2 = 0;
        uiKeyCtntyCnt2 = 0;
    }
    else if (ucKeyLock2 == 0)
    {
        uiKeyTimeCnt2++; //累加定时中断次数

        if (uiKeyTimeCnt2 > const_key_time)
        {
            uiKeyTimeCnt2 = 0;
            ucKeyLock2 = 1;
//触发2号键
            return 2 ;
        }
    }
    else if (uiKeyTimeCnt2 < const_time_1s)
    {
        uiKeyTimeCnt2++;
    }
    else
    {
        uiKeyCtntyCnt2++;

        if (uiKeyCtntyCnt2 > const_time_0_25s)
        {
            uiKeyCtntyCnt2 = 0;
//触发2号键
            return 2;

        }
    }

//-----------------下键,有连发,键码=3-------------------
    if (KEYD)
    {
        ucKeyLock3 = 0;
        uiKeyTimeCnt3 = 0;
        uiKeyCtntyCnt3 = 0;
    }
    else if (ucKeyLock3 == 0)
    {
        uiKeyTimeCnt3++; //累加定时中断次数

        if (uiKeyTimeCnt3 > const_key_time)
        {
            uiKeyTimeCnt3 = 0;
            ucKeyLock3 = 1;
//触发3号键
            return 3 ;

        }
    }
    else if (uiKeyTimeCnt3 < const_time_1s)
    {
        uiKeyTimeCnt3++;
    }
    else
    {
        uiKeyCtntyCnt3++;

        if (uiKeyCtntyCnt3 > const_time_0_25s)
        {
            uiKeyCtntyCnt3 = 0;
//触发3号键
            return  3 ;
        }
    }


    return 0;
}


回复 支持 反对

使用道具 举报

发表于 2023-12-22 11:21:59 | 显示全部楼层
增加一个标志位,按键未按下时置false;按键按下后判断标志位为false则“时钟加1,标志位置true”;为true则表示已经加1过了,直接返回;
回复 支持 反对

使用道具 举报

发表于 2023-12-22 11:22:44 来自手机浏览器 | 显示全部楼层
hellozwt 发表于 2023-12-22 10:56
谢谢大家,这个问题要怎样解决:但实践中发现按一下KEY4 本意是要时钟加1,实际加了好几个1
...

四楼说的对,实践中按键扫描需要添加按键消抖。一般可在硬件上添加消抖电容或在软件上添加消抖算法。
回复 支持 反对

使用道具 举报

发表于 2023-12-22 12:40:31 来自手机浏览器 | 显示全部楼层
两种可能:按键没有消抖,你以为只按下一次,单片机检测到了多次信号;没有做单次按键处理,如果检测到高电平或低电平就一直响应,那么,一次按键操作响应的次数是不可预测的。
按键处理可以参考一下我之前发的帖子
https://www.mydigit.cn/forum.php?mod=viewthread&tid=393998&fromuid=1850339
回复 支持 反对

使用道具 举报

发表于 2023-12-22 15:03:56 | 显示全部楼层
楼主可以试下,自带硬件USB直接仿真,自带硬件USB直接下载的 RMB1.4的 STC8H8K64U
回复 支持 反对

使用道具 举报

发表于 2023-12-23 12:22:36 | 显示全部楼层
有时间了一定好好学这个,
回复 支持 反对

使用道具 举报

发表于 2023-12-23 19:01:24 | 显示全部楼层
本帖最后由 慕名而来 于 2023-12-23 19:08 编辑

一个来月了网站总是无法翻页、无法回帖,真的很郁闷,上午也打开网站了,打卡后点击主题就崩溃了,此时再试竟然翻阅了好几页了,窃喜!


回复 支持 反对

使用道具 举报

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

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-2 06:56 , Processed in 0.171601 second(s), 12 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

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