[51单片机] 从单片机基础到程序框架(连载)

[复制链接]
364965|721
 楼主| jianhong_wu 发表于 2018-4-12 09:32 | 显示全部楼层
本帖最后由 jianhong_wu 于 2018-4-12 19:36 编辑

第一百一十三节: 动态扫描的数码管显示数字。

【113.1   动态扫描的数码管。】
   
                上图113.1.1  数码管

       上一节,看到打开显示的数码管右起第1个(com4)和第3个(com2)在任意时刻显示的数字是一样的,为什么?因为四个数码管的8个段码a,b,c,d,e,f,g,h所连接的单片机IO口是共用的,如果把四个数码管全部打开(com1,com2,com3,com4全部输出低电平),会发现四个数码管在任意时刻显示的四个数字也是一样的!实际应用中,要四个数码管能各自独立显示不同的数字,就需要用到“分时动态扫描”的方式。所谓分时,就是在任意时刻只能显示其中一个数码管(某个com输出低电平),其它三个数码管关闭(其它三个com输出高电平),每个数码管显示停留的时间固定一致并且非常短暂,四个数码管依次循环的切换显示,只要切换画面的速度足够快,人的视觉就分辨不出来,感觉八个数码管是同时亮的(实际不是同时亮),跟动画片“1秒钟动态切换显示多少幅画面”的原理一样。现在编写一个程序例子,四个数码管要显示四个不同的数字“1234”,程序代码如下:
  1. #include "REG52.H"  

  2. /* 注释一:
  3. *  SCAN_TIME是每个数码管停留显示的短暂时间。这里称为“扫描时间”。这个时间既不能太长也不能
  4. *  太短,要调试到恰到好处。太长,则影响其它数码管的显示,会让人觉得画面不连贯不是同时亮;
  5. *  太短,又会影响显示的亮度。具体的时间应该根据实际项目不断调试修正而得到最佳显示的数值。
  6. */

  7. #define SCAN_TIME  1   


  8. void T0_time();
  9. void SystemInitial(void) ;
  10. void Delay(unsigned long u32DelayTime) ;
  11. void PeripheralInitial(void) ;

  12. void DisplayScan(void);    //数码管的动态扫描函数,放在定时中断里。

  13. sbit P1_0=P1^0;  //右起第1位数码管的公共端com4
  14. sbit P1_1=P1^1;  //右起第2位数码管的公共端com3
  15. sbit P1_2=P1^2;  //右起第3位数码管的公共端com2
  16. sbit P1_3=P1^3;  //右起第4位数码管的公共端com1

  17. //根据原理图得出的共阴数码管编码转换表,类似于一个字库表
  18. code unsigned char Cu8DigTable[]=
  19. {
  20. 0x3f,  //0       序号0
  21. 0x06,  //1       序号1
  22. 0x5b,  //2       序号2
  23. 0x4f,  //3       序号3
  24. 0x66,  //4       序号4
  25. 0x6d,  //5       序号5
  26. 0x7d,  //6       序号6
  27. 0x07,  //7       序号7
  28. 0x7f,  //8       序号8
  29. 0x6f,  //9       序号9
  30. 0x00,  //不显示  序号10
  31. };

  32. volatile unsigned char vGu8ScanTimerFlag=0;  //动态扫描的定时器
  33. volatile unsigned int vGu16ScanTimerCnt=0;  

  34. /* 注释二:
  35. *  vGu8Display_Righ_4,vGu8Display_Righ_3,vGu8Display_Righ_2,vGu8Display_Righ_1,这四个
  36. *  全局变量用来传递每位数码管需要显示的数字,作为对上面应用层调用的接口变量。
  37. */

  38. volatile unsigned char vGu8Display_Righ_4=1;  //右起第4位数码管显示的变量。这里显示“1”
  39. volatile unsigned char vGu8Display_Righ_3=2;  //右起第3位数码管显示的变量。这里显示“2”
  40. volatile unsigned char vGu8Display_Righ_2=3;  //右起第2位数码管显示的变量。这里显示“3”
  41. volatile unsigned char vGu8Display_Righ_1=4;  //右起第1位数码管显示的变量。这里显示“4”

  42. void main()
  43. {
  44. SystemInitial();            
  45. Delay(10000);               
  46. PeripheralInitial();      
  47.     while(1)  
  48. {  
  49.     }
  50. }

  51. /* 注释三:
  52. *  DisplayScan数码管的动态扫描函数,之所以放在定时中断里,是因为动态扫描数码管对时间均匀度
  53. *  要求很高,如果放在main主函数中,期间稍微出现一些延时滞后或者超前执行的情况,都会导致
  54. *  数码管出现“闪烁”或者“忽暗忽亮”的显示效果。
  55. */

  56. void DisplayScan(void)   
  57. {
  58. static unsigned char Su8GetCode;  //从编码转换表中提取出来的编码。
  59. static unsigned char Su8ScanStep=1;  //扫描步骤

  60. if(0==vGu16ScanTimerCnt)  //定时的时间到,切换显示下一个数码管,依次动态快速循环切换显示
  61. {

  62. /* 注释四:
  63. *  在即将切换显示到下一个新的数码管之前,应该先关闭显示所有的数码管,避免因关闭不彻底而导致
  64. *  数码管某些段位出现“漏光”,也就是数码管因程序处理不善而出现常见的“鬼影”显示情况。
  65. */

  66.     P0=0x00; //输出显示先清零,先关闭显示所有的数码管
  67.     //先关闭所有的com口,先关闭显示所有的数码管
  68. P1_0=1;  //右起第1位数码管的公共端com4,“总开关”关闭,输出低电平1
  69. P1_1=1;  //右起第2位数码管的公共端com3,“总开关”关闭,输出高电平1
  70. P1_2=1;  //右起第3位数码管的公共端com2,“总开关”关闭,输出低电平1
  71. P1_3=1;  //右起第4位数码管的公共端com1,“总开关”关闭,输出高电平1

  72.     switch(Su8ScanStep)
  73. {
  74.        case 1: //显示右起第1个数码管
  75. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1]; //从编码转换表中提取出来的编码。
  76.     P0=Su8GetCode; //段码端输出需要显示的编码
  77. P1_0=0;  //右起第1位数码管的公共端com4,“总开关”打开,输出低电平0
  78. P1_1=1;  //右起第2位数码管的公共端com3,“总开关”关闭,输出高电平1
  79. P1_2=1;  //右起第3位数码管的公共端com2,“总开关”关闭,输出高电平1
  80. P1_3=1;  //右起第4位数码管的公共端com1,“总开关”关闭,输出高电平1            
  81. break;

  82.        case 2: //显示右起第2个数码管
  83. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2]; //从编码转换表中提取出来的编码。
  84.     P0=Su8GetCode; //段码端输出需要显示的编码
  85. P1_0=1;  //右起第1位数码管的公共端com4,“总开关”关闭,输出高电平1
  86. P1_1=0;  //右起第2位数码管的公共端com3,“总开关”打开,输出低电平0
  87. P1_2=1;  //右起第3位数码管的公共端com2,“总开关”关闭,输出高电平1
  88. P1_3=1;  //右起第4位数码管的公共端com1,“总开关”关闭,输出高电平1            
  89. break;

  90.        case 3: //显示右起第3个数码管
  91. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3]; //从编码转换表中提取出来的编码。
  92.     P0=Su8GetCode; //段码端输出需要显示的编码
  93. P1_0=1;  //右起第1位数码管的公共端com4,“总开关”关闭,输出高电平1
  94. P1_1=1;  //右起第2位数码管的公共端com3,“总开关”关闭,输出高电平1
  95. P1_2=0;  //右起第3位数码管的公共端com2,“总开关”打开,输出低电平0
  96. P1_3=1;  //右起第4位数码管的公共端com1,“总开关”关闭,输出高电平1            
  97. break;

  98.        case 4: //显示右起第4个数码管
  99. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4]; //从编码转换表中提取出来的编码。
  100.     P0=Su8GetCode; //段码端输出需要显示的编码
  101. P1_0=1;  //右起第1位数码管的公共端com4,“总开关”关闭,输出高电平1
  102. P1_1=1;  //右起第2位数码管的公共端com3,“总开关”关闭,输出高电平1
  103. P1_2=1;  //右起第3位数码管的公共端com2,“总开关”关闭,输出高电平1
  104. P1_3=0;  //右起第4位数码管的公共端com1,“总开关”打开,输出低电平0            
  105. break;

  106. }

  107. Su8ScanStep++;
  108. if(Su8ScanStep>4) //如果扫描步骤大于4,继续从第1步开始扫描
  109. {
  110. Su8ScanStep=1;

  111. }

  112. vGu8ScanTimerFlag=0;
  113. vGu16ScanTimerCnt=SCAN_TIME;  
  114. vGu8ScanTimerFlag=1;  //启动新一轮的定时器
  115. }
  116. }

  117. void T0_time() interrupt 1     
  118. {
  119. DisplayScan(); //数码管的动态扫描函数

  120. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  //数码管显示切换时间的定时器
  121. {
  122. vGu16ScanTimerCnt--;
  123. }

  124.    
  125. TH0=0xfc;   
  126. TL0=0x66;   
  127. }


  128. void SystemInitial(void)
  129. {
  130. //初始化上电瞬间数码管的状态,关闭显示所有的数码管
  131. P0=0x00;
  132. P1_0=1;  //右起第1位数码管的公共端com4,“总开关”关闭,输出低电平1
  133. P1_1=1;  //右起第2位数码管的公共端com3,“总开关”关闭,输出高电平1
  134. P1_2=1;  //右起第3位数码管的公共端com2,“总开关”关闭,输出低电平1
  135. P1_3=1;  //右起第4位数码管的公共端com1,“总开关”关闭,输出高电平1
  136.    
  137. TMOD=0x01;  
  138. TH0=0xfc;   
  139. TL0=0x66;   
  140. EA=1;      
  141. ET0=1;      
  142. TR0=1;      
  143. }

  144. void Delay(unsigned long u32DelayTime)
  145. {
  146.     for(;u32DelayTime>0;u32DelayTime--);
  147. }

  148. void PeripheralInitial(void)
  149. {

  150. }






