找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4201|回复: 9
收起左侧

51单片机按键数码管显示 时间片轮转+状态机 源程序

  [复制链接]
ID:750420 发表于 2021-11-17 11:00 | 显示全部楼层 |阅读模式
前段时间看到了分层思想、时间片轮转和状态机的相关文章正好近期手上有一些小项目,于是就进行了一些尝试。相关的概念就不在细说了,很多嵌入式的公众号文章都有介绍过,下面只叙述下我开发的经历帮助后来者少走弯路也是我个人的一个记录。
我在看到讲述相关思想的文章后在脑子里想着架构出一个相对比较完善的系统。
分层思想不用多说根据相应的软硬件结构进行分层即可,其中的好处会在代码复用时体现出来。
时间片轮转思想在我个人实践的过程中走了很多弯路,本人使用的硬件为清翔电子的最小系统板,在最开始进行开发时使用定时器的方式对固定延时变量(REF20MS等)进行相应的环形累加(if(REF20MS==0)REF20MS=NUM20MS)(NUM20MS为宏定义的定时器计数记录到对应时间所需要的值)。然后当相应的变量计数完成后对相应的标志位进行计数。
在我最初的构想中,希望能够完成一个多个需要相同的延时时间的任务对同一个标记位的检测即可完成延时的效果,但是在实际实现过程中遇到了很多问题。
1、如何判定任务1完成了20ms延时(仅作为举例),判定相应的延时时间的话需要对REF20MS的值进行记录当该变量再一次到达记录值时则说明完成20ms延时,这有一个问题就是定时器中断的时间一定时比单片机主函数中运行的时间要慢的于是当该变量进行记录时定时器未能及时对延时记录变量进行增减从而导致没有延时的现象,当然这种问题可以通过再增加一个记录次数的标记位来解决但是还有更好的解决方式我下面会讲。
2、如何多个任务同时标记并使用同一个延时变量,这个问题使用变量记录加标记位的方式可以去解决但是结构复杂较为繁琐。
在遇到上述问题后我改变了想法,使用一个u32位(u64更好但是51不支持u64)的变量(u32 TimeRef)作为统一的一个时间片(定时器125us中断一次,足够使用一周左右),对每个任务定义一个_DELAY变量(如按键:u32 KEY_DELAY) 在每次需要延时时KEY_DELAY=TimeRef+NUM_20MS;(NUM20MS为宏定义的定时器计数记录到对应时间所需要的值),在使用状态机的思想进行状态转移即可。能够较为简单的时间一些任务轮转的功能,整体代码理解起来也比较简单。整体单片机代码如下:
  1. #include<reg52.h>
  2. #include<intrins.h>
  3. //尝试分层思想与时间片轮转

  4. #define u8  unsigned char
  5. #define u16 unsigned int
  6. #define u32 unsigned long int
  7. //按键引脚映射
  8. sbit         KEY1=P3^5;
  9. sbit         KEY2=P3^4;
  10. sbit         KEY3=P3^3;
  11. sbit         KEY4=P3^2;
  12. sbit         LED1=P1^0;
  13. u8                 KEY_DAT=0;//按键寄存器按键为1存储当前被按下的按键键值,0为无按键

  14. u8 SEG7[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

  15. //时间片为125us
  16. #define NUM_1MS                 8
  17. #define NUM_2MS                 16
  18. #define NUM_5MS                        40
  19. #define NUM_20MS                 160
  20. #define NUM_100MS         800
  21. #define NUM_200MS         1600
  22. #define NUM_500MS         4000
  23. #define NUM_1000MS         8000

  24. //时间片轮转变量
  25. u32 TimeRef=0;




  26. //任务延时变量
  27. //按键任务
  28. u32 KEY_DELAY                                =0;
  29. u8  KEY_STATE                                =0;
  30. u8         KEY_DATx                                =0;
  31. //显示任务
  32. u32 SEG_DELAY                =0;
  33. u8  SEG_STATE                =0;//0位标记是否第一次进入
  34. u8         SEG_NUM[4]        ={0,0,0,0};
  35. u8         SEG_I                          =0;

  36. /////////////////////////函数声明/////////////////////////////////
  37. //读按键键值
  38. u8 ReadKeyDat(void);
  39. //读按键寄存器
  40. u8 ReadKeyReg(void);
  41. //中断初始化函数
  42. void IsrInit(void);
  43. //数码管显示控制函数
  44. void SegDisplay(u16 num);

  45. void main(void)
  46. {
  47.         u16 num=0;
  48.         u8 key=0;
  49.         IsrInit();
  50.         while(1)
  51.         {               
  52.                 SegDisplay(num);
  53.                 key = ReadKeyDat();
  54.                 if(key==1)
  55.                         num++;
  56.                 if(key==2)
  57.                         num--;
  58. //                if(key==3)
  59. //                        LED1=0;
  60. //                if(key==4)
  61. //                        LED1=1 ;
  62.         }
  63.         
  64. }
  65. //数码管显示控制函数
  66. void SegDisplay(u16 num)
  67. {
  68.         u8 i=0;
  69.         if(SEG_STATE==0)//分割字符状态
  70.         {
  71.                 SEG_I=0;
  72.                 for(i=0;i<4;i++)
  73.                 {
  74.                         SEG_NUM[SEG_I++]=num%10;
  75.                         num=num/10;
  76.                 }
  77.                 SEG_STATE=1;
  78.                
  79.         }
  80.                           
  81.         if(SEG_STATE==1)//显示状态
  82.         {
  83.                 P0=SEG7[SEG_NUM[4-SEG_I]];//[];
  84.                 P2=~(0x01<<(SEG_I-1));
  85.                 SEG_I--;
  86.                 SEG_DELAY=TimeRef+NUM_5MS;
  87.                 SEG_STATE=2;
  88.         }

  89.         if(SEG_STATE==2)//延时等待状态
  90.         {
  91.                 if(TimeRef >= SEG_DELAY)
  92.                 {
  93.                         //判断是否为最后一次延时
  94.                         if(SEG_I==0)
  95.                                 SEG_STATE=0;
  96.                         else
  97.                                 SEG_STATE=1;
  98.                 }
  99.         }
  100.         
  101. }



  102. //读按键键值
  103. u8 ReadKeyDat(void)
  104. {
  105.         ReadKeyReg();
  106.         if(KEY_STATE == 0)//第一次读取状态
  107.         {
  108.                 if(KEY_DAT!=0)//读取到按键
  109.                 {
  110.                         KEY_DATx=KEY_DAT;
  111.                         KEY_DELAY=TimeRef+NUM_100MS;
  112.                         KEY_STATE=1;
  113.                 }
  114.         }
  115.         
  116.         if(KEY_STATE == 1)//延时状态
  117.         {
  118.                 if(TimeRef >= KEY_DELAY)
  119.                 {
  120.                         KEY_STATE=2;
  121.                 }
  122.         }
  123.         if(KEY_STATE == 2)//第二次读取状态
  124.         {
  125.                 if(KEY_DATx == KEY_DAT)//读取到按键
  126.                 {
  127.                         KEY_STATE=0;
  128.                         return KEY_DATx;
  129.                 }
  130.                 KEY_STATE=0;
  131.         }
  132.         return 0;
  133. }

  134. //读按键寄存器
  135. u8 ReadKeyReg(void)
  136. {
  137.         if(KEY1==0)
  138.         {
  139.                 KEY_DAT=1;
  140.                 return 1;
  141.         }
  142.         if(KEY2==0)
  143.         {
  144.                 KEY_DAT=2;
  145.                 return 1;
  146.         }
  147.         if(KEY3==0)
  148.         {
  149.                 KEY_DAT=3;
  150.                 return 1;
  151.         }
  152.         if(KEY4==0)
  153.         {
  154.                 KEY_DAT=4;
  155.                 return 1;
  156.         }
  157.         KEY_DAT=0;
  158.         return 0;
  159. }

  160. //中断初始化函数
  161. void IsrInit(void)
  162. {
  163.         EA=1;
  164.         
  165.         //使能定时器中断并开启定时器
  166. //        ET0=1;
  167. //        TR0=1;
  168.         //ET1=1;
  169.         //使能两个外部中断并将其设定为跳沿触发
  170. //        EX0=1;
  171. //        IT0=1;
  172. //        EX1=1;
  173. //        IT1=1;
  174.         //定时器0,方式2,计数器
  175.         TMOD|=0x02;
  176.         TH0=0x8d;//125us 中断一次
  177.         TL0=0x8d;
  178.         ET0=1;
  179.         TR0=1;
  180. //        //使用定时器1,方式2,计数器
  181. //        TMOD|=0x20;
  182. //        TH1=0x8d;//125us 中断一次
  183. //        TL1=0x8d;
  184. }
  185. //定时器0,提供基础时钟125us中断1次
  186. //////////////////////////统一时间完成标志位可能没有意义//////////////////////////////////////
  187. int Tim0Isr() interrupt 1 using 1
  188. {
  189.         TimeRef++;        
  190. //        P0=SEG7[TimeRef/100%10];//[];
  191. //        P2=0xfe;
  192. }
复制代码
Keil代码下载: 1、按键检测控制数码管显示.7z (16.25 KB, 下载次数: 116)

评分

参与人数 1黑币 +90 收起 理由
admin + 90 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:262 发表于 2021-11-18 06:01 | 显示全部楼层
好资料,51黑有你更精彩!!!
回复

使用道具 举报

ID:70035 发表于 2021-11-23 11:06 | 显示全部楼层
结构清晰!赞!
回复

使用道具 举报

ID:189875 发表于 2022-4-9 16:58 | 显示全部楼层
学习受教了,很有用
回复

使用道具 举报

ID:914666 发表于 2022-8-3 15:19 | 显示全部楼层
51黑有你更精彩,学习使人进步
回复

使用道具 举报

ID:983641 发表于 2022-9-8 12:12 | 显示全部楼层
正对这方面感兴趣。
回复

使用道具 举报

ID:88154 发表于 2022-10-13 10:42 | 显示全部楼层
学习使人进步
回复

使用道具 举报

ID:34149 发表于 2022-10-16 15:18 | 显示全部楼层
动态扫描,最近在学习595驱动。谢谢分享
回复

使用道具 举报

ID:933601 发表于 2022-10-17 06:40 | 显示全部楼层
时间片轮转+状态机,精彩
回复

使用道具 举报

ID:46750 发表于 2022-10-18 13:11 | 显示全部楼层
时间片轮转+状态机,精彩
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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