本帖子中包含更多资源

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

×
y15067805290 发表于 2018-4-12 16:29 | 显示全部楼层

期待楼主给我们带来更多的干货哦~~
wangwenwuaisiyu 发表于 2018-4-13 09:22 | 显示全部楼层
支持一下楼主
wangwenwuaisiyu 发表于 2018-4-13 16:20 | 显示全部楼层
讲的太好了,看了一天都不觉的的累,越看越来劲了,
michael1982 发表于 2018-4-13 18:33 | 显示全部楼层
膜拜大牛
 楼主| jianhong_wu 发表于 2018-4-16 12:53 | 显示全部楼层
第一百一十四节: 动态扫描的数码管显示小数点。

【114.1   动态扫描的数码管显示小数点。】
   
                上图114.1.1  数码管

       如上图,小数点的段码是h,对应单片机的P0.7口。数码管编码转换表(类似字库)的11个以字节为单位的数据,把它们从十六进制转换成二进制后,可以发现第7位(对应P0.7口)都是0。因此,从转换表里取数据后,得到的数据默认是让数码管的小数点不显示的。如果想显示这个小数点,就需要用到“或(|)”语句操作。比如,本节程序需要显示“1.234”这个带小数点的数值,代码如下:



  1. #include "REG52.H"  

  2. #define SCAN_TIME  1   

  3. void T0_time();
  4. void SystemInitial(void) ;
  5. void Delay(unsigned long u32DelayTime) ;
  6. void PeripheralInitial(void) ;

  7. void DisplayScan(void);   

  8. sbit P1_0=P1^0;  
  9. sbit P1_1=P1^1;  
  10. sbit P1_2=P1^2;  
  11. sbit P1_3=P1^3;

  12. //转换表,里面的11个数据,转换成二进制后,第7位数据都是0默认不显示小数点
  13. code unsigned char Cu8DigTable[]=
  14. {
  15. 0x3f,  //0       序号0
  16. 0x06,  //1       序号1
  17. 0x5b,  //2       序号2
  18. 0x4f,  //3       序号3
  19. 0x66,  //4       序号4
  20. 0x6d,  //5       序号5
  21. 0x7d,  //6       序号6
  22. 0x07,  //7       序号7
  23. 0x7f,  //8       序号8
  24. 0x6f,  //9       序号9
  25. 0x00,  //不显示  序号10
  26. };

  27. volatile unsigned char vGu8ScanTimerFlag=0;  
  28. volatile unsigned int vGu16ScanTimerCnt=0;  


  29. volatile unsigned char vGu8Display_Righ_4=1;  //右起第4位数码管显示的变量。这里显示“1”
  30. volatile unsigned char vGu8Display_Righ_3=2;  //右起第3位数码管显示的变量。这里显示“2”
  31. volatile unsigned char vGu8Display_Righ_2=3;  //右起第2位数码管显示的变量。这里显示“3”
  32. volatile unsigned char vGu8Display_Righ_1=4;  //右起第1位数码管显示的变量。这里显示“4”

  33. /* 注释一:
  34. *  vGu8Display_Righ_Dot_4,vGu8Display_Righ_Dot_3,vGu8Display_Righ_Dot_2,
  35. *  vGu8Display_Righ_Dot_1,这四个全局变量用来传递每位数码管是否需要显示它的小数点,如果是1
  36. *  代表需要显示其小数点,如果是0则不显示小数点。这四个变量作为对上面应用层调用的接口变量。
  37. */

  38. volatile unsigned char vGu8Display_Righ_Dot_4=1;  //右起第4位数码管的小数点。1代表打开显示。
  39. volatile unsigned char vGu8Display_Righ_Dot_3=0;  //右起第3位数码管的小数点。0代表关闭显示。
  40. volatile unsigned char vGu8Display_Righ_Dot_2=0;  //右起第2位数码管的小数点。0代表关闭显示。
  41. volatile unsigned char vGu8Display_Righ_Dot_1=0;  //右起第1位数码管的小数点。0代表关闭显示。

  42. void main()
  43. {
  44. SystemInitial();            
  45. Delay(10000);               
  46. PeripheralInitial();      
  47.     while(1)  
  48. {  
  49.     }
  50. }


  51. void DisplayScan(void)   
  52. {
  53. static unsigned char Su8GetCode;  
  54. static unsigned char Su8ScanStep=1;  

  55. if(0==vGu16ScanTimerCnt)  
  56. {


  57.     P0=0x00;
  58. P1_0=1;  
  59. P1_1=1;  
  60. P1_2=1;  
  61. P1_3=1;  

  62.     switch(Su8ScanStep)
  63. {
  64.        case 1:
  65. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  66. /* 注释二:
  67. *  这里是本节的关键。通过判断全局的接口变量的数值,来决定是否打开显示小数点。
  68. *  从转换表取出字模数据后再跟0x80进行“或”运算即可把第7位数据改为1。
  69. */

  70. if(1==vGu8Display_Righ_Dot_1) //如果打开了需要显示第1个数码管的小数点
  71. {
  72. Su8GetCode=Su8GetCode|0x80;  //把第7位数据改为1,显示小数点
  73. }
  74.     P0=Su8GetCode;
  75. P1_0=0;
  76. P1_1=1;  
  77. P1_2=1;  
  78. P1_3=1;            
  79. break;

  80.        case 2:
  81. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  82. if(1==vGu8Display_Righ_Dot_2) //如果打开了需要显示第2个数码管的小数点
  83. {
  84. Su8GetCode=Su8GetCode|0x80;  //把第7位数据改为1,显示小数点
  85. }
  86.     P0=Su8GetCode;
  87. P1_0=1;  
  88. P1_1=0;
  89. P1_2=1;
  90. P1_3=1;         
  91. break;

  92.        case 3:
  93. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  94. if(1==vGu8Display_Righ_Dot_3) //如果打开了需要显示第3个数码管的小数点
  95. {
  96. Su8GetCode=Su8GetCode|0x80;  //把第7位数据改为1,显示小数点
  97. }
  98.     P0=Su8GetCode;
  99. P1_0=1;  
  100. P1_1=1;  
  101. P1_2=0;  
  102. P1_3=1;         
  103. break;

  104.        case 4:
  105. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  106. if(1==vGu8Display_Righ_Dot_4) //如果打开了需要显示第4个数码管的小数点
  107. {
  108. Su8GetCode=Su8GetCode|0x80;  //把第7位数据改为1,显示小数点
  109. }
  110.     P0=Su8GetCode;
  111. P1_0=1;  
  112. P1_1=1;  
  113. P1_2=1;  
  114. P1_3=0;           
  115. break;

  116. }

  117. Su8ScanStep++;
  118. if(Su8ScanStep>4)
  119. {
  120. Su8ScanStep=1;
  121. }

  122. vGu8ScanTimerFlag=0;
  123. vGu16ScanTimerCnt=SCAN_TIME;  
  124. vGu8ScanTimerFlag=1;  
  125. }
  126. }

  127. void T0_time() interrupt 1     
  128. {
  129. DisplayScan();

  130. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  131. {
  132. vGu16ScanTimerCnt--;
  133. }


  134. TH0=0xfc;   
  135. TL0=0x66;   
  136. }


  137. void SystemInitial(void)
  138. {
  139. P0=0x00;
  140. P1_0=1;  
  141. P1_1=1;  
  142. P1_2=1;  
  143. P1_3=1;  

  144. TMOD=0x01;  
  145. TH0=0xfc;   
  146. TL0=0x66;   
  147. EA=1;      
  148. ET0=1;      
  149. TR0=1;      
  150. }

  151. void Delay(unsigned long u32DelayTime)
  152. {
  153.     for(;u32DelayTime>0;u32DelayTime--);
  154. }

  155. void PeripheralInitial(void)
  156. {

  157. }


本帖子中包含更多资源

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

×
 楼主| jianhong_wu 发表于 2018-4-22 12:07 | 显示全部楼层
第一百一十五节: 按键控制数码管的秒表。

【115.1   按键控制数码管的秒表。】
   
                上图115.1.1  数码管




                上图115.1.2  独立按键

       本节通过一个秒表的小项目,让大家学会以下4个知识点:
      (1)上层的界面显示框架几乎都要用到更新变量,更新变量包括整屏更新和局部更新,本节只用到整屏更新。更新变量是用全局变量在函数之间传递信息。作用是,当有某个需要显示的数据发生改变的时候,就要给更新变量置1,让显示函数重新更新一次显示,确保最新的数据能及时显示出来,平时没有数据更新改变的时候不用频繁更新显示避免占用CPU过多的时间。
      (2)凡是需要显示数字的地方,都必须涉及如何把一个数据按“个十百千万...”的位逐个提取出来的算法。这个算法比较简单,主要用“求余”和“求商”这两个运算语句就可以随心所欲的把数据位提取出来。除此之外,还要学会如何用if语句判断数据的范围,来把高位尚未用到的某个数码管屏蔽,让该位数码管只显示一个“不显示”的数据(避免直接显示一个0)。
      (3)我常把单片机程序简化成4个代表:按键(人机输入),数码管(人机界面),跑马灯(应用程序),串口(通信)。本节的“应用程序”不是跑马灯,而是秒表。不管是跑马灯,还是秒表,都要用到一个总启动Gu8RunStart和一个总运行步骤Gu8RunStep。建议大家,总启动Gu8RunStart和总运行步骤Gu8RunStep应该成双成对的出现(这样关断响应更及时,并且结构更紧凑,漏洞更少),比如,凡是总启动Gu8RunStart发生改变的时候,总运行步骤Gu8RunStep都复位归零一下。
      (4)一个硬件的定时器中断,可以衍生出N个软件定时器,之前跟大家介绍的是“递减式”的软件定时器,而且实际应用中,“递减式”的软件定时器也是用得最多。本节因为项目的需要,需要用到的是“累加式”的软件定时器。不管是哪种软件定时器,大家都要注意定时器变量在定义时所用到的数据类型,这个数据类型决定了定时时间的长度,比如在51单片机中,unsigned int的范围是0到65535,最大一次性定时65.535秒。而unsigned long的范围是0到4294967295,最大一次性定时4294967.295秒。本节秒表的时间超过65.535秒,因此需要用到unsigned long类型的定时器变量。
本节秒表程序的功能:K1按键是复位按键,每按一次,秒表都停止并且重新归零。K2按键是启动和暂停按键,当秒表处于复位后停止的状态时按一次则开始启动,当秒表处于正在工作的状态时按一次则处于暂停状态,当秒表处于暂停的状态时按一次则继续处于工作的状态。本节4位数码管,显示的时间是带2位小数点的,能显示的时间范围是:0.00秒到99.99秒。代码如下:

  1. #include "REG52.H"  

  2. #define KEY_FILTER_TIME  25

  3. #define SCAN_TIME  1   

  4. void T0_time();
  5. void SystemInitial(void) ;
  6. void Delay(unsigned long u32DelayTime) ;
  7. void PeripheralInitial(void) ;

  8. void KeyScan(void);  
  9. void KeyTask(void);  

  10. void DisplayScan(void);  //底层显示的驱动函数  
  11. void DisplayTask(void);  //上层显示的任务函数

  12. void RunTask(void);  //秒表的应用程序

  13. sbit KEY_INPUT1=P2^2;  
  14. sbit KEY_INPUT2=P2^1;  

  15. sbit P1_0=P1^0;  
  16. sbit P1_1=P1^1;  
  17. sbit P1_2=P1^2;  
  18. sbit P1_3=P1^3;

  19. //数码管转换表
  20. code unsigned char Cu8DigTable[]=
  21. {
  22. 0x3f,  //0       序号0
  23. 0x06,  //1       序号1
  24. 0x5b,  //2       序号2
  25. 0x4f,  //3       序号3
  26. 0x66,  //4       序号4
  27. 0x6d,  //5       序号5
  28. 0x7d,  //6       序号6
  29. 0x07,  //7       序号7
  30. 0x7f,  //8       序号8
  31. 0x6f,  //9       序号9
  32. 0x00,  //不显示  序号10
  33. };

  34. //数码管底层驱动扫描的软件定时器
  35. volatile unsigned char vGu8ScanTimerFlag=0;  
  36. volatile unsigned int vGu16ScanTimerCnt=0;  

  37. //秒表的软件定时器,注意,这里是unsigned long类型,范围是0到4294967295毫秒
  38. volatile unsigned char vGu8StopWatchTimerFlag=0;  
  39. volatile unsigned long vGu32StopWatchTimerCnt=0;  

  40. //数码管上层每10ms就定时刷新一次显示的软件定时器。用于及时更新显示秒表当前的实时数值
  41. volatile unsigned char vGu8UpdateTimerFlag=0;  
  42. volatile unsigned int vGu16UpdateTimerCnt=0;  


  43. unsigned char Gu8RunStart=0;  //应用程序的总启动
  44. unsigned char Gu8RunStep=0;  //应用程序的总运行步骤。建议跟vGu8RunStart成双成对出现
  45. unsigned char Gu8RunStatus=0; //当前秒表的状态。0代表停止,1代表正在工作中,2代表暂停

  46. unsigned char Gu8WdUpdate=1;  //开机默认整屏更新一次显示。此变量在显示框架中是非常重要的变量

  47. volatile unsigned char vGu8Display_Righ_4=10;  //开机默认最高位数码管显示一个“不显示”数据
  48. volatile unsigned char vGu8Display_Righ_3=0;  
  49. volatile unsigned char vGu8Display_Righ_2=0;  
  50. volatile unsigned char vGu8Display_Righ_1=0;  

  51. volatile unsigned char vGu8Display_Righ_Dot_4=0;  
  52. volatile unsigned char vGu8Display_Righ_Dot_3=1;    //开机默认保留显示2个小数点
  53. volatile unsigned char vGu8Display_Righ_Dot_2=0;  
  54. volatile unsigned char vGu8Display_Righ_Dot_1=0;  

  55. volatile unsigned char vGu8KeySec=0;  

  56. void main()
  57. {
  58. SystemInitial();            
  59. Delay(10000);               
  60. PeripheralInitial();      
  61.     while(1)  
  62. {  
  63. KeyTask();      //按键的任务函数
  64. DisplayTask();  //数码管显示的上层任务函数
  65. RunTask();      //秒表的应用程序
  66.     }
  67. }

  68. void KeyTask(void)    //按键的任务函数
  69. {
  70. if(0==vGu8KeySec)
  71. {
  72. return;
  73. }

  74. switch(vGu8KeySec)
  75. {
  76.    case 1:     //复位按键

  77. Gu8RunStatus=0; //秒表返回停止的状态

  78. Gu8RunStart=0;  //秒表停止
  79.             Gu8RunStep=0;  //总运行步骤归零。建议跟vGu8RunStart成双成对出现

  80. vGu8StopWatchTimerFlag=0;  
  81. vGu32StopWatchTimerCnt=0;   //秒表的软件定时器清零

  82.             Gu8WdUpdate=1;  //整屏更新一次显示

  83. vGu8KeySec=0;  
  84. break;

  85.    case 2:     //启动与暂停的按键
  86.         if(0==Gu8RunStatus) //在停止状态下
  87.         {
  88.             Gu8RunStatus=1;  //秒表处于工作状态

  89. vGu8StopWatchTimerFlag=0;
  90. vGu32StopWatchTimerCnt=0;  
  91. vGu8StopWatchTimerFlag=1;  //启动秒表的软件定时器


  92. Gu8RunStart=1;   //秒表总开关启动
  93.                 Gu8RunStep=0;    //总运行步骤归零。建议跟vGu8RunStart成双成对出现
  94. }
  95.         else if(1==Gu8RunStatus) //在工作状态下
  96.         {
  97.             Gu8RunStatus=2;  //秒表处于暂停状态
  98. }
  99.         else  //在暂停状态下
  100.         {
  101.             Gu8RunStatus=1;  //秒表处于工作状态
  102. }

  103.         Gu8WdUpdate=1;  //整屏更新一次显示,确保在暂停的时候能显示到最新的数据

  104. vGu8KeySec=0;
  105. break;

  106. }
  107. }

  108. void DisplayTask(void) //数码管显示的上层任务函数
  109. {
  110. //需要借用的中间变量,用来拆分数据位
  111. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量

  112. /* 注释一:
  113. *  此处为什么要多加4个中间过渡变量Su8Temp_X?是因为vGu32StopWatchTimerCnt分解数据的时候
  114. *  需要进行除法和求余数的运算,就会用到好多条指令,就会耗掉一点时间,类似延时了一会。我们
  115. *  的定时器每隔一段时间都会产生中断,然后在中断里驱动数码管显示,当vGu32StopWatchTimerCnt
  116. *  还没完全分解出4位有效数据时,这个时候来的定时中断,就有可能导致显示的数据瞬间产生不完整,
  117. *  影响显示效果。因此,为了把需要显示的数据过渡最快,所以采取了先分解,再过渡显示的方法。
  118. */

  119. if(1==Gu8WdUpdate) //如果需要整屏更新
  120. {
  121. Gu8WdUpdate=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  122.           //先分解数据

  123.           //Su8Temp_4提取“十秒”位。
  124. Su8Temp_4=vGu32StopWatchTimerCnt/10000; //实际精度是0.0001秒,但显示精度是0.01秒
  125. Su8Temp_4=Su8Temp_4%10;

  126.          //Su8Temp_3提取“个秒”位。
  127. Su8Temp_3=vGu32StopWatchTimerCnt/1000; //实际精度是0.0001秒,但显示精度是0.01秒
  128. Su8Temp_3=Su8Temp_3%10;

  129.          //Su8Temp_2提取“百毫秒”位。
  130. Su8Temp_2=vGu32StopWatchTimerCnt/100; //实际精度是0.0001秒,但显示精度是0.01秒
  131. Su8Temp_2=Su8Temp_2%10;

  132.           //Su8Temp_1提取“十毫秒”位。
  133. Su8Temp_1=vGu32StopWatchTimerCnt/10; //实际精度是0.0001秒,但显示精度是0.01秒
  134. Su8Temp_1=Su8Temp_1%10;

  135.           //判断数据范围,来决定最高位数码管是否需要显示。
  136.           if(vGu32StopWatchTimerCnt<10000) //10.000秒。实际4位数码管最大只能显示99.99秒
  137.           {
  138.              Su8Temp_4=10;  //在数码管转换表里,10代表一个“不显示”的数据
  139. }

  140. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  141. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  142. vGu8Display_Righ_3=Su8Temp_3;  
  143. vGu8Display_Righ_2=Su8Temp_2;  
  144. vGu8Display_Righ_1=Su8Temp_1;  

  145. vGu8Display_Righ_Dot_4=0;  
  146. vGu8Display_Righ_Dot_3=1;   //保留显示2位小数点
  147. vGu8Display_Righ_Dot_2=0;  
  148. vGu8Display_Righ_Dot_1=0;  

  149. }
  150. }

  151. void RunTask(void)  //秒表的应用程序
  152. {
  153. if(0==Gu8RunStart)
  154. {
  155.    return;  // 如果秒表处于停止状态,则直接退出当前函数,不执行该函数以下的其它代码
  156. }

  157. switch(Gu8RunStep)
  158. {
  159.     case 0:  //在这个步骤里,主要用来初始化一些参数

  160. vGu8UpdateTimerFlag=0;
  161. vGu16UpdateTimerCnt=10;  //每10ms更新显示一次当前秒表的时间
  162. vGu8UpdateTimerFlag=1;

  163. Gu8RunStep=1; //跳转到每10ms更新显示一次的步骤里
  164. break;

  165.     case 1:  //每10ms更新一次显示,确保实时显示秒表当前的时间
  166.        if(0==vGu16UpdateTimerCnt) //每10ms更新显示一次当前秒表的时间
  167.        {
  168.     vGu8UpdateTimerFlag=0;
  169. vGu16UpdateTimerCnt=10;  //重置定时器,为下一个10ms更新做准备
  170. vGu8UpdateTimerFlag=1;

  171.             Gu8WdUpdate=1;  //整屏更新一次显示当前秒表的时间
  172. }
  173. break;

  174. }

  175. }


  176. void KeyScan(void)  //按键底层的驱动扫描函数,放在定时中断函数里
  177. {
  178.    static unsigned char Su8KeyLock1;
  179.    static unsigned int  Su16KeyCnt1;
  180.    static unsigned char Su8KeyLock2;
  181.    static unsigned int  Su16KeyCnt2;

  182.    if(0!=KEY_INPUT1)
  183.    {
  184.       Su8KeyLock1=0;
  185.       Su16KeyCnt1=0;   
  186.    }
  187.    else if(0==Su8KeyLock1)
  188.    {
  189.       Su16KeyCnt1++;
  190.       if(Su16KeyCnt1>=KEY_FILTER_TIME)
  191.       {
  192.          Su8KeyLock1=1;  
  193.          vGu8KeySec=1;   
  194.       }
  195.    }

  196.    if(0!=KEY_INPUT2)
  197.    {
  198.       Su8KeyLock2=0;
  199.       Su16KeyCnt2=0;      
  200.    }
  201.    else if(0==Su8KeyLock2)
  202.    {
  203.       Su16KeyCnt2++;
  204.       if(Su16KeyCnt2>=KEY_FILTER_TIME)
  205.       {
  206.          Su8KeyLock2=1;  
  207.          vGu8KeySec=2;   
  208.       }
  209.    }
  210. }

  211. void DisplayScan(void)    //数码管底层的驱动扫描函数,放在定时中断函数里
  212. {
  213. static unsigned char Su8GetCode;  
  214. static unsigned char Su8ScanStep=1;  

  215. if(0==vGu16ScanTimerCnt)  
  216. {


  217.     P0=0x00;
  218. P1_0=1;  
  219. P1_1=1;  
  220. P1_2=1;  
  221. P1_3=1;  

  222.     switch(Su8ScanStep)
  223. {
  224.        case 1:
  225. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  226. if(1==vGu8Display_Righ_Dot_1)
  227. {
  228. Su8GetCode=Su8GetCode|0x80;  
  229. }
  230.     P0=Su8GetCode;
  231. P1_0=0;
  232. P1_1=1;  
  233. P1_2=1;  
  234. P1_3=1;            
  235. break;

  236.        case 2:
  237. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  238. if(1==vGu8Display_Righ_Dot_2)
  239. {
  240. Su8GetCode=Su8GetCode|0x80;  
  241. }
  242.     P0=Su8GetCode;
  243. P1_0=1;  
  244. P1_1=0;
  245. P1_2=1;
  246. P1_3=1;         
  247. break;

  248.        case 3:
  249. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  250. if(1==vGu8Display_Righ_Dot_3)
  251. {
  252. Su8GetCode=Su8GetCode|0x80;  
  253. }
  254.     P0=Su8GetCode;
  255. P1_0=1;  
  256. P1_1=1;  
  257. P1_2=0;  
  258. P1_3=1;         
  259. break;

  260.        case 4:
  261. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  262. if(1==vGu8Display_Righ_Dot_4)
  263. {
  264. Su8GetCode=Su8GetCode|0x80;  
  265. }
  266.     P0=Su8GetCode;
  267. P1_0=1;  
  268. P1_1=1;  
  269. P1_2=1;  
  270. P1_3=0;           
  271. break;

  272. }

  273. Su8ScanStep++;
  274. if(Su8ScanStep>4)
  275. {
  276. Su8ScanStep=1;
  277. }

  278. vGu8ScanTimerFlag=0;
  279. vGu16ScanTimerCnt=SCAN_TIME;  
  280. vGu8ScanTimerFlag=1;  
  281. }
  282. }

  283. void T0_time() interrupt 1     
  284. {
  285. KeyScan();      //按键底层的驱动扫描函数
  286. DisplayScan();  //数码管底层的驱动扫描函数

  287. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  288. {
  289. vGu16ScanTimerCnt--;  //递减式的软件定时器
  290. }

  291. //每10ms就定时更新一次显示的软件定时器
  292. if(1==vGu8UpdateTimerFlag&&vGu16UpdateTimerCnt>0)  
  293. {
  294. vGu16UpdateTimerCnt--;  //递减式的软件定时器
  295. }

  296. //秒表实际走的时间的软件定时器,注意,这里是“累加式”的软件定时器
  297. if(1==vGu8StopWatchTimerFlag&&1==Gu8RunStatus) //秒表处于工作的状态
  298. {
  299. vGu32StopWatchTimerCnt++;  //累加式的软件定时器
  300. }


  301. TH0=0xfc;   
  302. TL0=0x66;   
  303. }


  304. void SystemInitial(void)
  305. {
  306. P0=0x00;
  307. P1_0=1;  
  308. P1_1=1;  
  309. P1_2=1;  
  310. P1_3=1;  

  311. TMOD=0x01;  
  312. TH0=0xfc;   
  313. TL0=0x66;   
  314. EA=1;      
  315. ET0=1;      
  316. TR0=1;      
  317. }

  318. void Delay(unsigned long u32DelayTime)
  319. {
  320.     for(;u32DelayTime>0;u32DelayTime--);
  321. }

  322. void PeripheralInitial(void)
  323. {

  324. }


本帖子中包含更多资源

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

×
arima 发表于 2018-4-23 12:36 | 显示全部楼层
加油!!!
伯乐121 发表于 2018-4-24 09:43 | 显示全部楼层

本公司诚聘电子工程师:
一.岗位要求
1. 本科以上学历,机电一体化、电子、电子信息等电子类相关专业;
2.2年以上电子设计相关经验
3. 熟悉基本的数字电路和模拟电路,能分析常见电路的工作原理
4. 熟练掌握单片机C语言编程,有良好的编程风格;至少熟练使用一种单片机(STM8/STM32等)进行产品开发
5. 良好的逻辑思维能力,能比较好的对产品进行整体把控;
6. 能积极主动解决产品设计中遇到的各种问题
7. 具有良好的团队合作精神,服从工作安排
8. 参加电子设计大赛并获奖者,或参加过学校电子协会的优先;
9、要求英语4级以上,能较流畅的阅读元器件英文版datasheet
10、有较好的学习能力和一定的抗压能力,能够独立解决技术问题,根据项目需要主动学习和掌握新的技术。

二.岗位职责
从事智能门锁(指纹密码锁、酒店锁、柜锁、联网锁等)的软硬件设计
具体职责包括:1. 客户需求整理(客户提供功能定义、电路板尺寸图等)
2. 原理图设计和审核,包括指纹、触摸密码、13.56MHz卡片读写、125KHz卡片读写、语音、遥控、OLED液晶显示等,这些都已经实现了模块化,只需根据客户要求做少量调整;
3.重点是 MCU单片机程序编写,主要使用STM8/STM32
4. 生产文档制作,包括物料清单、生产指导书、生产测试文档等,都是基于公司的模板来做。
5. 编写产品使用说明书
6.各种新技术/新方案的研发和集成




三.补充职位亮点
1.根据个人能力月薪8-12K,按国家法定节假日放假,过试用期购买保险。
2一般有月平均实发工资的2倍左右作为年终奖              
3.每天有工作下午茶:水果、点心、咖啡等
4.每几月不定期的举行集体活动:徒步、登山、看电影、烧烤等。
5.一年两次旅游
工作地点:东莞市石碣镇
联系人:李'S       QQ:244714897

chentiaotiao 发表于 2018-4-25 14:36 | 显示全部楼层
jianhong_wu 发表于 2017-9-4 19:36
第八十五节: 定时中断的寄存器配置。

【85.1   寄存器配置的本质。】

吴老师,如果我把案例里的if判断语句>=改成<=的话,我发现LED灯还是会闪烁,而且频率更快了,这是怎么回事呢?
yhchen2001 发表于 2018-4-26 14:00 | 显示全部楼层
为无私的老师点赞!
xiaofengfeng 发表于 2018-4-28 22:41 | 显示全部楼层
希望中国工程师 联合
 楼主| jianhong_wu 发表于 2018-4-30 11:50 | 显示全部楼层
第一百一十六节: 按键控制数码管的倒计时。

【116.1   按键控制数码管的倒计时。】
  
                上图116.1.1  数码管




                上图116.1.2  独立按键

         
                上图116.1.3  有源蜂鸣器

       上一节讲“累加式”的秒表,这一节讲“递减式”的倒计时。通过此小项目,加深理解在显示框架中常用的更新变量(整屏更新和局部更新),以及应用程序与按键是如何关联的框架。同时,在拆分“个十百千万...”的时候,有一处地方必须注意,“先整除后求余”必须用一行代码一气呵成,不能拆分成两行代码“先整除;后求余”,否则会有隐患会有bug。除非,把四个临时变都改成unsigned long类型。上一节在此处残留了一个bug我发帖后还来不及修改过来,敬请大家注意。比如:

        以下这样是对的:
  1. static unsigned char Su8Temp_1;
  2. Su8Temp_1=vGu32CountdownTimerCnt/10%10; //一气呵成,没毛病。


        以下这样是有隐患有bug的(除非把Su8Temp_1改成unsigned long类型):
  1. static unsigned char Su8Temp_1;
  2. Su8Temp_1=vGu32CountdownTimerCnt/10;
  3. Su8Temp_1=Su8Temp_1%10; //拆分成两行代码后,有隐患有bug。数据溢出的原因引起。


        本节倒计时程序的功能:K1按键是复位按键,每按一次,倒计时停止并且重新恢复初始值10.00秒。K2按键是启动按键,当秒表处于复位后停止的状态时按一次则开始启动倒计时,当倒计时变为0.00秒的时候,蜂鸣器发出一次“滴”的提示声。此时,如果想启动新一轮的倒计时,只需按一次K1复位键,然后再按K2启动按键,就可以启动新一轮10.00秒的倒计时。代码如下:

  1. #include "REG52.H"  

  2. #define KEY_FILTER_TIME  25

  3. #define SCAN_TIME  1   

  4. #define VOICE_TIME   50     //蜂鸣器一次“滴”的声音长度 50ms  

  5. void T0_time();
  6. void SystemInitial(void) ;
  7. void Delay(unsigned long u32DelayTime) ;
  8. void PeripheralInitial(void) ;

  9. void KeyScan(void);  
  10. void KeyTask(void);  

  11. void VoiceScan(void);  //蜂鸣器的驱动函数
  12. void DisplayScan(void);  //底层显示的驱动函数  
  13. void DisplayTask(void);  //上层显示的任务函数

  14. void RunTask(void);  //倒计时的应用程序

  15. void BeepOpen(void);   
  16. void BeepClose(void);

  17. sbit KEY_INPUT1=P2^2;  
  18. sbit KEY_INPUT2=P2^1;  

  19. sbit P1_0=P1^0;  
  20. sbit P1_1=P1^1;  
  21. sbit P1_2=P1^2;  
  22. sbit P1_3=P1^3;

  23. sbit P3_4=P3^4;

  24. //数码管转换表
  25. code unsigned char Cu8DigTable[]=
  26. {
  27. 0x3f,  //0       序号0
  28. 0x06,  //1       序号1
  29. 0x5b,  //2       序号2
  30. 0x4f,  //3       序号3
  31. 0x66,  //4       序号4
  32. 0x6d,  //5       序号5
  33. 0x7d,  //6       序号6
  34. 0x07,  //7       序号7
  35. 0x7f,  //8       序号8
  36. 0x6f,  //9       序号9
  37. 0x00,  //不显示  序号10
  38. };

  39. //数码管底层驱动扫描的软件定时器
  40. volatile unsigned char vGu8ScanTimerFlag=0;  
  41. volatile unsigned int vGu16ScanTimerCnt=0;  

  42. //倒计时的软件定时器,注意,这里是unsigned long类型,范围是0到4294967295毫秒
  43. volatile unsigned char vGu8CountdownTimerFlag=0;  
  44. volatile unsigned long vGu32CountdownTimerCnt=10000;  //开机默认显示初始值10.000秒

  45. //数码管上层每10ms就定时刷新一次显示的软件定时器。用于及时更新显示秒表当前的实时数值
  46. volatile unsigned char vGu8UpdateTimerFlag=0;  
  47. volatile unsigned int vGu16UpdateTimerCnt=0;  

  48. //蜂鸣器的软件定时器
  49. volatile unsigned char vGu8BeepTimerFlag=0;  
  50. volatile unsigned int vGu16BeepTimerCnt=0;  

  51. unsigned char Gu8RunStart=0;  //应用程序的总启动
  52. unsigned char Gu8RunStep=0;  //应用程序的总运行步骤。建议跟vGu8RunStart成双成对出现
  53. unsigned char Gu8RunStatus=0; //当前倒计时的状态。0代表停止,1代表正在工作中

  54. unsigned char Gu8WdUpdate=1;  //开机默认整屏更新一次显示。此变量在显示框架中是非常重要的变量

  55. volatile unsigned char vGu8Display_Righ_4=1;  //显示“1”,跟vGu32CountdownTimerCnt高位一致
  56. volatile unsigned char vGu8Display_Righ_3=0;  
  57. volatile unsigned char vGu8Display_Righ_2=0;  
  58. volatile unsigned char vGu8Display_Righ_1=0;  

  59. volatile unsigned char vGu8Display_Righ_Dot_4=0;  
  60. volatile unsigned char vGu8Display_Righ_Dot_3=1;    //开机默认保留显示2个小数点
  61. volatile unsigned char vGu8Display_Righ_Dot_2=0;  
  62. volatile unsigned char vGu8Display_Righ_Dot_1=0;  

  63. volatile unsigned char vGu8KeySec=0;  

  64. void main()
  65. {
  66. SystemInitial();            
  67. Delay(10000);               
  68. PeripheralInitial();      
  69.     while(1)  
  70. {  
  71. KeyTask();      //按键的任务函数
  72. DisplayTask();  //数码管显示的上层任务函数
  73. RunTask();      //倒计时的应用程序
  74.     }
  75. }

  76. void KeyTask(void)    //按键的任务函数
  77. {
  78. if(0==vGu8KeySec)
  79. {
  80. return;
  81. }

  82. switch(vGu8KeySec)
  83. {
  84.    case 1:     //复位按键

  85. Gu8RunStatus=0; //倒计时返回停止的状态

  86. Gu8RunStart=0;  //倒计时的运行步骤的停止
  87.             Gu8RunStep=0;  //总运行步骤归零。建议跟vGu8RunStart成双成对出现

  88. vGu8CountdownTimerFlag=0;    //禁止倒计时开始(而Gu8RunStart是启动开关,不矛盾)
  89. vGu32CountdownTimerCnt=10000;   //倒计时的软件定时器恢复初始值10.000秒

  90.             Gu8WdUpdate=1;  //整屏更新一次显示

  91. vGu8KeySec=0;  
  92. break;

  93.    case 2:     //启动的按键
  94.         if(0==Gu8RunStatus) //在停止状态下
  95.         {

  96. vGu8CountdownTimerFlag=0;
  97. vGu32CountdownTimerCnt=10000;  //初始值是10.000秒
  98. vGu8CountdownTimerFlag=1;      //允许倒计时的软件定时器的启动

  99.             Gu8RunStatus=1;  //倒计时处于工作状态(并且,这一瞬间才正式启动倒计时)

  100. Gu8RunStart=1;   //倒计时的运行步骤的总开关开启
  101.                 Gu8RunStep=0;    //总运行步骤归零。建议跟vGu8RunStart成双成对出现

  102.             Gu8WdUpdate=1;  //整屏更新一次显示,确保在启动的时候能显示到最新的数据
  103. }


  104. vGu8KeySec=0;
  105. break;

  106. }
  107. }

  108. void DisplayTask(void) //数码管显示的上层任务函数
  109. {
  110. //需要借用的中间变量,用来拆分数据位。
  111. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量

  112. if(1==Gu8WdUpdate) //如果需要整屏更新
  113. {
  114. Gu8WdUpdate=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  115.           //先分解数据,注意,这里分解的时候,“先整除后求余”必须用一行代码一气呵成,不能拆
  116.           //分成两行代码,否则会有隐患会有bug。除非,把四个临时变都改成unsigned long类型。

  117.           //Su8Temp_4提取“十秒”位。
  118. Su8Temp_4=vGu32CountdownTimerCnt/10000%10; //实际精度是0.001秒,但显示精度是0.01秒

  119.          //Su8Temp_3提取“个秒”位。
  120. Su8Temp_3=vGu32CountdownTimerCnt/1000%10; //实际精度是0.001秒,但显示精度是0.01秒

  121.          //Su8Temp_2提取“百毫秒”位。
  122. Su8Temp_2=vGu32CountdownTimerCnt/100%10; //实际精度是0.001秒,但显示精度是0.01秒

  123.           //Su8Temp_1提取“十毫秒”位。
  124. Su8Temp_1=vGu32CountdownTimerCnt/10%10; //实际精度是0.001秒,但显示精度是0.01秒

  125.           //判断数据范围,来决定最高位数码管是否需要显示。
  126.           if(vGu32CountdownTimerCnt<10000) //10.000秒。实际4位数码管最大只能显示99.99秒
  127.           {
  128.              Su8Temp_4=10;  //在数码管转换表里,10代表一个“不显示”的数据
  129. }

  130. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  131. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  132. vGu8Display_Righ_3=Su8Temp_3;  
  133. vGu8Display_Righ_2=Su8Temp_2;  
  134. vGu8Display_Righ_1=Su8Temp_1;  

  135. vGu8Display_Righ_Dot_4=0;  
  136. vGu8Display_Righ_Dot_3=1;   //保留显示2位小数点
  137. vGu8Display_Righ_Dot_2=0;  
  138. vGu8Display_Righ_Dot_1=0;  

  139. }
  140. }

  141. void RunTask(void)  //倒计时的应用程序
  142. {
  143. if(0==Gu8RunStart)
  144. {
  145.    return;  // 如果总开关处于停止状态,则直接退出当前函数,不执行该函数以下的其它代码
  146. }

  147. switch(Gu8RunStep)
  148. {
  149.     case 0:  //在这个步骤里,主要用来初始化一些参数

  150. vGu8UpdateTimerFlag=0;
  151. vGu16UpdateTimerCnt=10;  //每10ms更新显示一次当前倒计时的时间
  152. vGu8UpdateTimerFlag=1;

  153. Gu8RunStep=1; //跳转到每10ms更新显示一次的步骤里
  154. break;

  155.     case 1:  //每10ms更新一次显示,确保实时显示当前倒计时的时间
  156.        if(0==vGu16UpdateTimerCnt) //每10ms更新显示一次当前倒计时的时间
  157.        {


  158.     vGu8UpdateTimerFlag=0;
  159. vGu16UpdateTimerCnt=10;  //重置定时器,为下一个10ms更新做准备
  160. vGu8UpdateTimerFlag=1;

  161.             Gu8WdUpdate=1;  //整屏更新一次显示当前倒计时的时间

  162.             if(0==vGu32CountdownTimerCnt) //如果倒计时的时间到,则跳转到结束的步骤
  163.             {
  164. Gu8RunStep=2; //跳转到倒计时结束的步骤
  165. }

  166. }
  167. break;

  168. case 2:  //倒计时结束的步骤
  169. //Gu8RunStatus=0; //这行代码注释掉,让每次新启动之前都必须按一次K1复位按键才有效

  170. Gu8RunStart=0;  //倒计时的运行步骤的停止
  171.             Gu8RunStep=0;   //总运行步骤归零。建议跟vGu8RunStart成双成对出现

  172. vGu8BeepTimerFlag=0;  
  173. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  174. vGu8BeepTimerFlag=1;  

  175.         Gu8WdUpdate=1;  //整屏更新一次显示当前倒计时的时间

  176. break;

  177. }

  178. }


  179. void KeyScan(void)  //按键底层的驱动扫描函数,放在定时中断函数里
  180. {
  181.    static unsigned char Su8KeyLock1;
  182.    static unsigned int  Su16KeyCnt1;
  183.    static unsigned char Su8KeyLock2;
  184.    static unsigned int  Su16KeyCnt2;

  185.    if(0!=KEY_INPUT1)
  186.    {
  187.       Su8KeyLock1=0;
  188.       Su16KeyCnt1=0;   
  189.    }
  190.    else if(0==Su8KeyLock1)
  191.    {
  192.       Su16KeyCnt1++;
  193.       if(Su16KeyCnt1>=KEY_FILTER_TIME)
  194.       {
  195.          Su8KeyLock1=1;  
  196.          vGu8KeySec=1;   
  197.       }
  198.    }

  199.    if(0!=KEY_INPUT2)
  200.    {
  201.       Su8KeyLock2=0;
  202.       Su16KeyCnt2=0;      
  203.    }
  204.    else if(0==Su8KeyLock2)
  205.    {
  206.       Su16KeyCnt2++;
  207.       if(Su16KeyCnt2>=KEY_FILTER_TIME)
  208.       {
  209.          Su8KeyLock2=1;  
  210.          vGu8KeySec=2;   
  211.       }
  212.    }
  213. }

  214. void DisplayScan(void)    //数码管底层的驱动扫描函数,放在定时中断函数里
  215. {
  216. static unsigned char Su8GetCode;  
  217. static unsigned char Su8ScanStep=1;  

  218. if(0==vGu16ScanTimerCnt)  
  219. {


  220.     P0=0x00;
  221. P1_0=1;  
  222. P1_1=1;  
  223. P1_2=1;  
  224. P1_3=1;  

  225.     switch(Su8ScanStep)
  226. {
  227.        case 1:
  228. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  229. if(1==vGu8Display_Righ_Dot_1)
  230. {
  231. Su8GetCode=Su8GetCode|0x80;  
  232. }
  233.     P0=Su8GetCode;
  234. P1_0=0;
  235. P1_1=1;  
  236. P1_2=1;  
  237. P1_3=1;            
  238. break;

  239.        case 2:
  240. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  241. if(1==vGu8Display_Righ_Dot_2)
  242. {
  243. Su8GetCode=Su8GetCode|0x80;  
  244. }
  245.     P0=Su8GetCode;
  246. P1_0=1;  
  247. P1_1=0;
  248. P1_2=1;
  249. P1_3=1;         
  250. break;

  251.        case 3:
  252. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  253. if(1==vGu8Display_Righ_Dot_3)
  254. {
  255. Su8GetCode=Su8GetCode|0x80;  
  256. }
  257.     P0=Su8GetCode;
  258. P1_0=1;  
  259. P1_1=1;  
  260. P1_2=0;  
  261. P1_3=1;         
  262. break;

  263.        case 4:
  264. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  265. if(1==vGu8Display_Righ_Dot_4)
  266. {
  267. Su8GetCode=Su8GetCode|0x80;  
  268. }
  269.     P0=Su8GetCode;
  270. P1_0=1;  
  271. P1_1=1;  
  272. P1_2=1;  
  273. P1_3=0;           
  274. break;

  275. }

  276. Su8ScanStep++;
  277. if(Su8ScanStep>4)
  278. {
  279. Su8ScanStep=1;
  280. }

  281. vGu8ScanTimerFlag=0;
  282. vGu16ScanTimerCnt=SCAN_TIME;  
  283. vGu8ScanTimerFlag=1;  
  284. }
  285. }


  286. void VoiceScan(void) //蜂鸣器的驱动函数
  287. {

  288.           static unsigned char Su8Lock=0;  

  289. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  290.           {
  291.                   if(0==Su8Lock)
  292.                   {
  293.                    Su8Lock=1;  
  294. BeepOpen();
  295.      }
  296.     else  
  297. {     

  298.                        vGu16BeepTimerCnt--;         

  299.                    if(0==vGu16BeepTimerCnt)
  300.                    {
  301.                            Su8Lock=0;     
  302. BeepClose();  
  303.                    }

  304. }
  305.           }         
  306. }

  307. void BeepOpen(void)
  308. {
  309. P3_4=0;  
  310. }

  311. void BeepClose(void)
  312. {
  313. P3_4=1;  
  314. }

  315. void T0_time() interrupt 1     
  316. {
  317. VoiceScan();    //蜂鸣器的驱动函数
  318. KeyScan();      //按键底层的驱动扫描函数
  319. DisplayScan();  //数码管底层的驱动扫描函数

  320. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  321. {
  322. vGu16ScanTimerCnt--;  //递减式的软件定时器
  323. }

  324. //每10ms就定时更新一次显示的软件定时器
  325. if(1==vGu8UpdateTimerFlag&&vGu16UpdateTimerCnt>0)  
  326. {
  327. vGu16UpdateTimerCnt--;  //递减式的软件定时器
  328. }

  329. //倒计时实际走的时间的软件定时器,注意,这里还附加了启动状态的条件“&&1==Gu8RunStatus”
  330. if(1==vGu8CountdownTimerFlag&&vGu32CountdownTimerCnt>0&&1==Gu8RunStatus)
  331. {
  332. vGu32CountdownTimerCnt--;  //递减式的软件定时器
  333. }


  334. TH0=0xfc;   
  335. TL0=0x66;   
  336. }


  337. void SystemInitial(void)
  338. {
  339. P0=0x00;
  340. P1_0=1;  
  341. P1_1=1;  
  342. P1_2=1;  
  343. P1_3=1;  

  344. TMOD=0x01;  
  345. TH0=0xfc;   
  346. TL0=0x66;   
  347. EA=1;      
  348. ET0=1;      
  349. TR0=1;      
  350. }

  351. void Delay(unsigned long u32DelayTime)
  352. {
  353.     for(;u32DelayTime>0;u32DelayTime--);
  354. }

  355. void PeripheralInitial(void)
  356. {

  357. }


本帖子中包含更多资源

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

×
天命风流 发表于 2018-5-3 11:26 | 显示全部楼层
厉害啊
maglasbirl 发表于 2018-5-4 11:35 | 显示全部楼层
怎么收藏主题啊?
凡逸浩 发表于 2018-5-10 15:15 | 显示全部楼层
大概看了一下,还是蛮有用的
 楼主| jianhong_wu 发表于 2018-5-12 10:39 | 显示全部楼层
第一百一十七节: 按键切换数码管窗口来设置参数。

【117.1   按键切换数码管窗口来设置参数。】
     
                上图117.1.1  数码管




                上图117.1.2  独立按键

         
                上图117.1.3  有源蜂鸣器

        单片机是“数据”驱动型的。按什么逻辑跑,以什么方式跑,都是“数据”决定的。人机交互的核心就是“人”以什么渠道去更改“机”内部的某些“数据”。在程序框架层面,按键更改或者编辑某些数据,我的核心思路都是“在某个窗口下去更改某个特定的数据”,如果某个窗口的数据很多,就需要在此窗口下进一步细分,细分为“某个窗口下的某个局部(子菜单、光标选择)”。可见,“窗口”是支点,“局部”是支点中再细分出来的支点。窗口对应一个名叫 “窗口选择”的全局变量Gu8Wd,局部(子菜单、光标选择)对应一个名叫“局部选择”的全局变量Gu8Part。数据发生变化的时候,才需要更新显示到数码管上,平时不用一直更新显示,因此,与“窗口选择”Gu8Wd还对应一个名叫“整屏更新”的全局变量Gu8WdUpdate,与“局部选择”Gu8Part还对应一个名叫“局部更新”的全局变量Gu8PartUpdate。本节的小项目程序只用到“窗口”,没有用到“局部”。
        本节小项目的程序功能,利用按键与数码管的人机交互,可以对单片机内部三个参数Gu8SetData_1,Gu8SetData_2,Gu8SetData_3进行编辑。这三个参数分别在三个窗口下进行编辑,这三个窗口是数码管显示“1-XX”,“2-YY”,“3-ZZ”。其中,XX代表Gu8SetData_1数据,YY代表Gu8SetData_2数据,ZZ代表Gu8SetData_3数据,这三个数据的范围是从0到99。K1是窗口切换按键,每按一次,窗口会在“1-XX”,“2-YY”,“3-ZZ”三个窗口之间进行切换。K2是数字累加按键,每按一次,显示的数字会累加1。K3是数字递减按键,每按一次,显示的数字会递减1。代码如下:

  1. #include "REG52.H"  

  2. #define KEY_FILTER_TIME  25
  3. #define SCAN_TIME  1   
  4. #define VOICE_TIME   50   

  5. void T0_time();
  6. void SystemInitial(void) ;
  7. void Delay(unsigned long u32DelayTime) ;
  8. void PeripheralInitial(void) ;

  9. void KeyScan(void);  
  10. void KeyTask(void);  

  11. void VoiceScan(void);  
  12. void DisplayScan(void);  
  13. void DisplayTask(void);  //上层显示的任务函数
  14. void Wd1(void);   //窗口1显示函数
  15. void Wd2(void);   //窗口2显示函数
  16. void Wd3(void);   //窗口3显示函数

  17. void BeepOpen(void);   
  18. void BeepClose(void);

  19. sbit KEY_INPUT1=P2^2;  
  20. sbit KEY_INPUT2=P2^1;  
  21. sbit KEY_INPUT3=P2^0;  

  22. sbit P1_0=P1^0;  
  23. sbit P1_1=P1^1;  
  24. sbit P1_2=P1^2;  
  25. sbit P1_3=P1^3;

  26. sbit P3_4=P3^4;

  27. //数码管转换表
  28. code unsigned char Cu8DigTable[]=
  29. {
  30. 0x3f,  //0       序号0
  31. 0x06,  //1       序号1
  32. 0x5b,  //2       序号2
  33. 0x4f,  //3       序号3
  34. 0x66,  //4       序号4
  35. 0x6d,  //5       序号5
  36. 0x7d,  //6       序号6
  37. 0x07,  //7       序号7
  38. 0x7f,  //8       序号8
  39. 0x6f,  //9       序号9
  40. 0x00,  //不显示  序号10
  41. 0x40,  //横杠-   序号11
  42. };

  43. volatile unsigned char vGu8ScanTimerFlag=0;  
  44. volatile unsigned int vGu16ScanTimerCnt=0;  

  45. volatile unsigned char vGu8BeepTimerFlag=0;  
  46. volatile unsigned int vGu16BeepTimerCnt=0;  

  47. unsigned char Gu8SetData_1=0; //单片机内部第1个可编辑的参数
  48. unsigned char Gu8SetData_2=0; //单片机内部第2个可编辑的参数
  49. unsigned char Gu8SetData_3=0; //单片机内部第3个可编辑的参数

  50. unsigned char Gu8Wd=1;   //窗口选择变量。人机交互程序框架的支点。初始化开机后显示第1个窗口。
  51. unsigned char Gu8WdUpdate=1;  //整屏更新变量。初始化为1开机后整屏更新一次显示。

  52. volatile unsigned char vGu8Display_Righ_4=1;  //显示窗口“1”
  53. volatile unsigned char vGu8Display_Righ_3=11; //显示横杠“-”  
  54. volatile unsigned char vGu8Display_Righ_2=0;  //显示十位数值“0”
  55. volatile unsigned char vGu8Display_Righ_1=0;  //显示个位数值“0”

  56. volatile unsigned char vGu8Display_Righ_Dot_4=0;  
  57. volatile unsigned char vGu8Display_Righ_Dot_3=0;     
  58. volatile unsigned char vGu8Display_Righ_Dot_2=0;  
  59. volatile unsigned char vGu8Display_Righ_Dot_1=0;  

  60. volatile unsigned char vGu8KeySec=0;  

  61. void main()
  62. {
  63. SystemInitial();            
  64. Delay(10000);               
  65. PeripheralInitial();      
  66.     while(1)  
  67. {  
  68. KeyTask();      //按键的任务函数
  69. DisplayTask();  //数码管显示的上层任务函数
  70.     }
  71. }

  72. void KeyTask(void)    //按键的任务函数
  73. {
  74. if(0==vGu8KeySec)
  75. {
  76. return;
  77. }

  78. switch(vGu8KeySec)
  79. {
  80.    case 1:     //窗口切换的按键
  81. Gu8Wd++;  //窗口切换到下一个窗口
  82. if(Gu8Wd>3)  //一共3个窗口。切换第3个窗口之后,继续返回到第1个窗口
  83. {
  84. Gu8Wd=1;   //返回到第1个窗口
  85. }
  86.             Gu8WdUpdate=1;  //整屏更新一次显示

  87. vGu8BeepTimerFlag=0;  
  88. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  89. vGu8BeepTimerFlag=1;  

  90. vGu8KeySec=0;  
  91. break;

  92.    case 2:     //累加的按键
  93.         switch(Gu8Wd)  //以窗口选择Gu8Wd为支点,去编辑对应的数据。又一次用到switch语句
  94.         {
  95.              case 1:     //在第1个窗口下编辑Gu8SetData_1数据
  96.                   Gu8SetData_1++;
  97.                   if(Gu8SetData_1>99) //把最大范围限定在99
  98. {
  99. Gu8SetData_1=99;
  100. }
  101.                       Gu8WdUpdate=1;  //整屏更新一次显示
  102.                   break;

  103.              case 2:     //在第2个窗口下编辑Gu8SetData_2数据
  104.                   Gu8SetData_2++;
  105.                   if(Gu8SetData_2>99) //把最大范围限定在99
  106. {
  107. Gu8SetData_2=99;
  108. }
  109.                       Gu8WdUpdate=1;  //整屏更新一次显示
  110.                   break;

  111.              case 3:     //在第3个窗口下编辑Gu8SetData_3数据
  112.                   Gu8SetData_3++;
  113.                   if(Gu8SetData_3>99) //把最大范围限定在99
  114. {
  115. Gu8SetData_3=99;
  116. }
  117.                       Gu8WdUpdate=1;  //整屏更新一次显示
  118.                   break;
  119. }

  120. vGu8BeepTimerFlag=0;  
  121. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  122. vGu8BeepTimerFlag=1;  

  123. vGu8KeySec=0;  
  124. break;

  125.    case 3:     //递减的按键
  126.         switch(Gu8Wd)  //以窗口选择Gu8Wd为支点,去编辑对应的数据。又一次用到switch语句
  127.         {
  128.              case 1:     //在第1个窗口下编辑Gu8SetData_1数据
  129.                   if(Gu8SetData_1>0) //把最小范围限定在0
  130. {
  131. Gu8SetData_1--;
  132. }
  133.                       Gu8WdUpdate=1;  //整屏更新一次显示
  134.                   break;

  135.              case 2:     //在第2个窗口下编辑Gu8SetData_2数据
  136.                   if(Gu8SetData_2>0) //把最小范围限定在0
  137. {
  138. Gu8SetData_2--;
  139. }
  140.                       Gu8WdUpdate=1;  //整屏更新一次显示
  141.                   break;

  142.              case 3:     //在第3个窗口下编辑Gu8SetData_3数据
  143.                   if(Gu8SetData_3>0) //把最小范围限定在0
  144. {
  145. Gu8SetData_3--;
  146. }
  147.                       Gu8WdUpdate=1;  //整屏更新一次显示
  148.                   break;
  149. }

  150. vGu8BeepTimerFlag=0;  
  151. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  152. vGu8BeepTimerFlag=1;  

  153. vGu8KeySec=0;  
  154. break;
  155. }
  156. }

  157. void DisplayTask(void) //数码管显示的上层任务函数
  158. {
  159.   switch(Gu8Wd)  //以窗口选择Gu8Wd为支点,去执行对应的窗口显示函数。又一次用到switch语句
  160. {
  161.     case 1:
  162.         Wd1();   //窗口1显示函数
  163.         break;
  164.     case 2:
  165.         Wd2();   //窗口2显示函数
  166.         break;
  167.     case 3:
  168.         Wd3();   //窗口3显示函数
  169.         break;
  170. }

  171. }

  172. void Wd1(void)   //窗口1显示函数
  173. {
  174. //需要借用的中间变量,用来拆分数据位。
  175. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量

  176. if(1==Gu8WdUpdate) //如果需要整屏更新
  177. {
  178. Gu8WdUpdate=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  179. Su8Temp_4=1;  //窗口“1”
  180. Su8Temp_3=11; //横杠“-”
  181. Su8Temp_2=Gu8SetData_1/10%10; //十位数值
  182. Su8Temp_1=Gu8SetData_1/1%10;  //个位数值

  183. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  184. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  185. vGu8Display_Righ_3=Su8Temp_3;  
  186. vGu8Display_Righ_2=Su8Temp_2;  
  187. vGu8Display_Righ_1=Su8Temp_1;  

  188. //不显示任何一个小数点
  189. vGu8Display_Righ_Dot_4=0;  
  190. vGu8Display_Righ_Dot_3=0;  
  191. vGu8Display_Righ_Dot_2=0;  
  192. vGu8Display_Righ_Dot_1=0;  
  193. }
  194. }

  195. void Wd2(void)   //窗口2显示函数
  196. {
  197. //需要借用的中间变量,用来拆分数据位。
  198. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量

  199. if(1==Gu8WdUpdate) //如果需要整屏更新
  200. {
  201. Gu8WdUpdate=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  202. Su8Temp_4=2;  //窗口“2”
  203. Su8Temp_3=11; //横杠“-”
  204. Su8Temp_2=Gu8SetData_2/10%10; //十位数值
  205. Su8Temp_1=Gu8SetData_2/1%10;  //个位数值

  206. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  207. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  208. vGu8Display_Righ_3=Su8Temp_3;  
  209. vGu8Display_Righ_2=Su8Temp_2;  
  210. vGu8Display_Righ_1=Su8Temp_1;  

  211. //不显示任何一个小数点
  212. vGu8Display_Righ_Dot_4=0;  
  213. vGu8Display_Righ_Dot_3=0;  
  214. vGu8Display_Righ_Dot_2=0;  
  215. vGu8Display_Righ_Dot_1=0;  
  216. }
  217. }

  218. void Wd3(void)   //窗口3显示函数
  219. {
  220. //需要借用的中间变量,用来拆分数据位。
  221. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量

  222. if(1==Gu8WdUpdate) //如果需要整屏更新
  223. {
  224. Gu8WdUpdate=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  225. Su8Temp_4=3;  //窗口“3”
  226. Su8Temp_3=11; //横杠“-”
  227. Su8Temp_2=Gu8SetData_3/10%10; //十位数值
  228. Su8Temp_1=Gu8SetData_3/1%10;  //个位数值

  229. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  230. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  231. vGu8Display_Righ_3=Su8Temp_3;  
  232. vGu8Display_Righ_2=Su8Temp_2;  
  233. vGu8Display_Righ_1=Su8Temp_1;  

  234. //不显示任何一个小数点
  235. vGu8Display_Righ_Dot_4=0;  
  236. vGu8Display_Righ_Dot_3=0;  
  237. vGu8Display_Righ_Dot_2=0;  
  238. vGu8Display_Righ_Dot_1=0;  
  239. }
  240. }

  241. void KeyScan(void)  //按键底层的驱动扫描函数,放在定时中断函数里
  242. {
  243.    static unsigned char Su8KeyLock1;
  244.    static unsigned int  Su16KeyCnt1;
  245.    static unsigned char Su8KeyLock2;
  246.    static unsigned int  Su16KeyCnt2;
  247.     static unsigned char Su8KeyLock3;
  248.    static unsigned int  Su16KeyCnt3;

  249.    if(0!=KEY_INPUT1)
  250.    {
  251.       Su8KeyLock1=0;
  252.       Su16KeyCnt1=0;   
  253.    }
  254.    else if(0==Su8KeyLock1)
  255.    {
  256.       Su16KeyCnt1++;
  257.       if(Su16KeyCnt1>=KEY_FILTER_TIME)
  258.       {
  259.          Su8KeyLock1=1;  
  260.          vGu8KeySec=1;   
  261.       }
  262.    }

  263.    if(0!=KEY_INPUT2)
  264.    {
  265.       Su8KeyLock2=0;
  266.       Su16KeyCnt2=0;      
  267.    }
  268.    else if(0==Su8KeyLock2)
  269.    {
  270.       Su16KeyCnt2++;
  271.       if(Su16KeyCnt2>=KEY_FILTER_TIME)
  272.       {
  273.          Su8KeyLock2=1;  
  274.          vGu8KeySec=2;   
  275.       }
  276.    }

  277.    if(0!=KEY_INPUT3)
  278.    {
  279.       Su8KeyLock3=0;
  280.       Su16KeyCnt3=0;      
  281.    }
  282.    else if(0==Su8KeyLock3)
  283.    {
  284.       Su16KeyCnt3++;
  285.       if(Su16KeyCnt3>=KEY_FILTER_TIME)
  286.       {
  287.          Su8KeyLock3=1;  
  288.          vGu8KeySec=3;   
  289.       }
  290.    }

  291. }

  292. void DisplayScan(void)    //数码管底层的驱动扫描函数,放在定时中断函数里
  293. {
  294. static unsigned char Su8GetCode;  
  295. static unsigned char Su8ScanStep=1;  

  296. if(0==vGu16ScanTimerCnt)  
  297. {


  298.     P0=0x00;
  299. P1_0=1;  
  300. P1_1=1;  
  301. P1_2=1;  
  302. P1_3=1;  

  303.     switch(Su8ScanStep)
  304. {
  305.        case 1:
  306. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  307. if(1==vGu8Display_Righ_Dot_1)
  308. {
  309. Su8GetCode=Su8GetCode|0x80;  
  310. }
  311.     P0=Su8GetCode;
  312. P1_0=0;
  313. P1_1=1;  
  314. P1_2=1;  
  315. P1_3=1;            
  316. break;

  317.        case 2:
  318. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  319. if(1==vGu8Display_Righ_Dot_2)
  320. {
  321. Su8GetCode=Su8GetCode|0x80;  
  322. }
  323.     P0=Su8GetCode;
  324. P1_0=1;  
  325. P1_1=0;
  326. P1_2=1;
  327. P1_3=1;         
  328. break;

  329.        case 3:
  330. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  331. if(1==vGu8Display_Righ_Dot_3)
  332. {
  333. Su8GetCode=Su8GetCode|0x80;  
  334. }
  335.     P0=Su8GetCode;
  336. P1_0=1;  
  337. P1_1=1;  
  338. P1_2=0;  
  339. P1_3=1;         
  340. break;

  341.        case 4:
  342. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  343. if(1==vGu8Display_Righ_Dot_4)
  344. {
  345. Su8GetCode=Su8GetCode|0x80;  
  346. }
  347.     P0=Su8GetCode;
  348. P1_0=1;  
  349. P1_1=1;  
  350. P1_2=1;  
  351. P1_3=0;           
  352. break;

  353. }

  354. Su8ScanStep++;
  355. if(Su8ScanStep>4)
  356. {
  357. Su8ScanStep=1;
  358. }

  359. vGu8ScanTimerFlag=0;
  360. vGu16ScanTimerCnt=SCAN_TIME;  
  361. vGu8ScanTimerFlag=1;  
  362. }
  363. }


  364. void VoiceScan(void) //蜂鸣器的驱动函数
  365. {

  366.           static unsigned char Su8Lock=0;  

  367. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  368.           {
  369.                   if(0==Su8Lock)
  370.                   {
  371.                    Su8Lock=1;  
  372. BeepOpen();
  373.      }
  374.     else  
  375. {     

  376.                        vGu16BeepTimerCnt--;         

  377.                    if(0==vGu16BeepTimerCnt)
  378.                    {
  379.                            Su8Lock=0;     
  380. BeepClose();  
  381.                    }

  382. }
  383.           }         
  384. }

  385. void BeepOpen(void)
  386. {
  387. P3_4=0;  
  388. }

  389. void BeepClose(void)
  390. {
  391. P3_4=1;  
  392. }

  393. void T0_time() interrupt 1     
  394. {
  395. VoiceScan();    //蜂鸣器的驱动函数
  396. KeyScan();      //按键底层的驱动扫描函数
  397. DisplayScan();  //数码管底层的驱动扫描函数

  398. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  399. {
  400. vGu16ScanTimerCnt--;  //递减式的软件定时器
  401. }

  402. TH0=0xfc;   
  403. TL0=0x66;   
  404. }


  405. void SystemInitial(void)
  406. {
  407. P0=0x00;
  408. P1_0=1;  
  409. P1_1=1;  
  410. P1_2=1;  
  411. P1_3=1;  

  412. TMOD=0x01;  
  413. TH0=0xfc;   
  414. TL0=0x66;   
  415. EA=1;      
  416. ET0=1;      
  417. TR0=1;      
  418. }

  419. void Delay(unsigned long u32DelayTime)
  420. {
  421.     for(;u32DelayTime>0;u32DelayTime--);
  422. }

  423. void PeripheralInitial(void)
  424. {

  425. }


本帖子中包含更多资源

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

×
tanik 发表于 2018-5-13 21:46 | 显示全部楼层
走到哪里,都要顶!
波哥有气质 发表于 2018-5-15 16:09 | 显示全部楼层
jianhong_wu 发表于 2017-10-2 11:54
第八十九节: 跑马灯的三种境界。

【89.1   跑马灯的三种境界。】

学到了很多,感谢您的无私奉献!!
zxf6641 发表于 2018-5-21 20:27 来自手机 | 显示全部楼层
感谢楼主无私奉献科普知识,国家需要的就是这踏实做事的人才!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部