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

[复制链接]
楼主: jianhong_wu
| 2018-5-13 21:46 | 显示全部楼层
走到哪里,都要顶!
| 2018-5-15 16:09 | 显示全部楼层
jianhong_wu 发表于 2017-10-2 11:54
第八十九节: 跑马灯的三种境界。

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

学到了很多,感谢您的无私奉献!!
| 2018-5-21 20:27 | 显示全部楼层
感谢楼主无私奉献科普知识,国家需要的就是这踏实做事的人才!
| 2018-5-21 22:16 | 显示全部楼层
楼主,50岁的我跟你学单片机
| 2018-5-22 19:16 | 显示全部楼层
这个按键的局部变量为什么一定要加static
 楼主 | 2018-5-29 09:55 | 显示全部楼层
第一百一十八节: 按键让某位数码管闪烁跳动来设置参数。

【118.1   按键让某位数码管闪烁跳动来设置参数。】
   
                上图118.1.1  数码管




                上图118.1.2  独立按键

         
                上图118.1.3  有源蜂鸣器

       当一个窗口只有一个数据的时候,只需以“窗口”为支点,切换到某个窗口下去设置某个数据即可。但是,当某个窗口有几个数据时,就必须在以“窗口”为支点的前提下,再细分出一个二级的支点,这个二级支点就是“局部”(或者称为子菜单)。“窗口”对应一个“窗口选择”的全局变量Gu8Wd,“局部”对应一个“局部选择”的全局变量Gu8Part。数据需要更新显示输出到屏幕(数码管)时,有两种更新方式,一种是“整屏更新”,另一种是“局部更新”。“整屏更新”只有一个整屏的更新变量Gu8WdUpdate,而“局部更新”有N个更新变量Gu8PartUpdate_x(Gu8PartUpdate_1,Gu8PartUpdate_2,Gu8PartUpdate_3),一个窗口下有多少个数据就存在多少个局部的更新变量Gu8PartUpdate_x,这些局部的更新变量在不同的窗口下是可以共用的。当某个局部被选中的时候,可以有很多种表现方式,比如在液晶屏上,常见的有光标跳动,某行文字的底色变色(反显),本节例程用的数码管,当某个局部被选中的时候,用某位数码管闪烁跳动的方式。
       本节小项目的程序功能,在一个窗口下,对单片机内部四个参数Gu8SetData_4,Gu8SetData_3,Gu8SetData_2,Gu8SetData_1进行编辑。这四个参数的范围是从0到9,从左到右分别显示在四位数码管上,每一位数码管对应一个数据。比如左起第1位是Gu8SetData_4,左起第2位是Gu8SetData_3,左起第3位是Gu8SetData_2,左起第4位是Gu8SetData_1。K1是局部选择的切换按键,每按一次,数码管从左到右,依次闪烁跳动,表示某个数据被选中。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. #define BLINK_TIME   250    //数码管闪烁跳动的时间的间隔


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

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

  12. void VoiceScan(void);  
  13. void DisplayScan(void);  
  14. void DisplayTask(void);  //上层显示的任务函数
  15. void Wd1(void);   //窗口1显示函数
  16. void PartUpdate(unsigned char u8Part);  //局部选择对应的某个局部变量更新显示输出

  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. volatile unsigned char vGu8BlinkTimerFlag=0;   //数码管闪烁跳动的定时器
  48. volatile unsigned int vGu16BlinkTimerCnt=0;  

  49. unsigned char Gu8SetData_4=0; //单片机内部第4个可编辑的参数
  50. unsigned char Gu8SetData_3=0; //单片机内部第3个可编辑的参数
  51. unsigned char Gu8SetData_2=0; //单片机内部第2个可编辑的参数
  52. unsigned char Gu8SetData_1=0; //单片机内部第1个可编辑的参数

  53. unsigned char Gu8Wd=1;   //窗口选择变量。人机交互程序框架的支点。初始化开机后显示第1个窗口。
  54. unsigned char Gu8WdUpdate=1;  //整屏更新变量。初始化为1开机后整屏更新一次显示。
  55. unsigned char Gu8Part=0;   //局部选择变量。0代表当前窗口下没有数据被选中。
  56. unsigned char Gu8PartUpdate_1=0;   //局部1的更新变量,
  57. unsigned char Gu8PartUpdate_2=0;   //局部2的更新变量
  58. unsigned char Gu8PartUpdate_3=0;   //局部3的更新变量
  59. unsigned char Gu8PartUpdate_4=0;   //局部4的更新变量


  60. volatile unsigned char vGu8Display_Righ_4=0;   //左起第1位初始化显示数值“0”
  61. volatile unsigned char vGu8Display_Righ_3=0;   //左起第2位初始化显示数值“0”
  62. volatile unsigned char vGu8Display_Righ_2=0;   //左起第3位初始化显示数值“0”
  63. volatile unsigned char vGu8Display_Righ_1=0;   //左起第4位初始化显示数值“0”

  64. volatile unsigned char vGu8Display_Righ_Dot_4=0;  
  65. volatile unsigned char vGu8Display_Righ_Dot_3=0;     
  66. volatile unsigned char vGu8Display_Righ_Dot_2=0;  
  67. volatile unsigned char vGu8Display_Righ_Dot_1=0;  

  68. volatile unsigned char vGu8KeySec=0;  

  69. void main()
  70. {
  71. SystemInitial();            
  72. Delay(10000);               
  73. PeripheralInitial();      
  74.     while(1)  
  75. {  
  76. KeyTask();      //按键的任务函数
  77. DisplayTask();  //数码管显示的上层任务函数
  78.     }
  79. }

  80. void PartUpdate(unsigned char u8Part)  //局部选择对应的某个局部变量更新显示输出
  81. {
  82.     switch(u8Part)
  83. {
  84.    case 1:
  85. Gu8PartUpdate_1=1;
  86. break;
  87.    case 2:
  88. Gu8PartUpdate_2=1;
  89. break;
  90.    case 3:
  91. Gu8PartUpdate_3=1;
  92. break;
  93.    case 4:
  94. Gu8PartUpdate_4=1;
  95. break;
  96. }

  97. }

  98. void KeyTask(void)    //按键的任务函数
  99. {
  100. if(0==vGu8KeySec)
  101. {
  102. return;
  103. }

  104. switch(vGu8KeySec)
  105. {
  106.    case 1:     //局部切换的按键
  107.         switch(Gu8Wd) //在某个窗口下
  108.         {
  109.            case 1:     //在窗口1下
  110.                 //以下之所以有两个PartUpdate(Gu8Part),是因为相邻的两个局部发生了变化。

  111.                 PartUpdate(Gu8Part);  //切换之前的局部进行更新。
  112.                 Gu8Part++;  //切换到下一个局部
  113.                 if(Gu8Part>4)
  114.                 {
  115. Gu8Part=0;
  116. }
  117.                 PartUpdate(Gu8Part);  //切换之后的局部进行更新。
  118.                 break;
  119. }

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

  123. vGu8KeySec=0;  
  124. break;

  125.    case 2:     //累加的按键
  126.         switch(Gu8Wd) //在某个窗口下
  127.         {
  128.            case 1:     //在窗口1下
  129.                 switch(Gu8Part)  //二级支点的局部选择
  130.                 {
  131.                     case 1:  //局部1被选中,代表左起第1位数据Gu8SetData_4被选中。
  132. Gu8SetData_4++;
  133. if(Gu8SetData_4>9)
  134. {
  135. Gu8SetData_4=9;
  136. }
  137. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  138.                           break;

  139.                     case 2:  //局部2被选中,代表左起第2位数据Gu8SetData_3被选中。
  140. Gu8SetData_3++;
  141. if(Gu8SetData_3>9)
  142. {
  143. Gu8SetData_3=9;
  144. }
  145. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  146.                           break;

  147.                     case 3:  //局部3被选中,代表左起第3位数据Gu8SetData_2被选中。
  148. Gu8SetData_2++;
  149. if(Gu8SetData_2>9)
  150. {
  151. Gu8SetData_2=9;
  152. }
  153. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  154.                           break;

  155.                     case 4:  //局部4被选中,代表左起第4位数据Gu8SetData_1被选中。
  156. Gu8SetData_1++;
  157. if(Gu8SetData_1>9)
  158. {
  159. Gu8SetData_1=9;
  160. }
  161. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  162.                           break;

  163. }
  164. break;

  165.            case 2:     //在窗口2下(本节只用到窗口1)
  166.                     break;
  167. }

  168. vGu8BeepTimerFlag=0;  
  169. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  170. vGu8BeepTimerFlag=1;  

  171. vGu8KeySec=0;  
  172. break;

  173.    case 3:     //递减的按键
  174.         switch(Gu8Wd) //在某个窗口下
  175.         {
  176.            case 1:     //在窗口1下
  177.                 switch(Gu8Part)  //二级支点的局部选择
  178.                 {
  179.                     case 1:  //局部1被选中,代表左起第1位数据Gu8SetData_4被选中。
  180. if(Gu8SetData_4>0)
  181. {
  182. Gu8SetData_4--;
  183. }
  184. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  185.                           break;

  186.                     case 2:  //局部2被选中,代表左起第2位数据Gu8SetData_3被选中。
  187. if(Gu8SetData_3>0)
  188. {
  189. Gu8SetData_3--;
  190. }
  191. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  192.                           break;

  193.                     case 3:  //局部3被选中,代表左起第3位数据Gu8SetData_2被选中。
  194. if(Gu8SetData_2>0)
  195. {
  196. Gu8SetData_2--;
  197. }
  198. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  199.                           break;

  200.                     case 4:  //局部4被选中,代表左起第4位数据Gu8SetData_1被选中。
  201. if(Gu8SetData_1>0)
  202. {
  203. Gu8SetData_1--;
  204. }
  205. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  206.                           break;

  207. }
  208. break;

  209.            case 2:     //在窗口2下(本节只用到窗口1)
  210.                     break;
  211. }

  212. vGu8BeepTimerFlag=0;  
  213. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  214. vGu8BeepTimerFlag=1;  

  215. vGu8KeySec=0;  
  216. break;
  217. }
  218. }

  219. void DisplayTask(void) //数码管显示的上层任务函数
  220. {
  221.   switch(Gu8Wd)  //以窗口选择Gu8Wd为支点,去执行对应的窗口显示函数。又一次用到switch语句
  222. {
  223.     case 1:
  224.         Wd1();   //窗口1显示函数
  225.         break;
  226.     case 2:      //窗口2显示选择(本节只用到窗口1)
  227.         break;
  228. }

  229. }

  230. void Wd1(void)   //窗口1显示函数
  231. {
  232. //需要借用的中间变量,用来拆分数据位。
  233. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  234. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量


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

  238. //不显示任何一个小数点
  239. vGu8Display_Righ_Dot_4=0;  
  240. vGu8Display_Righ_Dot_3=0;  
  241. vGu8Display_Righ_Dot_2=0;  
  242. vGu8Display_Righ_Dot_1=0;  

  243. Gu8PartUpdate_1=1;  //局部1更新显示
  244. Gu8PartUpdate_2=1  ;//局部2更新显示
  245. Gu8PartUpdate_3=1;  //局部3更新显示
  246. Gu8PartUpdate_4=1;  //局部4更新显示

  247. }

  248. if(1==Gu8PartUpdate_1) //局部1更新显示
  249. {
  250. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  251. Su8Temp_4=Gu8SetData_4;  //显示左起第1个数据Gu8SetData_4

  252. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  253. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  254. }

  255. if(1==Gu8PartUpdate_2) //局部2更新显示
  256. {
  257. Gu8PartUpdate_2=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  258. Su8Temp_3=Gu8SetData_3;  //显示左起第2个数据Gu8SetData_3

  259. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  260. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量
  261. }

  262. if(1==Gu8PartUpdate_3) //局部3更新显示
  263. {
  264. Gu8PartUpdate_3=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  265. Su8Temp_2=Gu8SetData_2;  //显示左起第3个数据Gu8SetData_2

  266. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  267. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  268. }

  269. if(1==Gu8PartUpdate_4) //局部4更新显示
  270. {
  271. Gu8PartUpdate_4=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  272. Su8Temp_1=Gu8SetData_1;  //显示左起第4个数据Gu8SetData_1

  273. //上面先分解数据之后,再过渡需要显示的数据到底层驱动变量里,让过渡的时间越短越好
  274. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  275. }

  276. if(0==vGu16BlinkTimerCnt)  //某位被选中的数码管跳动闪烁的定时器
  277. {
  278. vGu8BlinkTimerFlag=0;
  279.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  280. vGu8BlinkTimerFlag=1;

  281.     switch(Gu8Part)  //某个局部被选中,则闪烁跳动
  282.     {
  283.         case 1:
  284.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  285. {
  286. Su8BlinkFlag=1;
  287. Su8Temp_4=10;  //左起第1个显示“不显示”(10代表不显示)
  288. }
  289. else
  290. {
  291. Su8BlinkFlag=0;
  292.               Su8Temp_4=Gu8SetData_4;  //左起第1个显示数据Gu8SetData_4
  293. }

  294.              break;

  295.         case 2:
  296.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  297. {
  298. Su8BlinkFlag=1;
  299. Su8Temp_3=10;  //左起第2个显示“不显示”(10代表不显示)
  300. }
  301. else
  302. {
  303. Su8BlinkFlag=0;
  304.               Su8Temp_3=Gu8SetData_3;  //左起第2个显示数据Gu8SetData_3
  305. }

  306.              break;

  307.         case 3:
  308.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  309. {
  310. Su8BlinkFlag=1;
  311. Su8Temp_2=10;  //左起第3个显示“不显示”(10代表不显示)
  312. }
  313. else
  314. {
  315. Su8BlinkFlag=0;
  316.               Su8Temp_2=Gu8SetData_2;  //左起第3个显示数据Gu8SetData_2
  317. }

  318.              break;

  319.         case 4:
  320.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  321. {
  322. Su8BlinkFlag=1;
  323. Su8Temp_1=10;  //左起第3个显示“不显示”(10代表不显示)
  324. }
  325. else
  326. {
  327. Su8BlinkFlag=0;
  328.               Su8Temp_1=Gu8SetData_1;  //左起第4个显示数据Gu8SetData_1
  329. }

  330.              break;
  331.         default:   //都没有被选中的时候
  332.        Su8Temp_4=Gu8SetData_4;  //左起第1个显示数据Gu8SetData_4
  333.        Su8Temp_3=Gu8SetData_3;  //左起第2个显示数据Gu8SetData_3
  334.        Su8Temp_2=Gu8SetData_2;  //左起第3个显示数据Gu8SetData_2
  335.        Su8Temp_1=Gu8SetData_1;  //左起第4个显示数据Gu8SetData_1
  336.              break;
  337. }

  338. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  339. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量
  340. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  341. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量


  342. }

  343. }


  344. void KeyScan(void)  //按键底层的驱动扫描函数,放在定时中断函数里
  345. {
  346.    static unsigned char Su8KeyLock1;
  347.    static unsigned int  Su16KeyCnt1;
  348.    static unsigned char Su8KeyLock2;
  349.    static unsigned int  Su16KeyCnt2;
  350.     static unsigned char Su8KeyLock3;
  351.    static unsigned int  Su16KeyCnt3;

  352.    if(0!=KEY_INPUT1)
  353.    {
  354.       Su8KeyLock1=0;
  355.       Su16KeyCnt1=0;   
  356.    }
  357.    else if(0==Su8KeyLock1)
  358.    {
  359.       Su16KeyCnt1++;
  360.       if(Su16KeyCnt1>=KEY_FILTER_TIME)
  361.       {
  362.          Su8KeyLock1=1;  
  363.          vGu8KeySec=1;   
  364.       }
  365.    }

  366.    if(0!=KEY_INPUT2)
  367.    {
  368.       Su8KeyLock2=0;
  369.       Su16KeyCnt2=0;      
  370.    }
  371.    else if(0==Su8KeyLock2)
  372.    {
  373.       Su16KeyCnt2++;
  374.       if(Su16KeyCnt2>=KEY_FILTER_TIME)
  375.       {
  376.          Su8KeyLock2=1;  
  377.          vGu8KeySec=2;   
  378.       }
  379.    }

  380.    if(0!=KEY_INPUT3)
  381.    {
  382.       Su8KeyLock3=0;
  383.       Su16KeyCnt3=0;      
  384.    }
  385.    else if(0==Su8KeyLock3)
  386.    {
  387.       Su16KeyCnt3++;
  388.       if(Su16KeyCnt3>=KEY_FILTER_TIME)
  389.       {
  390.          Su8KeyLock3=1;  
  391.          vGu8KeySec=3;   
  392.       }
  393.    }

  394. }

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

  399. if(0==vGu16ScanTimerCnt)  
  400. {


  401.     P0=0x00;
  402. P1_0=1;  
  403. P1_1=1;  
  404. P1_2=1;  
  405. P1_3=1;  

  406.     switch(Su8ScanStep)
  407. {
  408.        case 1:
  409. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  410. if(1==vGu8Display_Righ_Dot_1)
  411. {
  412. Su8GetCode=Su8GetCode|0x80;  
  413. }
  414.     P0=Su8GetCode;
  415. P1_0=0;
  416. P1_1=1;  
  417. P1_2=1;  
  418. P1_3=1;            
  419. break;

  420.        case 2:
  421. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  422. if(1==vGu8Display_Righ_Dot_2)
  423. {
  424. Su8GetCode=Su8GetCode|0x80;  
  425. }
  426.     P0=Su8GetCode;
  427. P1_0=1;  
  428. P1_1=0;
  429. P1_2=1;
  430. P1_3=1;         
  431. break;

  432.        case 3:
  433. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  434. if(1==vGu8Display_Righ_Dot_3)
  435. {
  436. Su8GetCode=Su8GetCode|0x80;  
  437. }
  438.     P0=Su8GetCode;
  439. P1_0=1;  
  440. P1_1=1;  
  441. P1_2=0;  
  442. P1_3=1;         
  443. break;

  444.        case 4:
  445. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  446. if(1==vGu8Display_Righ_Dot_4)
  447. {
  448. Su8GetCode=Su8GetCode|0x80;  
  449. }
  450.     P0=Su8GetCode;
  451. P1_0=1;  
  452. P1_1=1;  
  453. P1_2=1;  
  454. P1_3=0;           
  455. break;

  456. }

  457. Su8ScanStep++;
  458. if(Su8ScanStep>4)
  459. {
  460. Su8ScanStep=1;
  461. }

  462. vGu8ScanTimerFlag=0;
  463. vGu16ScanTimerCnt=SCAN_TIME;  
  464. vGu8ScanTimerFlag=1;  
  465. }
  466. }


  467. void VoiceScan(void) //蜂鸣器的驱动函数
  468. {

  469.           static unsigned char Su8Lock=0;  

  470. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  471.           {
  472.                   if(0==Su8Lock)
  473.                   {
  474.                    Su8Lock=1;  
  475. BeepOpen();
  476.      }
  477.     else  
  478. {     

  479.                        vGu16BeepTimerCnt--;         

  480.                    if(0==vGu16BeepTimerCnt)
  481.                    {
  482.                            Su8Lock=0;     
  483. BeepClose();  
  484.                    }

  485. }
  486.           }         
  487. }

  488. void BeepOpen(void)
  489. {
  490. P3_4=0;  
  491. }

  492. void BeepClose(void)
  493. {
  494. P3_4=1;  
  495. }

  496. void T0_time() interrupt 1     
  497. {
  498. VoiceScan();    //蜂鸣器的驱动函数
  499. KeyScan();      //按键底层的驱动扫描函数
  500. DisplayScan();  //数码管底层的驱动扫描函数

  501. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  502. {
  503. vGu16ScanTimerCnt--;  //递减式的软件定时器
  504. }

  505. if(1==vGu8BlinkTimerFlag&&vGu16BlinkTimerCnt>0)   //数码管闪烁跳动的定时器
  506. {
  507. vGu16BlinkTimerCnt--;  //递减式的软件定时器
  508. }

  509. TH0=0xfc;   
  510. TL0=0x66;   
  511. }


  512. void SystemInitial(void)
  513. {
  514. P0=0x00;
  515. P1_0=1;  
  516. P1_1=1;  
  517. P1_2=1;  
  518. P1_3=1;  

  519. TMOD=0x01;  
  520. TH0=0xfc;   
  521. TL0=0x66;   
  522. EA=1;      
  523. ET0=1;      
  524. TR0=1;      
  525. }

  526. void Delay(unsigned long u32DelayTime)
  527. {
  528.     for(;u32DelayTime>0;u32DelayTime--);
  529. }

  530. void PeripheralInitial(void)
  531. {

  532. }
复制代码


本帖子中包含更多资源

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

x
| 2018-6-1 16:37 | 显示全部楼层
jianhong_wu 发表于 2017-10-9 10:02
第九十节: 多任务并行处理两路跑马灯。

【90.1   多任务并行处理。】

这个算多任务吗?
 楼主 | 2018-6-3 10:36 | 显示全部楼层
第一百一十九节: 一个完整的人机界面的程序框架的脉络。

【119.1   一个完整的人机界面的程序框架的脉络。】

       前面两节例子告诉我们,一个完整的人机界面的程序框架包含两个要素,分别是“支点”与“更新”。“支点”包括“窗口选择”和“局部选择”,“更新”包括“整屏更新”和“局部更新”。
       “支点”的作用是把显示函数与按键函数完美无缝的关联起来,两个函数同样的“支点”促使同样的“话语体系”,让“所见即所得”实时同步,确保按键操作的数据就是当前显示被选中的数据。
       “静态数据”与“动态数据”的概念。被窗口显示的数据通常有两种:一种是静态数据,比如装饰门面的数据,只能显示不能更改的数据,以及图片图标这类数据;另外一种是动态数据,这种数据在窗口显示上是活动的可编辑的,是需要经常修改的,往往也是系统核心的数据,需要保存或者需要跟某些关键运动密切相关的数据。比如,在前面章节中,数码管要显示三个窗口“1-XX”,“2-YY”,“3-ZZ”,其中“1-”、“2-”、“3-”是属于静态数据,它们是起“装饰”作用的。而“XX”、“YY”、“ZZ”则是动态数据,它们是可编辑的,也是单片机系统内部核心的数据。
       “整屏更新”与“局部更新”的分工。“整屏更新”主要负责在切换新窗口时,把“静态数据”一次性显示到当前窗口。而“局部更新”主要负责在当前窗口下显示“动态数据”。
       下面,我把一个完整的人机界面的程序框架的脉络勾勒出来,让大家有一个整体的观感,这种人机界面的程序框架放之四海而皆准,我已把它应用在各种数码管,单色液晶屏,彩屏,电脑上位机等项目上。假设某个项目中只有两个”窗口”只有两个“局部”,程序框架的脉络如下:

       显示部分:

  1. void DisplayTask(void) //数码管显示的上层任务函数
  2. {
  3.   switch(Gu8Wd)  //以“窗口选择”Gu8Wd为支点
  4. {
  5.     case 1:
  6.         Wd1();   //窗口1显示函数
  7.         break;
  8.     case 2:     
  9.         Wd2();   //窗口2显示函数
  10.         break;
  11. }
  12. }


  13. void Wd1(void)   //窗口1显示函数
  14. {
  15. if(1==Gu8WdUpdate) //整屏更新
  16. {
  17. Gu8WdUpdate=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  18.           ......    //此处省略N行代码,用来显示静态的数据,比如图片图标,或者装饰的数据


  19. //以下,“整屏更新”必然是要把所有的“局部更新”都触发一次
  20. Gu8PartUpdate_1=1;  //局部1更新显示
  21. Gu8PartUpdate_2=1  ;//局部2更新显示
  22. }

  23. if(1==Gu8PartUpdate_1) //局部1更新显示
  24. {
  25. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  26.           ......    //此处省略N行代码,用来显示动态的数据。比如可编辑的数据,实时变化的数据

  27. }

  28. if(1==Gu8PartUpdate_2) //局部2更新显示
  29. {
  30. Gu8PartUpdate_2=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  31.           ......    //此处省略N行代码,用来显示动态的数据。比如可编辑的数据,实时变化的数据

  32. }

  33. if(0==vGu16BlinkTimerCnt)  //跳动的光标,或者动态闪烁的某位被选中的数据
  34. {
  35. vGu8BlinkTimerFlag=0;
  36.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  37. vGu8BlinkTimerFlag=1;

  38.         ......    //此处省略N行代码,用来制作跳动的光标或者某位被选中而闪烁的数据

  39. }

  40. }


  41. void Wd2(void)   //窗口2显示函数
  42. {
  43.           ......    //此处省略N行代码,窗口2显示函数的代码跟窗口1类似
  44. }
复制代码



       按键部分:

  1. void KeyTask(void)    //按键的任务函数
  2. {
  3. if(0==vGu8KeySec)
  4. {
  5. return;
  6. }

  7. switch(vGu8KeySec)
  8. {
  9.    case 1:     //1号按键
  10.         switch(Gu8Wd) //以“窗口选择”Gu8Wd为支点
  11.         {
  12.            case 1:     //在窗口1下
  13.                 switch(Gu8Part)  //以“局部选择”Gu8Part为支点
  14.                 {
  15.                     case 1:  

  16.                           ......    //此处省略N行代码

  17.                           break;

  18.                     case 2:  //局部2被选中

  19.                           ......    //此处省略N行代码

  20.                           break;
  21. }
  22. break;

  23.            case 2:     //在窗口2下
  24.                 switch(Gu8Part)  //以“局部选择”Gu8Part为支点
  25.                 {
  26.                     case 1:  

  27.                           ......    //此处省略N行代码

  28.                           break;

  29.                     case 2:  //局部2被选中

  30.                           ......    //此处省略N行代码

  31.                           break;
  32. }
  33. break;
  34. }

  35. vGu8BeepTimerFlag=0;  
  36. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  37. vGu8BeepTimerFlag=1;  

  38. vGu8KeySec=0;  
  39. break;

  40.    case 2:     //2号按键

  41.         ......    //此处省略N行代码,跟1号按键的代码类似

  42. vGu8BeepTimerFlag=0;  
  43. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  44. vGu8BeepTimerFlag=1;  

  45. vGu8KeySec=0;  
  46. break;

  47. }
  48. }
复制代码


本帖子中包含更多资源

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

x
| 2018-6-7 18:39 | 显示全部楼层
支持楼主,坐等更新
 楼主 | 2018-6-10 12:13 | 显示全部楼层
第一百二十节: 按键切换窗口切换局部来设置参数。

【120.1   按键切换窗口切换局部来设置参数。】
     
                上图120.1.1  数码管


                上图120.1.2  独立按键

           
                上图120.1.3  有源蜂鸣器

       为了更好理解上一节提出的人机界面程序框架的脉络,本节程序恰好包含了整屏更新与局部更新的应用,同时也引入了一个新的知识点:在人机界面的程序框架中,常常会遇到需要以“位”来编辑某个数据的情况,这种情况实际上是先把“待编辑数据”分解成几个“位”中间临时个体,然后显示并且编辑这些“位”中间临时个体,编辑结束后,再把这些“位”中间临时个体合并成一个完整的数据赋值给“待编辑数据”。
       本节程序功能如下:
      (1)有3个窗口1-XX,2-YY,3-ZZ,其中XX,YY,ZZ分别代表3个可编辑的数据Gu8SetDate_1,Gu8SetDate_2,Gu8SetDate_3。数据范围是从0到99。
      (2)K1按键。含“短按”与“长按”复合双功能。当数码管“没有闪烁”时,“短按”K1按键可以切换窗口,而“长按”K1按键会使数码管从“没有闪烁”进入到“闪烁模式”。当数码管处于“闪烁模式”时,“短按”K1可以使数码管在十位和个位之间切换“闪烁”的“局部位”,而“长按”K1表示更改完毕当前窗口数据并从“闪烁模式”退出到“没有闪烁”。
      (3)K2按键。当数码管处于“闪烁模式”时,每按一次K2按键就可以使当前闪烁的某位数码管“递增1”。
      (4)K3按键。当数码管处于“闪烁模式”时,每按一次K2按键就可以使当前闪烁的某位数码管“递减1”。
       上述功能,在窗口切换和退出“闪烁模式”时用到整屏更新,在闪烁的某位数码管切换“局部”时用到局部更新。代码如下:

  1. #include "REG52.H"  

  2. #define KEY_FILTER_TIME  25    //按键的“短按”兼“滤波”的“稳定时间”
  3. #define KEY_LONG_TIME    500   //按键的“长按”兼“滤波”的“稳定时间”

  4. #define SCAN_TIME  1   
  5. #define VOICE_TIME   50   
  6. #define BLINK_TIME   250    //数码管闪烁跳动的时间的间隔

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

  11. void KeyScan(void);  
  12. void KeyTask(void);  

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

  19. void PartUpdate(unsigned char u8Part);  //局部选择对应的某个局部变量更新显示输出

  20. void BeepOpen(void);   
  21. void BeepClose(void);

  22. sbit KEY_INPUT1=P2^2;  
  23. sbit KEY_INPUT2=P2^1;  
  24. sbit KEY_INPUT3=P2^0;  

  25. sbit P1_0=P1^0;  
  26. sbit P1_1=P1^1;  
  27. sbit P1_2=P1^2;  
  28. sbit P1_3=P1^3;

  29. sbit P3_4=P3^4;

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

  46. volatile unsigned char vGu8ScanTimerFlag=0;  
  47. volatile unsigned int vGu16ScanTimerCnt=0;  

  48. volatile unsigned char vGu8BeepTimerFlag=0;  
  49. volatile unsigned int vGu16BeepTimerCnt=0;  

  50. volatile unsigned char vGu8BlinkTimerFlag=0;   //数码管闪烁跳动的定时器
  51. volatile unsigned int vGu16BlinkTimerCnt=0;  

  52. unsigned char Gu8SetData_3=0; //单片机内部第3个可编辑的参数,在窗口3
  53. unsigned char Gu8SetData_2=0; //单片机内部第2个可编辑的参数,在窗口2
  54. unsigned char Gu8SetData_1=0; //单片机内部第1个可编辑的参数,在窗口1

  55. /* 注释一:
  56. *      在人机界面的程序框架中,常常会遇到需要以“位”来编辑某个数据的情况,这种情况
  57. *  实际上是先把“待编辑数据”分解成几个“位”临时中间个体,然后显示并且编辑这些“位”
  58. *  临时中间个体,编辑结束后,再把这些“位”临时中间个体合并成一个完整的数据赋值给
  59. *  “待编辑数据”。以下Gu8EditData_2和Gu8EditData_1就是“位”临时中间个体的中间变量。
  60. */

  61. unsigned char Gu8EditData_2=0;  //对应显示左起第3位数码管的“位”数据,是中间变量。
  62. unsigned char Gu8EditData_1=0;  //对应显示左起第4位数码管的“位”数据,是中间变量。

  63. unsigned char Gu8Wd=1;   //窗口选择变量。人机交互程序框架的支点。初始化开机后显示第1个窗口。
  64. unsigned char Gu8WdUpdate=1;  //整屏更新变量。初始化为1开机后整屏更新一次显示。
  65. unsigned char Gu8Part=0;   //局部选择变量。0代表当前窗口下没有数据被选中。
  66. unsigned char Gu8PartUpdate_1=0;   //局部1的更新变量,
  67. unsigned char Gu8PartUpdate_2=0;   //局部2的更新变量


  68. volatile unsigned char vGu8Display_Righ_4=1;   //左起第1位初始化显示窗口“1”
  69. volatile unsigned char vGu8Display_Righ_3=11;  //左起第2位初始化显示横杠“-”
  70. volatile unsigned char vGu8Display_Righ_2=0;   //左起第3位初始化显示数值“0”
  71. volatile unsigned char vGu8Display_Righ_1=0;   //左起第4位初始化显示数值“0”

  72. //不显示小数点
  73. volatile unsigned char vGu8Display_Righ_Dot_4=0;  
  74. volatile unsigned char vGu8Display_Righ_Dot_3=0;     
  75. volatile unsigned char vGu8Display_Righ_Dot_2=0;  
  76. volatile unsigned char vGu8Display_Righ_Dot_1=0;  

  77. volatile unsigned char vGu8KeySec=0;  

  78. void main()
  79. {
  80. SystemInitial();            
  81. Delay(10000);               
  82. PeripheralInitial();      
  83.     while(1)  
  84. {  
  85. KeyTask();      //按键的任务函数
  86. DisplayTask();  //数码管显示的上层任务函数
  87.     }
  88. }

  89. void PartUpdate(unsigned char u8Part)  //局部选择对应的某个局部变量更新显示输出
  90. {
  91.     switch(u8Part)
  92. {
  93.    case 1:
  94. Gu8PartUpdate_1=1;
  95. break;
  96.    case 2:
  97. Gu8PartUpdate_2=1;
  98. break;
  99. }

  100. }

  101. void KeyTask(void)    //按键的任务函数
  102. {
  103. if(0==vGu8KeySec)
  104. {
  105. return;
  106. }

  107. switch(vGu8KeySec)
  108. {
  109.    case 1:     //K1按键的“短按”,具有“切换窗口”和“切换局部”的双功能。
  110.         if(0==Gu8Part)  //处于“没有闪烁”的时候,是“切换窗口”
  111. {
  112.             switch(Gu8Wd) //在某个窗口下
  113.             {
  114.                 case 1:     //在窗口1下
  115. Gu8Wd=2;  //切换到窗口2
  116. Gu8EditData_2=Gu8SetData_2/10%10;  //“待编辑数据”分解成中间个体
  117. Gu8EditData_1=Gu8SetData_2/1%10;   //“待编辑数据”分解成中间个体
  118.               Gu8WdUpdate=1;  //整屏更新
  119.                       break;

  120.                 case 2:     //在窗口2下
  121. Gu8Wd=3;  //切换到窗口3
  122. Gu8EditData_2=Gu8SetData_3/10%10;  //“待编辑数据”分解成中间个体
  123. Gu8EditData_1=Gu8SetData_3/1%10;   //“待编辑数据”分解成中间个体
  124.               Gu8WdUpdate=1;  //整屏更新
  125.                       break;

  126.                 case 3:     //在窗口3下
  127. Gu8Wd=1;  //切换到窗口1
  128. Gu8EditData_2=Gu8SetData_1/10%10;  //“待编辑数据”分解成中间个体
  129. Gu8EditData_1=Gu8SetData_1/1%10;   //“待编辑数据”分解成中间个体
  130.              Gu8WdUpdate=1;  //整屏更新
  131.                      break;

  132. }
  133. }
  134.             else    //处于“闪烁模式”的时候,是“切换局部”
  135. {
  136.     PartUpdate(Gu8Part);  //切换之前的局部进行更新。
  137.     Gu8Part++;  //切换局部
  138.             if(Gu8Part>2)
  139.             {
  140. Gu8Part=1;
  141. }
  142.             PartUpdate(Gu8Part);  //切换之后的局部进行更新。
  143. }

  144. vGu8BeepTimerFlag=0;  
  145. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  146. vGu8BeepTimerFlag=1;  

  147. vGu8KeySec=0;  
  148. break;

  149.    case 2:     //递增按键K2
  150.         switch(Gu8Wd) //在某个窗口下
  151.         {
  152.            case 1:     //在窗口1下
  153.            case 2:     //在窗口2下,窗口2与窗口1的代码完全一模一样,因此可以这样共享
  154.            case 3:     //在窗口3下,窗口3与窗口1的代码完全一模一样,因此可以这样共享
  155.                 switch(Gu8Part)  //二级支点的局部选择
  156.                 {
  157.                     case 1:  //局部1被选中,代表左起第3位数码管被选中。
  158. Gu8EditData_2++;  //编辑“十位”个体的中间变量
  159. if(Gu8EditData_2>9)
  160. {
  161. Gu8EditData_2=9;
  162. }
  163. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  164.                           break;

  165.                    case 2:  //局部2被选中,代表左起第4位数码管被选中。
  166. Gu8EditData_1++;  //编辑“个位”个体的中间变量
  167. if(Gu8EditData_1>9)
  168. {
  169. Gu8EditData_1=9;
  170. }
  171. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  172.                           break;
  173. }
  174. break;
  175. }

  176. vGu8BeepTimerFlag=0;  
  177. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  178. vGu8BeepTimerFlag=1;  

  179. vGu8KeySec=0;  
  180. break;

  181.    case 3:     //递减按键K3
  182.         switch(Gu8Wd) //在某个窗口下
  183.         {
  184.            case 1:     //在窗口1下
  185.            case 2:     //在窗口2下,窗口2与窗口1的代码完全一模一样,因此可以这样共享
  186.            case 3:     //在窗口3下,窗口3与窗口1的代码完全一模一样,因此可以这样共享
  187.                 switch(Gu8Part)  //二级支点的局部选择
  188.                 {
  189.                     case 1:  //局部1被选中,代表左起第3位数码管被选中。
  190. if(Gu8EditData_2>0)
  191. {
  192. Gu8EditData_2--; //编辑“十位”个体的中间变量
  193. }
  194. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  195.                           break;

  196.                    case 2:  //局部2被选中,代表左起第4位数码管被选中。
  197. if(Gu8EditData_1>0)
  198. {
  199. Gu8EditData_1--; //编辑“个位”个体的中间变量
  200. }
  201. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  202.                           break;
  203. }
  204. break;
  205. }

  206. vGu8BeepTimerFlag=0;  
  207. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  208. vGu8BeepTimerFlag=1;  

  209. vGu8KeySec=0;  
  210. break;

  211.    case 4:     //K1按键的“长按”,具有进入和退出“闪烁模式”的功能。“退出”隐含“确定”

  212.         switch(Gu8Wd) //在某个窗口下
  213.         {
  214.            case 1:     //在窗口1下
  215.                 if(0==Gu8Part)  //处于“没有闪烁”的时候,将进入“闪烁模式”
  216. {
  217.     Gu8EditData_2=Gu8SetData_1/10%10;  //先把“待编辑数据”分解成中间个体
  218.     Gu8EditData_1=Gu8SetData_1/1%10;   //先把“待编辑数据”分解成中间个体
  219.     Gu8Part=1;  //进入“闪烁模式”,从“局部1”开始闪烁
  220. }
  221.                     else    //处于“闪烁模式”的时候,将退出到“没有闪烁”,隐含“确定”功能
  222. {
  223.     Gu8SetData_1=Gu8EditData_2*10+Gu8EditData_1; //把个体合并还原成数据
  224.     Gu8Part=0;  //退出“闪烁模式”
  225.             Gu8WdUpdate=1;  //整屏更新
  226. }
  227.                 break;

  228.            case 2:     //在窗口2下
  229.                 if(0==Gu8Part)  //处于“没有闪烁”的时候,将进入“闪烁模式”
  230. {
  231.     Gu8EditData_2=Gu8SetData_2/10%10;  //先把“待编辑数据”分解成中间个体
  232.     Gu8EditData_1=Gu8SetData_2/1%10;   //先把“待编辑数据”分解成中间个体
  233.     Gu8Part=1;  //进入“闪烁模式”,从“局部1”开始闪烁
  234. }
  235.                     else    //处于“闪烁模式”的时候,将退出到“没有闪烁”,隐含“确定”功能
  236. {
  237.     Gu8SetData_2=Gu8EditData_2*10+Gu8EditData_1; //把个体合并还原成数据
  238.     Gu8Part=0;  //退出“闪烁模式”
  239.             Gu8WdUpdate=1;  //整屏更新
  240. }
  241.                 break;

  242.            case 3:     //在窗口3下
  243.                 if(0==Gu8Part)  //处于“没有闪烁”的时候,将进入“闪烁模式”
  244. {
  245.     Gu8EditData_2=Gu8SetData_3/10%10;  //先把“待编辑数据”分解成中间个体
  246.     Gu8EditData_1=Gu8SetData_3/1%10;   //先把“待编辑数据”分解成中间个体
  247.     Gu8Part=1;  //进入“闪烁模式”,从“局部1”开始闪烁
  248. }
  249.                     else    //处于“闪烁模式”的时候,将退出到“没有闪烁”,隐含“确定”功能
  250. {
  251.     Gu8SetData_3=Gu8EditData_2*10+Gu8EditData_1; //把个体合并还原成数据
  252.     Gu8Part=0;  //退出“闪烁模式”
  253.             Gu8WdUpdate=1;  //整屏更新
  254. }
  255.                 break;

  256. }

  257. vGu8BeepTimerFlag=0;  
  258. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  259. vGu8BeepTimerFlag=1;  

  260. vGu8KeySec=0;  
  261. break;

  262. }
  263. }

  264. void DisplayTask(void) //数码管显示的上层任务函数
  265. {
  266.   switch(Gu8Wd)  //以窗口选择Gu8Wd为支点,去执行对应的窗口显示函数。又一次用到switch语句
  267. {
  268.     case 1:
  269.         Wd1();   //窗口1显示函数
  270.         break;
  271.     case 2:
  272.         Wd2();   //窗口2显示函数
  273.         break;
  274.     case 3:
  275.         Wd3();   //窗口3显示函数
  276.         break;
  277. }

  278. }

  279. void Wd1(void)   //窗口1显示函数
  280. {
  281. //需要借用的中间变量,用来拆分数据位。
  282. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  283. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量


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

  287. Su8Temp_4=1;   //左起第1位数码管,显示窗口“1”,属于静态数据,起“装饰”作用。
  288. Su8Temp_3=11;  //左起第2位数码管,显示横杠“-”,属于静态数据,起“装饰”作用。

  289. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  290. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量

  291. //不显示任何一个小数点,属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  292. vGu8Display_Righ_Dot_4=0;  
  293. vGu8Display_Righ_Dot_3=0;  
  294. vGu8Display_Righ_Dot_2=0;  
  295. vGu8Display_Righ_Dot_1=0;  

  296. Gu8PartUpdate_1=1;  //局部1更新显示
  297. Gu8PartUpdate_2=1  ;//局部2更新显示
  298. }

  299. if(1==Gu8PartUpdate_1) //局部1更新显示
  300. {
  301. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  302. Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。

  303. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  304. }

  305. if(1==Gu8PartUpdate_2) //局部2更新显示
  306. {
  307. Gu8PartUpdate_2=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  308. Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。

  309. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  310. }


  311. if(0==vGu16BlinkTimerCnt)  //某位被选中的数码管跳动闪烁的定时器
  312. {
  313. vGu8BlinkTimerFlag=0;
  314.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  315. vGu8BlinkTimerFlag=1;

  316.     switch(Gu8Part)  //某个局部被选中,则闪烁跳动
  317.     {
  318.         case 1:
  319.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  320. {
  321. Su8BlinkFlag=1;
  322. Su8Temp_2=10;  //左起第3个显示“不显示”(10代表不显示)
  323. }
  324. else
  325. {
  326. Su8BlinkFlag=0;
  327.               Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。
  328. }

  329.              break;

  330.         case 2:
  331.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  332. {
  333. Su8BlinkFlag=1;
  334. Su8Temp_1=10;  //左起第4个显示“不显示”(10代表不显示)
  335. }
  336. else
  337. {
  338. Su8BlinkFlag=0;
  339.               Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。
  340. }

  341.              break;

  342.         default:   //都没有被选中的时候
  343.        Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。
  344. Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。      
  345.              break;
  346. }

  347. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  348. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量

  349. }
  350. }

  351. void Wd2(void)   //窗口2显示函数
  352. {
  353. //需要借用的中间变量,用来拆分数据位。
  354. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  355. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量


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

  359. Su8Temp_4=2;   //左起第1位数码管,显示窗口“2”,属于静态数据,起“装饰”作用。
  360. Su8Temp_3=11;  //左起第2位数码管,显示横杠“-”,属于静态数据,起“装饰”作用。

  361. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  362. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量

  363. //不显示任何一个小数点,属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  364. vGu8Display_Righ_Dot_4=0;  
  365. vGu8Display_Righ_Dot_3=0;  
  366. vGu8Display_Righ_Dot_2=0;  
  367. vGu8Display_Righ_Dot_1=0;  

  368. Gu8PartUpdate_1=1;  //局部1更新显示
  369. Gu8PartUpdate_2=1  ;//局部2更新显示
  370. }

  371. if(1==Gu8PartUpdate_1) //局部1更新显示
  372. {
  373. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  374. Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。

  375. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  376. }

  377. if(1==Gu8PartUpdate_2) //局部2更新显示
  378. {
  379. Gu8PartUpdate_2=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  380. Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。

  381. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  382. }


  383. if(0==vGu16BlinkTimerCnt)  //某位被选中的数码管跳动闪烁的定时器
  384. {
  385. vGu8BlinkTimerFlag=0;
  386.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  387. vGu8BlinkTimerFlag=1;

  388.     switch(Gu8Part)  //某个局部被选中,则闪烁跳动
  389.     {
  390.         case 1:
  391.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  392. {
  393. Su8BlinkFlag=1;
  394. Su8Temp_2=10;  //左起第3个显示“不显示”(10代表不显示)
  395. }
  396. else
  397. {
  398. Su8BlinkFlag=0;
  399.               Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。
  400. }

  401.              break;

  402.         case 2:
  403.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  404. {
  405. Su8BlinkFlag=1;
  406. Su8Temp_1=10;  //左起第4个显示“不显示”(10代表不显示)
  407. }
  408. else
  409. {
  410. Su8BlinkFlag=0;
  411.               Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。
  412. }

  413.              break;

  414.         default:   //都没有被选中的时候
  415.        Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。
  416. Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。      
  417.              break;
  418. }

  419. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  420. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量

  421. }
  422. }

  423. void Wd3(void)   //窗口3显示函数
  424. {
  425. //需要借用的中间变量,用来拆分数据位。
  426. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  427. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量


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

  431. Su8Temp_4=3;   //左起第1位数码管,显示窗口“3”,属于静态数据,起“装饰”作用。
  432. Su8Temp_3=11;  //左起第2位数码管,显示横杠“-”,属于静态数据,起“装饰”作用。

  433. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  434. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量

  435. //不显示任何一个小数点,属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  436. vGu8Display_Righ_Dot_4=0;  
  437. vGu8Display_Righ_Dot_3=0;  
  438. vGu8Display_Righ_Dot_2=0;  
  439. vGu8Display_Righ_Dot_1=0;  

  440. Gu8PartUpdate_1=1;  //局部1更新显示
  441. Gu8PartUpdate_2=1  ;//局部2更新显示
  442. }

  443. if(1==Gu8PartUpdate_1) //局部1更新显示
  444. {
  445. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  446. Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。

  447. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  448. }

  449. if(1==Gu8PartUpdate_2) //局部2更新显示
  450. {
  451. Gu8PartUpdate_2=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  452. Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。

  453. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  454. }


  455. if(0==vGu16BlinkTimerCnt)  //某位被选中的数码管跳动闪烁的定时器
  456. {
  457. vGu8BlinkTimerFlag=0;
  458.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  459. vGu8BlinkTimerFlag=1;

  460.     switch(Gu8Part)  //某个局部被选中,则闪烁跳动
  461.     {
  462.         case 1:
  463.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  464. {
  465. Su8BlinkFlag=1;
  466. Su8Temp_2=10;  //左起第3个显示“不显示”(10代表不显示)
  467. }
  468. else
  469. {
  470. Su8BlinkFlag=0;
  471.               Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。
  472. }

  473.              break;

  474.         case 2:
  475.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  476. {
  477. Su8BlinkFlag=1;
  478. Su8Temp_1=10;  //左起第4个显示“不显示”(10代表不显示)
  479. }
  480. else
  481. {
  482. Su8BlinkFlag=0;
  483.               Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。
  484. }

  485.              break;

  486.         default:   //都没有被选中的时候
  487.        Su8Temp_2=Gu8EditData_2;  //显示“十位”的临时中间个体,属于动态数据。
  488. Su8Temp_1=Gu8EditData_1;  //显示“个位”的临时中间个体,属于动态数据。      
  489.              break;
  490. }

  491. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  492. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量

  493. }
  494. }


  495. void KeyScan(void)  //按键底层的驱动扫描函数,放在定时中断函数里
  496. {
  497.    static unsigned char Su8KeyShortFlag=0;  //按键“短按”触发的标志   
  498.    static unsigned char Su8KeyLock1;
  499.    static unsigned int  Su16KeyCnt1;
  500.    static unsigned char Su8KeyLock2;
  501.    static unsigned int  Su16KeyCnt2;
  502.    static unsigned char Su8KeyLock3;
  503.    static unsigned int  Su16KeyCnt3;

  504.   //需要详细分析以下这段“短按”与“长按”代码的朋友,请参考第96节。
  505.    if(0!=KEY_INPUT1)
  506.    {
  507.       Su8KeyLock1=0;
  508.       Su16KeyCnt1=0;   
  509.       if(1==Su8KeyShortFlag)  
  510.       {
  511. Su8KeyShortFlag=0;   
  512. vGu8KeySec=1;    //触发K1的“短按”
  513. }  
  514.    }
  515.    else if(0==Su8KeyLock1)
  516.    {
  517.       Su16KeyCnt1++;

  518.       if(Su16KeyCnt1>=KEY_FILTER_TIME)
  519.       {
  520.             Su8KeyShortFlag=1;  
  521.       }

  522.       if(Su16KeyCnt1>=KEY_LONG_TIME)
  523.       {
  524.             Su8KeyLock1=1;      
  525. Su8KeyShortFlag=0;  
  526.             vGu8KeySec=4; //触发K1的“长按”
  527.       }
  528.    }

  529.    if(0!=KEY_INPUT2)
  530.    {
  531.       Su8KeyLock2=0;
  532.       Su16KeyCnt2=0;      
  533.    }
  534.    else if(0==Su8KeyLock2)
  535.    {
  536.       Su16KeyCnt2++;
  537.       if(Su16KeyCnt2>=KEY_FILTER_TIME)
  538.       {
  539.          Su8KeyLock2=1;  
  540.          vGu8KeySec=2;   
  541.       }
  542.    }

  543.    if(0!=KEY_INPUT3)
  544.    {
  545.       Su8KeyLock3=0;
  546.       Su16KeyCnt3=0;      
  547.    }
  548.    else if(0==Su8KeyLock3)
  549.    {
  550.       Su16KeyCnt3++;
  551.       if(Su16KeyCnt3>=KEY_FILTER_TIME)
  552.       {
  553.          Su8KeyLock3=1;  
  554.          vGu8KeySec=3;   
  555.       }
  556.    }

  557. }

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

  562. if(0==vGu16ScanTimerCnt)  
  563. {


  564.     P0=0x00;
  565. P1_0=1;  
  566. P1_1=1;  
  567. P1_2=1;  
  568. P1_3=1;  

  569.     switch(Su8ScanStep)
  570. {
  571.        case 1:
  572. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  573. if(1==vGu8Display_Righ_Dot_1)
  574. {
  575. Su8GetCode=Su8GetCode|0x80;  
  576. }
  577.     P0=Su8GetCode;
  578. P1_0=0;
  579. P1_1=1;  
  580. P1_2=1;  
  581. P1_3=1;            
  582. break;

  583.        case 2:
  584. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  585. if(1==vGu8Display_Righ_Dot_2)
  586. {
  587. Su8GetCode=Su8GetCode|0x80;  
  588. }
  589.     P0=Su8GetCode;
  590. P1_0=1;  
  591. P1_1=0;
  592. P1_2=1;
  593. P1_3=1;         
  594. break;

  595.        case 3:
  596. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  597. if(1==vGu8Display_Righ_Dot_3)
  598. {
  599. Su8GetCode=Su8GetCode|0x80;  
  600. }
  601.     P0=Su8GetCode;
  602. P1_0=1;  
  603. P1_1=1;  
  604. P1_2=0;  
  605. P1_3=1;         
  606. break;

  607.        case 4:
  608. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  609. if(1==vGu8Display_Righ_Dot_4)
  610. {
  611. Su8GetCode=Su8GetCode|0x80;  
  612. }
  613.     P0=Su8GetCode;
  614. P1_0=1;  
  615. P1_1=1;  
  616. P1_2=1;  
  617. P1_3=0;           
  618. break;

  619. }

  620. Su8ScanStep++;
  621. if(Su8ScanStep>4)
  622. {
  623. Su8ScanStep=1;
  624. }

  625. vGu8ScanTimerFlag=0;
  626. vGu16ScanTimerCnt=SCAN_TIME;  
  627. vGu8ScanTimerFlag=1;  
  628. }
  629. }


  630. void VoiceScan(void) //蜂鸣器的驱动函数
  631. {

  632.           static unsigned char Su8Lock=0;  

  633. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  634.           {
  635.                   if(0==Su8Lock)
  636.                   {
  637.                    Su8Lock=1;  
  638. BeepOpen();
  639.      }
  640.     else  
  641. {     

  642.                        vGu16BeepTimerCnt--;         

  643.                    if(0==vGu16BeepTimerCnt)
  644.                    {
  645.                            Su8Lock=0;     
  646. BeepClose();  
  647.                    }

  648. }
  649.           }         
  650. }

  651. void BeepOpen(void)
  652. {
  653. P3_4=0;  
  654. }

  655. void BeepClose(void)
  656. {
  657. P3_4=1;  
  658. }

  659. void T0_time() interrupt 1     
  660. {
  661. VoiceScan();    //蜂鸣器的驱动函数
  662. KeyScan();      //按键底层的驱动扫描函数
  663. DisplayScan();  //数码管底层的驱动扫描函数

  664. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  665. {
  666. vGu16ScanTimerCnt--;  //递减式的软件定时器
  667. }

  668. if(1==vGu8BlinkTimerFlag&&vGu16BlinkTimerCnt>0)   //数码管闪烁跳动的定时器
  669. {
  670. vGu16BlinkTimerCnt--;  //递减式的软件定时器
  671. }

  672. TH0=0xfc;   
  673. TL0=0x66;   
  674. }


  675. void SystemInitial(void)
  676. {
  677. P0=0x00;
  678. P1_0=1;  
  679. P1_1=1;  
  680. P1_2=1;  
  681. P1_3=1;  

  682. TMOD=0x01;  
  683. TH0=0xfc;   
  684. TL0=0x66;   
  685. EA=1;      
  686. ET0=1;      
  687. TR0=1;      
  688. }

  689. void Delay(unsigned long u32DelayTime)
  690. {
  691.     for(;u32DelayTime>0;u32DelayTime--);
  692. }

  693. void PeripheralInitial(void)
  694. {

  695. }
复制代码


本帖子中包含更多资源

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

x
 楼主 | 2018-6-21 14:36 | 显示全部楼层
第一百二十一节: 可调参数的数码管倒计时。

【121.1   可调参数的数码管倒计时。】

                上图121.1.1  数码管



                上图121.1.2  独立按键

        
                上图121.1.3  有源蜂鸣器

       上节讲如何设置数据,本节讲“数据”如何关联“某种功能”,本节的“可调参数”就是“数据”,“倒计时”就是“某种功能”。程序功能如下:
      (1)倒计时范围从0.00秒到99.99秒,范围可调。开机默认是:10.00秒。
      (2)K1[设置键]。当数码管“没有闪烁”时,“长按”K1键则进入“闪烁模式”,某位数码管开始闪烁,闪烁的位代表可修改的位数据,此时再“短按”K1按键可以使数码管在位之间切换闪烁。当数码管处于“闪烁模式”时,“长按”K1按键,代表数据修改完成并停止闪烁。
      (3)K2[加键]与[复位健]。当数码管某位正在闪烁时,此时K2是[加键],按K2会使位数据“自加1”。当数码管“没有闪烁”时,此时K2是[复位键],按K2会使当前倒计时数据恢复“设置值”。
      (4)K3[减键]与[开始健]。当数码管某位正在闪烁时,此时K3是[减键],按K3会使位数据“自减1”。当数码管“没有闪烁”时,此时K3是[开始键],按K3开始倒计时。
       代码如下:

  1. #include "REG52.H"  

  2. #define KEY_FILTER_TIME  25    //按键的“短按”兼“滤波”的“稳定时间”
  3. #define KEY_LONG_TIME    500   //按键的“长按”兼“滤波”的“稳定时间”

  4. #define SCAN_TIME  1   
  5. #define VOICE_TIME   50   
  6. #define BLINK_TIME   250    //数码管闪烁跳动的时间的间隔

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

  11. void KeyScan(void);  
  12. void KeyTask(void);  

  13. void VoiceScan(void);  
  14. void DisplayScan(void);  
  15. void DisplayTask(void);  //上层显示的任务函数
  16. void RunTask(void);      //倒计时的应用程序

  17. void Wd1(void);   //窗口1显示函数。用来设置参数。
  18. void Wd2(void);   //窗口2显示函数。倒计时的运行显示窗口

  19. void PartUpdate(unsigned char u8Part);  //局部选择对应的某个局部变量更新显示输出

  20. void BeepOpen(void);   
  21. void BeepClose(void);

  22. sbit KEY_INPUT1=P2^2;  
  23. sbit KEY_INPUT2=P2^1;  
  24. sbit KEY_INPUT3=P2^0;  

  25. sbit P1_0=P1^0;  
  26. sbit P1_1=P1^1;  
  27. sbit P1_2=P1^2;  
  28. sbit P1_3=P1^3;

  29. sbit P3_4=P3^4;

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

  46. volatile unsigned char vGu8ScanTimerFlag=0;  
  47. volatile unsigned int vGu16ScanTimerCnt=0;  

  48. volatile unsigned char vGu8BeepTimerFlag=0;  
  49. volatile unsigned int vGu16BeepTimerCnt=0;  

  50. volatile unsigned char vGu8BlinkTimerFlag=0;   //数码管闪烁跳动的定时器
  51. volatile unsigned int vGu16BlinkTimerCnt=0;  

  52. //倒计时的软件定时器,注意,这里是unsigned long类型,范围是0到4294967295毫秒
  53. volatile unsigned char vGu8CountdownTimerFlag=0;  
  54. volatile unsigned long vGu32CountdownTimerCnt=10000;  //当前倒计时的计时值
  55. unsigned long Gu32SetData_Countdown=10000;  //倒计时的设置值


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

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

  62. unsigned char Gu8EditData_4=0;  //对应显示右起第4位数码管的“位”数据,是中间变量。
  63. unsigned char Gu8EditData_3=0;  //对应显示右起第3位数码管的“位”数据,是中间变量。
  64. unsigned char Gu8EditData_2=0;  //对应显示右起第2位数码管的“位”数据,是中间变量。
  65. unsigned char Gu8EditData_1=0;  //对应显示右起第1位数码管的“位”数据,是中间变量。

  66. unsigned char Gu8Wd=1;   //窗口选择变量。人机交互程序框架的支点。初始化开机后显示第1个窗口。
  67. unsigned char Gu8WdUpdate=1;  //整屏更新变量。初始化为1开机后整屏更新一次显示。
  68. unsigned char Gu8Part=0;   //局部选择变量。0代表当前窗口下没有数据被选中。
  69. unsigned char Gu8PartUpdate_1=0;   //局部1的更新变量,
  70. unsigned char Gu8PartUpdate_2=0;   //局部2的更新变量
  71. unsigned char Gu8PartUpdate_3=0;   //局部3的更新变量,
  72. unsigned char Gu8PartUpdate_4=0;   //局部4的更新变量

  73. volatile unsigned char vGu8Display_Righ_4=1;  //显示“1”,跟vGu32CountdownTimerCnt高位一致
  74. volatile unsigned char vGu8Display_Righ_3=0;  
  75. volatile unsigned char vGu8Display_Righ_2=0;  
  76. volatile unsigned char vGu8Display_Righ_1=0;  

  77. volatile unsigned char vGu8Display_Righ_Dot_4=0;  
  78. volatile unsigned char vGu8Display_Righ_Dot_3=1;    //开机默认保留显示2个小数点
  79. volatile unsigned char vGu8Display_Righ_Dot_2=0;  
  80. volatile unsigned char vGu8Display_Righ_Dot_1=0;  

  81. volatile unsigned char vGu8KeySec=0;  

  82. void main()
  83. {
  84. SystemInitial();            
  85. Delay(10000);               
  86. PeripheralInitial();      
  87.     while(1)  
  88. {  
  89. KeyTask();      //按键的任务函数
  90. DisplayTask();  //数码管显示的上层任务函数
  91. RunTask();      //倒计时的应用程序
  92.     }
  93. }

  94. void PartUpdate(unsigned char u8Part)  //局部选择对应的某个局部变量更新显示输出
  95. {
  96.     switch(u8Part)
  97. {
  98.    case 1:
  99. Gu8PartUpdate_1=1;
  100. break;
  101.    case 2:
  102. Gu8PartUpdate_2=1;
  103. break;
  104.    case 3:
  105. Gu8PartUpdate_3=1;
  106. break;
  107.    case 4:
  108. Gu8PartUpdate_4=1;
  109. break;
  110. }

  111. }

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

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

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

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

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


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

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

  133.             if(0==vGu32CountdownTimerCnt) //如果倒计时的时间到,则跳转到结束的步骤
  134.             {
  135. Gu8RunStep=2; //跳转到倒计时结束的步骤
  136. }

  137. }
  138. break;

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

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

  143. vGu8BeepTimerFlag=0;  
  144. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  145. vGu8BeepTimerFlag=1;  

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

  147. break;

  148. }

  149. }


  150. void KeyTask(void)    //按键的任务函数
  151. {
  152. if(0==vGu8KeySec)
  153. {
  154. return;
  155. }

  156. if(0!=Gu8RunStatus) //在“非停止”状态下,用return来拦截一些“不该响应”的按键
  157. {
  158.     if(2==vGu8KeySec) //在“非停止”状态下,只响应[复位]这个按键
  159.     {
  160.        ;   //这里没有return语句,表示可以继续往下扫描本函数余下的代码,没有被拦截。
  161. }
  162. else
  163. {
  164.    return;   //其余的按键则拦截退出
  165. }
  166. }


  167. switch(vGu8KeySec)
  168. {
  169.    case 1:     //按键K1的“短按”。切换数码管闪烁的位。
  170.         switch(Gu8Wd) //在某个窗口下
  171.         {
  172.             case 1:     //在窗口1下

  173.                  if(0!=Gu8Part)  //处于“闪烁模式”的时候,是“切换局部”
  174. {
  175.               PartUpdate(Gu8Part);  //切换之前的局部进行更新。
  176.               Gu8Part++;  //切换局部
  177.                       if(Gu8Part>4)
  178.                       {
  179. Gu8Part=1;
  180. }
  181.                       PartUpdate(Gu8Part);  //切换之后的局部进行更新。

  182. vGu8BeepTimerFlag=0;  
  183. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  184. vGu8BeepTimerFlag=1;
  185. }
  186.                       break;
  187.             }

  188. vGu8KeySec=0;  
  189. break;

  190.    case 2:     //按键K2[加键]与[复位健]
  191.         if(0!=Gu8Part)  //处于“闪烁模式”的时候,是[加键]
  192. {
  193.             switch(Gu8Wd) //在某个窗口下
  194.             {
  195.                 case 1:     //在窗口1下
  196.                      switch(Gu8Part)  //二级支点的局部选择
  197.                      {
  198.                           case 1:  //局部1被选中,代表右起第4位数码管被选中。
  199. if(Gu8EditData_4<9)
  200. {
  201. Gu8EditData_4++;  //编辑“千位”个体的中间变量
  202. }
  203. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  204.                                 break;

  205.                           case 2:  //局部2被选中,代表右起第3位数码管被选中。
  206. if(Gu8EditData_3<9)
  207. {
  208. Gu8EditData_3++;  //编辑“百位”个体的中间变量
  209. }
  210. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  211.                                 break;

  212.                           case 3:  //局部3被选中,代表右起第2位数码管被选中。
  213. if(Gu8EditData_2<9)
  214. {
  215. Gu8EditData_2++;  //编辑“十位”个体的中间变量
  216. }
  217. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  218.                                 break;

  219.                           case 4:  //局部4被选中,代表右起第1位数码管被选中。
  220. if(Gu8EditData_1<9)
  221. {
  222. Gu8EditData_1++;  //编辑“个位”个体的中间变量
  223. }
  224. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  225.                                 break;
  226. }
  227. break;
  228. }
  229.             }
  230.             else   //处于“没有闪烁”的时候,是[复位健]
  231. {
  232. Gu8EditData_4=Gu32SetData_Countdown/10000%10; //分解成“十秒”个体
  233. Gu8EditData_3=Gu32SetData_Countdown/1000%10;  //分解成“个秒”个体
  234. Gu8EditData_2=Gu32SetData_Countdown/100%10; //分解成“百毫秒”个体
  235. Gu8EditData_1=Gu32SetData_Countdown/10%10;  //分解成“十毫秒”个体

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

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

  239.                 Gu8Wd=1; //返回设置数据的窗口
  240.                 Gu8WdUpdate=1;  //整屏更新一次显示
  241. }

  242. vGu8BeepTimerFlag=0;  
  243. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  244. vGu8BeepTimerFlag=1;  

  245. vGu8KeySec=0;  
  246. break;

  247.    case 3:     //按键K3[减键]与[开始健]
  248.         if(0!=Gu8Part)  //处于“闪烁模式”的时候,是[减键]
  249. {
  250.             switch(Gu8Wd) //在某个窗口下
  251.             {
  252.                 case 1:     //在窗口1下
  253.                      switch(Gu8Part)  //二级支点的局部选择
  254.                      {
  255.                           case 1:  //局部1被选中,代表右起第4位数码管被选中。
  256. if(Gu8EditData_4>0)
  257. {
  258. Gu8EditData_4--;  //编辑“十秒”个体的中间变量
  259. }
  260. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  261.                                 break;

  262.                           case 2:  //局部2被选中,代表右起第3位数码管被选中。
  263. if(Gu8EditData_3>0)
  264. {
  265. Gu8EditData_3--;  //编辑“个秒”个体的中间变量
  266. }
  267. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  268.                                 break;

  269.                           case 3:  //局部3被选中,代表右起第2位数码管被选中。
  270. if(Gu8EditData_2>0)
  271. {
  272. Gu8EditData_2--;  //编辑“百毫秒”个体的中间变量
  273. }
  274. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  275.                                 break;

  276.                           case 4:  //局部4被选中,代表右起第1位数码管被选中。
  277. if(Gu8EditData_1>0)
  278. {
  279. Gu8EditData_1--;  //编辑“十毫位”个体的中间变量
  280. }
  281. PartUpdate(Gu8Part); //当前局部更新显示输出到数码管
  282.                                 break;
  283. }
  284. break;
  285. }
  286.             }
  287.             else   //处于“没有闪烁”的时候,是[开始健]
  288. {
  289.             if(0==Gu8RunStatus) //在停止状态下
  290.             {

  291. vGu8CountdownTimerFlag=0;
  292. vGu32CountdownTimerCnt=Gu32SetData_Countdown;   //从“设置值”开始倒计时
  293. vGu8CountdownTimerFlag=1;      //允许倒计时的软件定时器的启动

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

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

  297.                     Gu8Wd=2; //进入倒计时运行的窗口
  298.                 Gu8WdUpdate=1;  //整屏更新一次显示,确保在启动的时候能显示到最新的数据
  299. }
  300. }

  301. vGu8BeepTimerFlag=0;  
  302. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  303. vGu8BeepTimerFlag=1;  

  304. vGu8KeySec=0;  
  305. break;

  306.    case 4:     //K1按键的“长按”,具有进入和退出“闪烁模式”的功能。“退出”隐含“确定”

  307.         switch(Gu8Wd) //在某个窗口下
  308.         {
  309.            case 1:     //在窗口1下
  310.                 if(0==Gu8Part)  //处于“没有闪烁”的时候,将进入“闪烁模式”
  311. {
  312.     Gu8EditData_4=Gu32SetData_Countdown/10000%10; //分解成“十秒”个体
  313.     Gu8EditData_3=Gu32SetData_Countdown/1000%10;  //分解成“个秒”个体
  314.     Gu8EditData_2=Gu32SetData_Countdown/100%10; //分解成“百毫秒”个体
  315.     Gu8EditData_1=Gu32SetData_Countdown/10%10;  //分解成“十毫秒”个体
  316.     Gu8Part=1;  //进入“闪烁模式”,从“局部1”开始闪烁
  317. }
  318.                     else    //处于“闪烁模式”的时候,将退出到“没有闪烁”,隐含“确定”功能
  319. {
  320. //把个体合并还原成数据
  321. Gu32SetData_Countdown=Gu8EditData_4*10000+Gu8EditData_3*1000;
  322. Gu32SetData_Countdown=Gu32SetData_Countdown+Gu8EditData_2*100;
  323. Gu32SetData_Countdown=Gu32SetData_Countdown+Gu8EditData_1*10;

  324.     Gu8Part=0;  //退出“闪烁模式”
  325.             Gu8WdUpdate=1;  //整屏更新
  326. }

  327.                 break;

  328. }

  329. vGu8BeepTimerFlag=0;  
  330. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  331. vGu8BeepTimerFlag=1;  

  332. vGu8KeySec=0;  
  333. break;

  334. }
  335. }

  336. void DisplayTask(void) //数码管显示的上层任务函数
  337. {
  338.   switch(Gu8Wd)  //以窗口选择Gu8Wd为支点,去执行对应的窗口显示函数。又一次用到switch语句
  339. {
  340.     case 1:
  341.         Wd1();   //窗口1显示函数。用来设置参数。
  342.         break;
  343.     case 2:
  344.         Wd2();   //窗口2显示函数。倒计时的运行显示窗口。
  345.         break;
  346. }

  347. }

  348. void Wd1(void)   //窗口1显示函数。用来设置参数。
  349. {
  350. //需要借用的中间变量,用来拆分数据位。
  351. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  352. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量


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

  356. //属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  357. vGu8Display_Righ_Dot_4=0;  
  358. vGu8Display_Righ_Dot_3=1;   //保留显示2位小数点
  359. vGu8Display_Righ_Dot_2=0;  
  360. vGu8Display_Righ_Dot_1=0;  

  361. Gu8PartUpdate_1=1;  //局部1更新显示
  362. Gu8PartUpdate_2=1  ;//局部2更新显示
  363. Gu8PartUpdate_3=1  ;//局部3更新显示
  364. Gu8PartUpdate_4=1  ;//局部4更新显示

  365. }

  366. if(1==Gu8PartUpdate_1) //局部1更新显示
  367. {
  368. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  369.       if(Gu32SetData_Countdown<10000)
  370.       {
  371.     Su8Temp_4=10;  //显示“无”
  372. }
  373. else
  374. {
  375.     Su8Temp_4=Gu8EditData_4;  //显示“十秒”的临时中间个体,属于动态数据。
  376. }

  377. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  378. }

  379. if(1==Gu8PartUpdate_2) //局部2更新显示
  380. {
  381. Gu8PartUpdate_2=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  382. Su8Temp_3=Gu8EditData_3;  //显示“个秒”的临时中间个体,属于动态数据。

  383. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量
  384. }

  385. if(1==Gu8PartUpdate_3) //局部3更新显示
  386. {
  387. Gu8PartUpdate_3=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  388. Su8Temp_2=Gu8EditData_2;  //显示“百毫秒”的临时中间个体,属于动态数据。

  389. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  390. }

  391. if(1==Gu8PartUpdate_4) //局部4更新显示
  392. {
  393. Gu8PartUpdate_4=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  394. Su8Temp_1=Gu8EditData_1;  //显示“十毫秒”的临时中间个体,属于动态数据。

  395. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  396. }

  397. if(0==vGu16BlinkTimerCnt)  //某位被选中的数码管跳动闪烁的定时器
  398. {
  399. vGu8BlinkTimerFlag=0;
  400.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  401. vGu8BlinkTimerFlag=1;

  402.     switch(Gu8Part)  //某个局部被选中,则闪烁跳动
  403.     {
  404.         case 1:
  405.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  406. {
  407. Su8BlinkFlag=1;
  408. Su8Temp_4=10;  //右起第4个显示“不显示”(10代表不显示)
  409. }
  410. else
  411. {
  412. Su8BlinkFlag=0;
  413.               Su8Temp_4=Gu8EditData_4;  //显示“十秒”的临时中间个体,属于动态数据。
  414. }

  415.              break;

  416.         case 2:
  417.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  418. {
  419. Su8BlinkFlag=1;
  420. Su8Temp_3=10;  //右起第3个显示“不显示”(10代表不显示)
  421. }
  422. else
  423. {
  424. Su8BlinkFlag=0;
  425.               Su8Temp_3=Gu8EditData_3;  //显示“个秒”的临时中间个体,属于动态数据。
  426. }

  427.              break;

  428.         case 3:
  429.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  430. {
  431. Su8BlinkFlag=1;
  432. Su8Temp_2=10;  //右起第2个显示“不显示”(10代表不显示)
  433. }
  434. else
  435. {
  436. Su8BlinkFlag=0;
  437.               Su8Temp_2=Gu8EditData_2;  //显示“百毫秒”的临时中间个体,属于动态数据。
  438. }

  439.              break;

  440.         case 4:
  441.              if(0==Su8BlinkFlag)  //两种状态的切换判断
  442. {
  443. Su8BlinkFlag=1;
  444. Su8Temp_1=10;  //右起第1个显示“不显示”(10代表不显示)
  445. }
  446. else
  447. {
  448. Su8BlinkFlag=0;
  449.               Su8Temp_1=Gu8EditData_1;  //显示“十毫秒”的临时中间个体,属于动态数据。
  450. }

  451.              break;

  452.         default:   //都没有被选中的时候
  453.               if(Gu32SetData_Countdown<10000)
  454.               {
  455.            Su8Temp_4=10;  //显示“无”
  456. }
  457. else
  458. {
  459.             Su8Temp_4=Gu8EditData_4;  //显示“十秒”的临时中间个体,属于动态数据。
  460. }
  461. Su8Temp_3=Gu8EditData_3;  //显示“个秒”的临时中间个体,属于动态数据。     
  462.        Su8Temp_2=Gu8EditData_2;  //显示“百毫秒”的临时中间个体,属于动态数据。
  463. Su8Temp_1=Gu8EditData_1;  //显示“十毫秒”的临时中间个体,属于动态数据。      
  464.              break;
  465. }

  466. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  467. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量
  468. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  469. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量

  470. }
  471. }

  472. void Wd2(void)   //窗口2显示函数。倒计时的运行显示窗口。
  473. {
  474. //需要借用的中间变量,用来拆分数据位。
  475. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量

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

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

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

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

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

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

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

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

  499. vGu8Display_Righ_Dot_4=0;  
  500. vGu8Display_Righ_Dot_3=1;   //保留显示2位小数点
  501. vGu8Display_Righ_Dot_2=0;  
  502. vGu8Display_Righ_Dot_1=0;  

  503. }
  504. }

  505. void KeyScan(void)  //按键底层的驱动扫描函数,放在定时中断函数里
  506. {
  507.    static unsigned char Su8KeyShortFlag=0;  //按键“短按”触发的标志   
  508.    static unsigned char Su8KeyLock1;
  509.    static unsigned int  Su16KeyCnt1;
  510.    static unsigned char Su8KeyLock2;
  511.    static unsigned int  Su16KeyCnt2;
  512.    static unsigned char Su8KeyLock3;
  513.    static unsigned int  Su16KeyCnt3;

  514.   //需要详细分析以下这段“短按”与“长按”代码的朋友,请参考第96节。
  515.    if(0!=KEY_INPUT1)
  516.    {
  517.       Su8KeyLock1=0;
  518.       Su16KeyCnt1=0;   
  519.       if(1==Su8KeyShortFlag)  
  520.       {
  521. Su8KeyShortFlag=0;   
  522. vGu8KeySec=1;    //触发K1的“短按”
  523. }  
  524.    }
  525.    else if(0==Su8KeyLock1)
  526.    {
  527.       Su16KeyCnt1++;

  528.       if(Su16KeyCnt1>=KEY_FILTER_TIME)
  529.       {
  530.             Su8KeyShortFlag=1;  
  531.       }

  532.       if(Su16KeyCnt1>=KEY_LONG_TIME)
  533.       {
  534.             Su8KeyLock1=1;      
  535. Su8KeyShortFlag=0;  
  536.             vGu8KeySec=4; //触发K1的“长按”
  537.       }
  538.    }

  539.    if(0!=KEY_INPUT2)
  540.    {
  541.       Su8KeyLock2=0;
  542.       Su16KeyCnt2=0;      
  543.    }
  544.    else if(0==Su8KeyLock2)
  545.    {
  546.       Su16KeyCnt2++;
  547.       if(Su16KeyCnt2>=KEY_FILTER_TIME)
  548.       {
  549.          Su8KeyLock2=1;  
  550.          vGu8KeySec=2;   
  551.       }
  552.    }

  553.    if(0!=KEY_INPUT3)
  554.    {
  555.       Su8KeyLock3=0;
  556.       Su16KeyCnt3=0;      
  557.    }
  558.    else if(0==Su8KeyLock3)
  559.    {
  560.       Su16KeyCnt3++;
  561.       if(Su16KeyCnt3>=KEY_FILTER_TIME)
  562.       {
  563.          Su8KeyLock3=1;  
  564.          vGu8KeySec=3;   
  565.       }
  566.    }

  567. }

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

  572. if(0==vGu16ScanTimerCnt)  
  573. {


  574.     P0=0x00;
  575. P1_0=1;  
  576. P1_1=1;  
  577. P1_2=1;  
  578. P1_3=1;  

  579.     switch(Su8ScanStep)
  580. {
  581.        case 1:
  582. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  583. if(1==vGu8Display_Righ_Dot_1)
  584. {
  585. Su8GetCode=Su8GetCode|0x80;  
  586. }
  587.     P0=Su8GetCode;
  588. P1_0=0;
  589. P1_1=1;  
  590. P1_2=1;  
  591. P1_3=1;            
  592. break;

  593.        case 2:
  594. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  595. if(1==vGu8Display_Righ_Dot_2)
  596. {
  597. Su8GetCode=Su8GetCode|0x80;  
  598. }
  599.     P0=Su8GetCode;
  600. P1_0=1;  
  601. P1_1=0;
  602. P1_2=1;
  603. P1_3=1;         
  604. break;

  605.        case 3:
  606. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  607. if(1==vGu8Display_Righ_Dot_3)
  608. {
  609. Su8GetCode=Su8GetCode|0x80;  
  610. }
  611.     P0=Su8GetCode;
  612. P1_0=1;  
  613. P1_1=1;  
  614. P1_2=0;  
  615. P1_3=1;         
  616. break;

  617.        case 4:
  618. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  619. if(1==vGu8Display_Righ_Dot_4)
  620. {
  621. Su8GetCode=Su8GetCode|0x80;  
  622. }
  623.     P0=Su8GetCode;
  624. P1_0=1;  
  625. P1_1=1;  
  626. P1_2=1;  
  627. P1_3=0;           
  628. break;

  629. }

  630. Su8ScanStep++;
  631. if(Su8ScanStep>4)
  632. {
  633. Su8ScanStep=1;
  634. }

  635. vGu8ScanTimerFlag=0;
  636. vGu16ScanTimerCnt=SCAN_TIME;  
  637. vGu8ScanTimerFlag=1;  
  638. }
  639. }

  640. void VoiceScan(void) //蜂鸣器的驱动函数
  641. {

  642.           static unsigned char Su8Lock=0;  

  643. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  644.           {
  645.                   if(0==Su8Lock)
  646.                   {
  647.                    Su8Lock=1;  
  648. BeepOpen();
  649.      }
  650.     else  
  651. {     

  652.                        vGu16BeepTimerCnt--;         

  653.                    if(0==vGu16BeepTimerCnt)
  654.                    {
  655.                            Su8Lock=0;     
  656. BeepClose();  
  657.                    }

  658. }
  659.           }         
  660. }

  661. void BeepOpen(void)
  662. {
  663. P3_4=0;  
  664. }

  665. void BeepClose(void)
  666. {
  667. P3_4=1;  
  668. }

  669. void T0_time() interrupt 1     
  670. {
  671. VoiceScan();    //蜂鸣器的驱动函数
  672. KeyScan();      //按键底层的驱动扫描函数
  673. DisplayScan();  //数码管底层的驱动扫描函数

  674. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  675. {
  676. vGu16ScanTimerCnt--;  //递减式的软件定时器
  677. }

  678. if(1==vGu8BlinkTimerFlag&&vGu16BlinkTimerCnt>0)   //数码管闪烁跳动的定时器
  679. {
  680. vGu16BlinkTimerCnt--;  //递减式的软件定时器
  681. }

  682. //每10ms就定时更新一次显示的软件定时器
  683. if(1==vGu8UpdateTimerFlag&&vGu16UpdateTimerCnt>0)  
  684. {
  685. vGu16UpdateTimerCnt--;  //递减式的软件定时器
  686. }

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

  692. TH0=0xfd;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  693. TL0=0x40;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  694. }

  695. void SystemInitial(void)
  696. {
  697. P0=0x00;
  698. P1_0=1;  
  699. P1_1=1;  
  700. P1_2=1;  
  701. P1_3=1;  

  702. TMOD=0x01;  
  703. TH0=0xfd;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  704. TL0=0x40;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  705. EA=1;      
  706. ET0=1;      
  707. TR0=1;      

  708. //上电初始化开机显示的窗口
  709. Gu8EditData_4=Gu32SetData_Countdown/10000%10; //分解成“十秒”个体
  710. Gu8EditData_3=Gu32SetData_Countdown/1000%10;  //分解成“个秒”个体
  711. Gu8EditData_2=Gu32SetData_Countdown/100%10; //分解成“百毫秒”个体
  712. Gu8EditData_1=Gu32SetData_Countdown/10%10;  //分解成“十毫秒”个体
  713.     Gu8Wd=1; //返回设置数据的窗口
  714.     Gu8WdUpdate=1;  //整屏更新一次显示

  715. }

  716. void Delay(unsigned long u32DelayTime)
  717. {
  718.     for(;u32DelayTime>0;u32DelayTime--);
  719. }

  720. void PeripheralInitial(void)
  721. {

  722. }
复制代码


本帖子中包含更多资源

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

x
| 2018-6-24 22:51 | 显示全部楼层
持续关注
| 2018-6-29 19:55 | 显示全部楼层
楼主加油!!!

学习学习....
 楼主 | 2018-7-1 12:36 | 显示全部楼层
第一百二十二节: 利用定时中断做的“时分秒”数显时钟。

【122.1   利用定时中断做的“时分秒”数显时钟。】
   
                上图122.1.1  数码管




                上图122.1.2  独立按键

         
                上图122.1.3  有源蜂鸣器

      本节的数显时钟小项目,意在人机界面程序框架的综合训练。程序功能如下:
     (1)只有“时分秒”,没有“年月日”。
     (2)平时时钟正常工作的时候,四位数码管的显示格式是这样的“HH.MM”,其中HH代表“时”,MM代表“分”,而中间的小数点“.”每隔一秒闪烁一次。
     (3)K1[设置键]与[切换窗口键]。当数码管“没有闪烁”时(处于正常工作模式),“长按”K1键则进入“闪烁模式”(修改时钟模式),“闪烁模式”一共有3个窗口,分别是“1-HH”,“2-MM”,“3-SS”。其中“HH”“MM”“SS”分别代表可修改的“时”“分”“秒”,它们处于“闪烁”的状态,代表可编辑。此时,“短按”K1按键代表[切换窗口键],可以使数码管在“1-HH”,“2-MM”,“3-SS”三个窗口之间依次切换。修改完毕后,只需“长按”K1键代表确定完成并且退出当前“闪烁模式”返回到时钟的“正常工作模式”。
     (4)K2[加键]。当数码管某位正在闪烁时,此时K2是[加键],按K2会使数据“自加1”。
     (5)K3[减键]。当数码管某位正在闪烁时,此时K3是[减键],按K3会使数据“自减1”。
     (6)处于“闪烁模式”时的3个窗口的数据范围。处于修改“时”的“1-HH”窗口时,HH的范围是:0到23;处于修改“分”的“2-MM”窗口时,MM的范围是:0到59;处于修改“秒”的“3-SS”窗口时,SS的范围是:0到59。
      代码如下:


  1. #include "REG52.H"  

  2. #define KEY_FILTER_TIME  25   
  3. #define KEY_LONG_TIME    500   

  4. #define SCAN_TIME  1   
  5. #define VOICE_TIME   50   
  6. #define BLINK_TIME   250   

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

  11. void KeyScan(void);  
  12. void KeyTask(void);  

  13. void VoiceScan(void);  
  14. void DisplayScan(void);  
  15. void DisplayTask(void);  
  16. void Wd1(void);   //窗口1。时钟正常工作的窗口“HH.MM”。小数点在闪烁跳动。
  17. void Wd2(void);   //窗口2。闪烁模式,修改“时”的“1-HH”的窗口。
  18. void Wd3(void);   //窗口3。闪烁模式,修改“分”的“2-MM”的窗口。
  19. void Wd4(void);   //窗口4。闪烁模式,修改“秒”的“3-SS”的窗口。

  20. void BeepOpen(void);   
  21. void BeepClose(void);

  22. sbit KEY_INPUT1=P2^2;  
  23. sbit KEY_INPUT2=P2^1;  
  24. sbit KEY_INPUT3=P2^0;  

  25. sbit P1_0=P1^0;  
  26. sbit P1_1=P1^1;  
  27. sbit P1_2=P1^2;  
  28. sbit P1_3=P1^3;

  29. sbit P3_4=P3^4;

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

  46. volatile unsigned char vGu8ScanTimerFlag=0;  
  47. volatile unsigned int vGu16ScanTimerCnt=0;  

  48. volatile unsigned char vGu8BeepTimerFlag=0;  
  49. volatile unsigned int vGu16BeepTimerCnt=0;  

  50. volatile unsigned char vGu8BlinkTimerFlag=0;   
  51. volatile unsigned int vGu16BlinkTimerCnt=0;  

  52. //时钟的软件定时器,注意,这里是unsigned long类型,范围是0到4294967295毫秒
  53. volatile unsigned char vGu8ClockTimerFlag=0;  
  54. volatile unsigned long vGu32ClockTimerCnt=0;  

  55. //时钟正常工作的时候,每500ms更新显示一次
  56. volatile unsigned char vGu8UpdateTimerFlag=0;
  57. volatile unsigned int vGu16UpdateTimerCnt=0;  

  58. unsigned char Gu8EditData_1=0;  //是中间变量,用于编辑窗口“1-HH”下的HH数据。
  59. unsigned char Gu8EditData_2=0;  //是中间变量,用于编辑窗口“2-MM”下的MM数据。
  60. unsigned char Gu8EditData_3=0;  //是中间变量,用于编辑窗口“3-SS”下的SS数据。

  61. unsigned char Gu8Wd=0;   //窗口选择变量。人机交互程序框架的支点。
  62. unsigned char Gu8WdUpdate=0;  //整屏更新变量。

  63. unsigned char Gu8PartUpdate_1=0;   //局部1的更新变量,
  64. unsigned char Gu8PartUpdate_2=0;   //局部2的更新变量
  65. unsigned char Gu8PartUpdate_3=0;   //局部3的更新变量,

  66. volatile unsigned char vGu8Display_Righ_4=0;  
  67. volatile unsigned char vGu8Display_Righ_3=0;  
  68. volatile unsigned char vGu8Display_Righ_2=0;  
  69. volatile unsigned char vGu8Display_Righ_1=0;  

  70. volatile unsigned char vGu8Display_Righ_Dot_4=0;  
  71. volatile unsigned char vGu8Display_Righ_Dot_3=1;    //开机默认保留显示2个小数点
  72. volatile unsigned char vGu8Display_Righ_Dot_2=0;  
  73. volatile unsigned char vGu8Display_Righ_Dot_1=0;  

  74. volatile unsigned char vGu8KeySec=0;  

  75. void main()
  76. {
  77. SystemInitial();            
  78. Delay(10000);               
  79. PeripheralInitial();      
  80.     while(1)  
  81. {  
  82. KeyTask();      //按键的任务函数
  83. DisplayTask();  //数码管显示的上层任务函数
  84.     }
  85. }

  86. void KeyTask(void)    //按键的任务函数
  87. {
  88. if(0==vGu8KeySec)
  89. {
  90. return;
  91. }

  92. switch(vGu8KeySec)
  93. {
  94.    case 1:     //按键K1的“短按”。在“闪烁模式”下切换数码管的窗口。
  95.         switch(Gu8Wd) //在某个窗口下
  96.         {
  97.             case 2:     //窗口2。修改“时”的“1-HH”窗口。
  98. Gu8Wd=3; //切换到窗口3的“2-MM”窗口
  99. Gu8WdUpdate=1; //整屏更新
  100.                       break;

  101.             case 3:     //窗口3。修改“分”的“2-MM”窗口。
  102. Gu8Wd=4; //切换到窗口4的“3-SS”窗口
  103. Gu8WdUpdate=1; //整屏更新
  104.                       break;

  105.             case 4:     //窗口4。修改“秒”的“3-SS”窗口。
  106. Gu8Wd=2; //切换到窗口2的“1-HH”窗口
  107. Gu8WdUpdate=1; //整屏更新
  108.                       break;
  109.             }

  110. vGu8BeepTimerFlag=0;  
  111. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  112. vGu8BeepTimerFlag=1;

  113. vGu8KeySec=0;  
  114. break;

  115.    case 2:     //按键K2[加键]

  116.         switch(Gu8Wd) //在某个窗口下
  117.         {
  118.             case 2:     //窗口2。修改“时”的“1-HH”窗口。
  119.                   if(Gu8EditData_1<23) //“时”的范围是0到23
  120. {
  121. Gu8EditData_1++;
  122. }
  123. Gu8PartUpdate_1=1;   //局部1更新显示
  124.                       break;

  125.             case 3:     //窗口3。修改“分”的“2-MM”窗口。
  126.                   if(Gu8EditData_2<59) //“分”的范围是0到59
  127. {
  128. Gu8EditData_2++;
  129. }
  130. Gu8PartUpdate_1=1;   //局部1更新显示
  131.                       break;

  132.             case 4:     //窗口4。修改“秒”的“3-SS”窗口。
  133.                   if(Gu8EditData_3<59) //“秒”的范围是0到59
  134. {
  135. Gu8EditData_3++;
  136. }
  137. Gu8PartUpdate_1=1;   //局部1更新显示
  138.                       break;
  139.             }

  140. vGu8BeepTimerFlag=0;  
  141. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  142. vGu8BeepTimerFlag=1;  

  143. vGu8KeySec=0;  
  144. break;

  145.    case 3:     //按键K3[减键]与[开始健]
  146.         switch(Gu8Wd) //在某个窗口下
  147.         {
  148.             case 2:     //窗口2。修改“时”的“1-HH”窗口。
  149.                   if(Gu8EditData_1>0)
  150. {
  151. Gu8EditData_1--;
  152. }
  153. Gu8PartUpdate_1=1;   //局部1更新显示
  154.                       break;

  155.             case 3:     //窗口3。修改“分”的“2-MM”窗口。
  156.                   if(Gu8EditData_2>0)
  157. {
  158. Gu8EditData_2--;
  159. }
  160. Gu8PartUpdate_1=1;   //局部1更新显示
  161.                       break;

  162.             case 4:     //窗口4。修改“秒”的“3-SS”窗口。
  163.                   if(Gu8EditData_3>0)
  164. {
  165. Gu8EditData_3--;
  166. }
  167. Gu8PartUpdate_1=1;   //局部1更新显示
  168.                       break;
  169.             }

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

  173. vGu8KeySec=0;  
  174. break;

  175.    case 4:     //K1按键的“长按”,具有进入和退出“闪烁模式”的功能。“退出”隐含“确定”

  176.         switch(Gu8Wd) //在某个窗口下
  177.         {
  178.            case 1:     //窗口1。时钟正常工作的窗口。   
  179.                 vGu8ClockTimerFlag=0;  //停止时钟的定时器

  180. Gu8EditData_1=vGu32ClockTimerCnt/3600000;  //分解成“时”个体
  181. Gu8EditData_2=vGu32ClockTimerCnt%3600000/60000;  //分解成“分”个体
  182. Gu8EditData_3=vGu32ClockTimerCnt%3600000%60000/1000;  //分解成“秒”个体

  183. Gu8Wd=2; //切换到窗口2的“1-HH”的闪烁窗口
  184. Gu8WdUpdate=1; //整屏更新
  185. break;

  186.            case 2:     //窗口2。修改时钟时间的“1-HH”的闪烁窗口  
  187.            case 3:     //窗口3。修改时钟时间的“2-MM”的闪烁窗口  
  188.            case 4:     //窗口4。修改时钟时间的“3-SS”的闪烁窗口  
  189. //把个体合并还原成当前时钟时间的数据
  190. vGu32ClockTimerCnt=Gu8EditData_1*3600000+Gu8EditData_2*60000+Gu8EditData_3*1000;
  191. vGu8ClockTimerFlag=1;  //启动时钟的定时器

  192. Gu8Wd=1; //切换到窗口1的正常工作的窗口
  193. Gu8WdUpdate=1; //整屏更新
  194. break;
  195. }

  196. vGu8BeepTimerFlag=0;  
  197. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  198. vGu8BeepTimerFlag=1;  

  199. vGu8KeySec=0;  
  200. break;

  201. }
  202. }

  203. void DisplayTask(void) //数码管显示的上层任务函数
  204. {
  205.   switch(Gu8Wd)  //以窗口选择Gu8Wd为支点,去执行对应的窗口显示函数。又一次用到switch语句
  206. {
  207.     case 1:
  208.         Wd1();   //窗口1。时钟正常运行的窗口
  209.         break;
  210.     case 2:
  211.         Wd2();   //窗口2。修改“时”的“1-HH”窗口
  212.         break;
  213.     case 3:
  214.         Wd3();   //窗口3。修改“分”的“2-MM”窗口
  215.         break;
  216.     case 4:
  217.         Wd4();   //窗口4。修改“秒”的“3-SS”窗口
  218.         break;
  219. }
  220. }

  221. void Wd1(void)   //窗口1。时钟正常运行的窗口
  222. {
  223. //需要借用的中间变量,用来拆分数据位。
  224. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  225. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量


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

  229. //属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  230. vGu8Display_Righ_Dot_4=0;  
  231. vGu8Display_Righ_Dot_3=1;   //保留显示2位小数点
  232. vGu8Display_Righ_Dot_2=0;  
  233. vGu8Display_Righ_Dot_1=0;  

  234. Gu8PartUpdate_1=1;  //局部1更新显示
  235. }

  236. if(1==Gu8PartUpdate_1) //局部1更新显示,更新显示一次数据和闪烁的的小数点
  237. {
  238. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  239. Su8Temp_4=vGu32ClockTimerCnt/3600000/10; //时的十位
  240. Su8Temp_3=vGu32ClockTimerCnt/3600000%10; //时的个位
  241. Su8Temp_2=vGu32ClockTimerCnt%3600000/60000/10; //分的十位
  242. Su8Temp_1=vGu32ClockTimerCnt%3600000/60000%10; //秒的个位

  243.           //小数点的闪烁
  244.           if(0==Su8BlinkFlag)
  245. {
  246. Su8BlinkFlag=1;
  247. vGu8Display_Righ_Dot_3=1;   //显示第2位小数点。

  248. }
  249.           else
  250. {
  251. Su8BlinkFlag=0;
  252. vGu8Display_Righ_Dot_3=0;   //不显示第2位小数点
  253. }

  254. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  255. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量
  256. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  257. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  258. }

  259. if(0==vGu16UpdateTimerCnt)  //每隔500ms就更新显示一次数据和闪烁的的小数点
  260. {
  261. vGu8UpdateTimerFlag=0;
  262.     vGu16UpdateTimerCnt=500;  //重设定时器的定时时间
  263. vGu8UpdateTimerFlag=1;

  264. Gu8PartUpdate_1=1;  //局部1更新显示
  265. }

  266. }

  267. void Wd2(void)   //窗口2。修改“时”的“1-HH”窗口。
  268. {
  269. //需要借用的中间变量,用来拆分数据位。
  270. static unsigned char Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  271. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量


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

  275. //属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  276. vGu8Display_Righ_4=1;   //显示数字“1”
  277. vGu8Display_Righ_3=11;  //显示横杠“-”

  278. vGu8Display_Righ_Dot_4=0;  
  279. vGu8Display_Righ_Dot_3=0;   
  280. vGu8Display_Righ_Dot_2=0;  
  281. vGu8Display_Righ_Dot_1=0;  

  282. Gu8PartUpdate_1=1;  //局部1更新显示
  283. }

  284. if(1==Gu8PartUpdate_1) //局部1更新显示
  285. {
  286. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  287. Su8Temp_2=Gu8EditData_1/10;  //显示“时”的十位
  288. Su8Temp_1=Gu8EditData_1%10;  //显示“时”的个位

  289. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  290. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  291. }


  292. if(0==vGu16BlinkTimerCnt)  //某位被选中的数码管跳动闪烁的定时器
  293. {
  294. vGu8BlinkTimerFlag=0;
  295.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  296. vGu8BlinkTimerFlag=1;

  297.     if(0==Su8BlinkFlag)  //两种状态的切换判断
  298. {
  299. Su8BlinkFlag=1;
  300. Su8Temp_2=10;  //10代表不显示
  301. Su8Temp_1=10;  //10代表不显示
  302. }
  303. else
  304. {
  305. Su8BlinkFlag=0;
  306.   Su8Temp_2=Gu8EditData_1/10;  //显示“时”的十位
  307. Su8Temp_1=Gu8EditData_1%10;  //显示“时”的个位

  308. }

  309. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  310. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  311. }
  312. }

  313. void Wd3(void)   //窗口3。修改“分”的“2-MM”窗口。
  314. {
  315. //需要借用的中间变量,用来拆分数据位。
  316. static unsigned char Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  317. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量

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

  321. //属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  322. vGu8Display_Righ_4=2;   //显示数字“2”
  323. vGu8Display_Righ_3=11;  //显示横杠“-”

  324. vGu8Display_Righ_Dot_4=0;  
  325. vGu8Display_Righ_Dot_3=0;   
  326. vGu8Display_Righ_Dot_2=0;  
  327. vGu8Display_Righ_Dot_1=0;  

  328. Gu8PartUpdate_1=1;  //局部1更新显示
  329. }

  330. if(1==Gu8PartUpdate_1) //局部1更新显示
  331. {
  332. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  333. Su8Temp_2=Gu8EditData_2/10;  //显示“分”的十位
  334. Su8Temp_1=Gu8EditData_2%10;  //显示“分”的个位

  335. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  336. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  337. }


  338. if(0==vGu16BlinkTimerCnt)  //某位被选中的数码管跳动闪烁的定时器
  339. {
  340. vGu8BlinkTimerFlag=0;
  341.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  342. vGu8BlinkTimerFlag=1;

  343.     if(0==Su8BlinkFlag)  //两种状态的切换判断
  344. {
  345. Su8BlinkFlag=1;
  346. Su8Temp_2=10;  //10代表不显示
  347. Su8Temp_1=10;  //10代表不显示
  348. }
  349. else
  350. {
  351. Su8BlinkFlag=0;
  352. Su8Temp_2=Gu8EditData_2/10;  //显示“分”的十位
  353. Su8Temp_1=Gu8EditData_2%10;  //显示“分”的个位
  354. }

  355. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  356. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  357. }
  358. }

  359. void Wd4(void)   //窗口4。修改“秒”的“3-SS”窗口。
  360. {
  361. //需要借用的中间变量,用来拆分数据位。
  362. static unsigned char Su8Temp_2,Su8Temp_1; //需要借用的中间变量
  363. static unsigned char Su8BlinkFlag=0;  //两种状态的切换判断的中间变量

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

  367. //属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  368. vGu8Display_Righ_4=3;   //显示数字“3”
  369. vGu8Display_Righ_3=11;  //显示横杠“-”

  370. vGu8Display_Righ_Dot_4=0;  
  371. vGu8Display_Righ_Dot_3=0;   
  372. vGu8Display_Righ_Dot_2=0;  
  373. vGu8Display_Righ_Dot_1=0;  

  374. Gu8PartUpdate_1=1;  //局部1更新显示
  375. }

  376. if(1==Gu8PartUpdate_1) //局部1更新显示
  377. {
  378. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  379. Su8Temp_2=Gu8EditData_3/10;  //显示“秒”的十位
  380. Su8Temp_1=Gu8EditData_3%10;  //显示“秒”的个位

  381. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  382. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  383. }


  384. if(0==vGu16BlinkTimerCnt)  //某位被选中的数码管跳动闪烁的定时器
  385. {
  386. vGu8BlinkTimerFlag=0;
  387.     vGu16BlinkTimerCnt=BLINK_TIME;  //重设定时器的定时时间
  388. vGu8BlinkTimerFlag=1;

  389.     if(0==Su8BlinkFlag)  //两种状态的切换判断
  390. {
  391. Su8BlinkFlag=1;
  392. Su8Temp_2=10;  //10代表不显示
  393. Su8Temp_1=10;  //10代表不显示
  394. }
  395. else
  396. {
  397. Su8BlinkFlag=0;
  398. Su8Temp_2=Gu8EditData_3/10;  //显示“秒”的十位
  399. Su8Temp_1=Gu8EditData_3%10;  //显示“秒”的个位
  400. }

  401. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  402. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  403. }
  404. }

  405. void KeyScan(void)  //按键底层的驱动扫描函数,放在定时中断函数里
  406. {
  407.    static unsigned char Su8KeyShortFlag=0;  //按键“短按”触发的标志   
  408.    static unsigned char Su8KeyLock1;
  409.    static unsigned int  Su16KeyCnt1;
  410.    static unsigned char Su8KeyLock2;
  411.    static unsigned int  Su16KeyCnt2;
  412.    static unsigned char Su8KeyLock3;
  413.    static unsigned int  Su16KeyCnt3;

  414.   //需要详细分析以下这段“短按”与“长按”代码的朋友,请参考第96节。
  415.    if(0!=KEY_INPUT1)
  416.    {
  417.       Su8KeyLock1=0;
  418.       Su16KeyCnt1=0;   
  419.       if(1==Su8KeyShortFlag)  
  420.       {
  421. Su8KeyShortFlag=0;   
  422. vGu8KeySec=1;    //触发K1的“短按”
  423. }  
  424.    }
  425.    else if(0==Su8KeyLock1)
  426.    {
  427.       Su16KeyCnt1++;

  428.       if(Su16KeyCnt1>=KEY_FILTER_TIME)
  429.       {
  430.             Su8KeyShortFlag=1;  
  431.       }

  432.       if(Su16KeyCnt1>=KEY_LONG_TIME)
  433.       {
  434.             Su8KeyLock1=1;      
  435. Su8KeyShortFlag=0;  
  436.             vGu8KeySec=4; //触发K1的“长按”
  437.       }
  438.    }

  439.    if(0!=KEY_INPUT2)
  440.    {
  441.       Su8KeyLock2=0;
  442.       Su16KeyCnt2=0;      
  443.    }
  444.    else if(0==Su8KeyLock2)
  445.    {
  446.       Su16KeyCnt2++;
  447.       if(Su16KeyCnt2>=KEY_FILTER_TIME)
  448.       {
  449.          Su8KeyLock2=1;  
  450.          vGu8KeySec=2;   
  451.       }
  452.    }

  453.    if(0!=KEY_INPUT3)
  454.    {
  455.       Su8KeyLock3=0;
  456.       Su16KeyCnt3=0;      
  457.    }
  458.    else if(0==Su8KeyLock3)
  459.    {
  460.       Su16KeyCnt3++;
  461.       if(Su16KeyCnt3>=KEY_FILTER_TIME)
  462.       {
  463.          Su8KeyLock3=1;  
  464.          vGu8KeySec=3;   
  465.       }
  466.    }

  467. }

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

  472. if(0==vGu16ScanTimerCnt)  
  473. {


  474.     P0=0x00;
  475. P1_0=1;  
  476. P1_1=1;  
  477. P1_2=1;  
  478. P1_3=1;  

  479.     switch(Su8ScanStep)
  480. {
  481.        case 1:
  482. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  483. if(1==vGu8Display_Righ_Dot_1)
  484. {
  485. Su8GetCode=Su8GetCode|0x80;  
  486. }
  487.     P0=Su8GetCode;
  488. P1_0=0;
  489. P1_1=1;  
  490. P1_2=1;  
  491. P1_3=1;            
  492. break;

  493.        case 2:
  494. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  495. if(1==vGu8Display_Righ_Dot_2)
  496. {
  497. Su8GetCode=Su8GetCode|0x80;  
  498. }
  499.     P0=Su8GetCode;
  500. P1_0=1;  
  501. P1_1=0;
  502. P1_2=1;
  503. P1_3=1;         
  504. break;

  505.        case 3:
  506. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  507. if(1==vGu8Display_Righ_Dot_3)
  508. {
  509. Su8GetCode=Su8GetCode|0x80;  
  510. }
  511.     P0=Su8GetCode;
  512. P1_0=1;  
  513. P1_1=1;  
  514. P1_2=0;  
  515. P1_3=1;         
  516. break;

  517.        case 4:
  518. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  519. if(1==vGu8Display_Righ_Dot_4)
  520. {
  521. Su8GetCode=Su8GetCode|0x80;  
  522. }
  523.     P0=Su8GetCode;
  524. P1_0=1;  
  525. P1_1=1;  
  526. P1_2=1;  
  527. P1_3=0;           
  528. break;

  529. }

  530. Su8ScanStep++;
  531. if(Su8ScanStep>4)
  532. {
  533. Su8ScanStep=1;
  534. }

  535. vGu8ScanTimerFlag=0;
  536. vGu16ScanTimerCnt=SCAN_TIME;  
  537. vGu8ScanTimerFlag=1;  
  538. }
  539. }

  540. void VoiceScan(void) //蜂鸣器的驱动函数
  541. {

  542.           static unsigned char Su8Lock=0;  

  543. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  544.           {
  545.                   if(0==Su8Lock)
  546.                   {
  547.                    Su8Lock=1;  
  548. BeepOpen();
  549.      }
  550.     else  
  551. {     

  552.                        vGu16BeepTimerCnt--;         

  553.                    if(0==vGu16BeepTimerCnt)
  554.                    {
  555.                            Su8Lock=0;     
  556. BeepClose();  
  557.                    }

  558. }
  559.           }         
  560. }

  561. void BeepOpen(void)
  562. {
  563. P3_4=0;  
  564. }

  565. void BeepClose(void)
  566. {
  567. P3_4=1;  
  568. }

  569. void T0_time() interrupt 1     
  570. {
  571. VoiceScan();    //蜂鸣器的驱动函数
  572. KeyScan();      //按键底层的驱动扫描函数
  573. DisplayScan();  //数码管底层的驱动扫描函数

  574. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  575. {
  576. vGu16ScanTimerCnt--;  //递减式的软件定时器
  577. }

  578. if(1==vGu8BlinkTimerFlag&&vGu16BlinkTimerCnt>0)   //数码管闪烁跳动的定时器
  579. {
  580. vGu16BlinkTimerCnt--;  //递减式的软件定时器
  581. }

  582. //在正常工作的窗口下,每500ms就定时更新一次显示的软件定时器
  583. if(1==vGu8UpdateTimerFlag&&vGu16UpdateTimerCnt>0)  
  584. {
  585. vGu16UpdateTimerCnt--;  //递减式的软件定时器
  586. }

  587. //时钟实际走的时间的软件定时器,注意,这里是递增式的软件定时器
  588. if(1==vGu8ClockTimerFlag)
  589. {
  590. vGu32ClockTimerCnt++;  //递增式的软件定时器
  591. if(vGu32ClockTimerCnt>=86400000) //86400000毫秒代表24时
  592. {
  593. vGu32ClockTimerCnt=0;
  594. }
  595. }

  596. TH0=0xfd;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  597. TL0=0x40;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  598. }

  599. void SystemInitial(void)
  600. {
  601. P0=0x00;
  602. P1_0=1;  
  603. P1_1=1;  
  604. P1_2=1;  
  605. P1_3=1;  

  606. TMOD=0x01;  
  607. TH0=0xfd;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  608. TL0=0x40;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  609. EA=1;      
  610. ET0=1;      
  611. TR0=1;      

  612. //上电初始化一些关键的数据

  613. Gu8Wd=1;   //窗口1。开机默认处于正常工作的窗口
  614. Gu8WdUpdate=1;  //整屏更新变量

  615. vGu8ClockTimerFlag=0;  
  616. vGu32ClockTimerCnt=43200000;  //43200000毫秒开机默认12:00点。12时就是43200000毫秒
  617. vGu8ClockTimerFlag=1;   //启动时钟的定时器

  618. //时钟正常工作的时候,每500ms更新显示一次
  619.     vGu16UpdateTimerCnt=500;  
  620. vGu8UpdateTimerFlag=1; //启动小数点闪烁的定时器

  621. }

  622. void Delay(unsigned long u32DelayTime)
  623. {
  624.     for(;u32DelayTime>0;u32DelayTime--);
  625. }

  626. void PeripheralInitial(void)
  627. {

  628. }
复制代码


本帖子中包含更多资源

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

x
| 2018-7-3 11:25 | 显示全部楼层
楼主的无私奉献佩服的五体投地
 楼主 | 2018-7-9 10:08 | 显示全部楼层
第一百二十三节: 一种能省去一个lock自锁变量的按键驱动程序。

【123.1   一种能省去一个lock自锁变量的按键驱动程序。】

       一位群友给我提到了一个按键的改进建议,能巧妙的省去一个lock自锁变量。这个建议引起了我对“变量的分工要专一,一个变量尽量只用在一类事物上,尽量不取巧兼容”的思考。

       第一种:带lock自锁变量,也是我一直在用的代码。
  1. if(0!=KEY_INPUT1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  2. {
  3.     Su8KeyLock1=0; //按键解锁
  4.     Su16KeyCnt1=0; //按键去抖动延时计数器清零,此行非常巧妙,是全场的亮点。      
  5. }
  6. else if(0==Su8KeyLock1)//有按键按下,且是第一次被按下。这行如果有疑问,请看92节的专题分析。
  7. {
  8.     Su16KeyCnt1++; //累加定时中断次数
  9.     if(Su16KeyCnt1>=KEY_FILTER_TIME) //滤波的“稳定时间”KEY_FILTER_TIME,长度是25ms。
  10.     {
  11.          Su8KeyLock1=1;  //按键的自锁,避免一直触发
  12.          vGu8KeySec=1;    //触发1号键
  13.     }
  14. }
复制代码


       第二种:省略掉一个lock自锁变量,群友提出的改进建议。
  1. if(0!=KEY_INPUT1)
  2. {
  3.     Su16KeyCnt1=0;
  4. }
  5. else if(Su16KeyCnt1<KEY_FILTER_TIME) //巧妙的利用了Su16KeyCnt1等于滤波时间时,只执行一次
  6. {
  7.     Su16KeyCnt1++;
  8.     if(KEY_FILTER_TIME==Su16KeyCnt1) //巧妙的利用了Su16KeyCnt1等于滤波时间时,只执行一次
  9.     {
  10.          vGu8KeySec=1;   
  11.     }
  12. }
复制代码


分析:
       不得不佩服群友的智慧,第二种改进后看起来非常巧妙,犹如蜻蜓点水般轻盈洒脱。但是,为此代码狂欢片刻后,我又有了新的思考和看法。“计时器Su16KeyCnt1”和“自锁变量Su8KeyLock1”是两个不同的事物,是两个不同的范畴,就应该用两个不同的变量进行区分。如果逞一时之巧,把两种不同范畴的事物巧妙合并成一个变量,势必会导致程序的“易读性”和“后续维护的可扩展性”大打折扣。“自锁变量Su8KeyLock1”真的是可有可无吗?假设,如果“计时器Su16KeyCnt1”的消抖时间KEY_FILTER_TIME要求等于0,那么第二种改进后的代码立刻暴露出了问题,行不通。而第一种代码,因为有“自锁变量Su8KeyLock1”的存在,即使消抖时间KEY_FILTER_TIME等于0,也不影响代码功能的完整性,因为第一种代码的理念是“自锁与计时器是两种不同的功能范畴,用两个不同的变量进行分开隔离,各自管理两种不同的事物,计时器即使为0也不影响代码本该有的自锁功能”。通过此例子,给初学者一个建议,在代码的“队形感,易读性,扩展性,分类清晰”和“巧妙,节省代码”两者之间,建议大家优先考虑“队形感,易读性,扩展性,分类清晰”,追求一种原则上的“工整,不出奇兵,扎硬寨,打呆仗,步步为营”,这样阵脚不易乱,能走得更远,驾驭更多千军万马的代码。

本帖子中包含更多资源

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

x
| 2018-7-14 22:16 | 显示全部楼层
谢谢你的讲解,比任何书籍都讲得清楚,不管是初学者,还是提高者都适合。62岁的我也跟着你学习单片机。
 楼主 | 2018-7-16 10:29 | 显示全部楼层
第一百二十四节: 数显仪表盘显示“速度、方向、计数器”的跑马灯。

【124.1   数显仪表盘显示“速度、方向、计数器”的跑马灯。】
   
                上图124.1.1  数码管




                上图124.1.2  独立按键

        
                上图124.1.3  有源蜂鸣器


     
                上图124.1.4  LED电路

       本节小项目,意在“人机界面”与“过程控制”如何关联的练习。
       程序功能如下:
      (1)数码管显示的格式是“S.D.CC”。其中S是代表3档速度,能显示的数字范围是“1、2、3”,分别代表“慢、中、快”3档速度。D代表方向,往右跑显示符号“r”(right的首字母),往左跑显示符号“L”(Left的首字母)。CC代表计数器,跑马灯每跑完一次,计数器自动加1,范围是0到99。
      (2)【速度】按键K1。每按一次【速度】按键K1,速度档位显示的数字在“1、2、3”之间切换。
      (3)【方向】按键K2。跑马灯上电后默认处于“往右跑”的方向,默认显示字符“r”。每按一次【方向】按键K2,跑马灯就在“往右跑”与“往左跑”两个方向之间切换,显示的字符在“r、L”之间切换。
      (4)【启动暂停】按键K3。上电后,按下【启动暂停】按键K3启动之后,跑马灯处于“启动”状态,4个LED灯挨个依次循环的变亮,给人“跑”起来的感觉,此时再按一次【启动暂停】按键K3,则跑马灯处于“暂停”状态,接着又按一次【启动暂停】按键K3,跑马灯又变回“启动”状态。因此,【启动暂停】按键K3是专门用来切换“启动”和“暂停”这两种状态。
       代码如下:

  1. #include "REG52.H"  

  2. #define KEY_FILTER_TIME  25   

  3. #define SCAN_TIME  1   
  4. #define VOICE_TIME   50   

  5. #define RUN_TIME_SLOW    500   //“慢”档速度的时间参数
  6. #define RUN_TIME_MIDDLE  300   //“中”档速度的时间参数
  7. #define RUN_TIME_FAST    100   //“快”档速度的时间参数

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

  12. void KeyScan(void);  
  13. void KeyTask(void);  
  14. void RunTask(void);   //跑马灯的任务函数

  15. void VoiceScan(void);  
  16. void DisplayScan(void);  
  17. void DisplayTask(void);  
  18. void Wd1(void);   //窗口1。
  19. void BeepOpen(void);   
  20. void BeepClose(void);

  21. sbit KEY_INPUT1=P2^2;  
  22. sbit KEY_INPUT2=P2^1;  
  23. sbit KEY_INPUT3=P2^0;  

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

  28. sbit P3_4=P3^4;

  29. //4个跑马灯的输出口
  30. sbit P1_4=P1^4;  
  31. sbit P1_5=P1^5;  
  32. sbit P1_6=P1^6;  
  33. sbit P3_3=P3^3;  


  34. //数码管转换表
  35. code unsigned char Cu8DigTable[]=
  36. {
  37. 0x3f,  //0       序号0
  38. 0x06,  //1       序号1
  39. 0x5b,  //2       序号2
  40. 0x4f,  //3       序号3
  41. 0x66,  //4       序号4
  42. 0x6d,  //5       序号5
  43. 0x7d,  //6       序号6
  44. 0x07,  //7       序号7
  45. 0x7f,  //8       序号8
  46. 0x6f,  //9       序号9
  47. 0x00,  //不显示  序号10
  48. 0x40,  //横杠-   序号11
  49. 0x38,  //字符L   序号12
  50. 0x70,  //字符r   序号13
  51. };

  52. volatile unsigned char vGu8ScanTimerFlag=0;  
  53. volatile unsigned int vGu16ScanTimerCnt=0;  

  54. volatile unsigned char vGu8BeepTimerFlag=0;  
  55. volatile unsigned int vGu16BeepTimerCnt=0;  

  56. unsigned char Gu8Wd=0;   //窗口选择变量。人机交互程序框架的支点。
  57. unsigned char Gu8WdUpdate=0;  //整屏更新变量。

  58. unsigned char Gu8PartUpdate_1=0;   //局部1的更新变量,
  59. unsigned char Gu8PartUpdate_2=0;   //局部2的更新变量
  60. unsigned char Gu8PartUpdate_3=0;   //局部3的更新变量,

  61. volatile unsigned char vGu8Display_Righ_4=0;  
  62. volatile unsigned char vGu8Display_Righ_3=0;  
  63. volatile unsigned char vGu8Display_Righ_2=0;  
  64. volatile unsigned char vGu8Display_Righ_1=0;  

  65. volatile unsigned char vGu8Display_Righ_Dot_4=1;  //需要显示的小数点
  66. volatile unsigned char vGu8Display_Righ_Dot_3=1;  //需要显示的小数点
  67. volatile unsigned char vGu8Display_Righ_Dot_2=0;  
  68. volatile unsigned char vGu8Display_Righ_Dot_1=0;  

  69. volatile unsigned char vGu8KeySec=0;  

  70. unsigned char Gu8RunCounter=0;    //计数器,范围是0到99

  71. unsigned char Gu8RunStep=0; //运行的步骤
  72. unsigned char Gu8RunStart=0;      //控制跑马灯启动的总开关

  73. unsigned char Gu8RunStatus=0;     //标识跑马灯当前的状态。0代表停止,1代表启动,2代表暂停。
  74. unsigned char Gu8RunDirection=0;  //标识跑马灯当前的方向。0代表往右跑,1代表往左跑。
  75. unsigned char Gu8RunSpeed=1;      //当前的速度档位。1代表“慢”,2代表“中”,3代表“快”。
  76. unsigned int  Gu16RunSpeedTimeDate=0; //承接各速度档位的时间参数的变量

  77. volatile unsigned char vGu8RunTimerFlag=0;   //用于控制跑马灯跑动速度的定时器
  78. volatile unsigned int vGu16RunTimerCnt=0;  

  79. void main()
  80. {
  81. SystemInitial();            
  82. Delay(10000);               
  83. PeripheralInitial();      
  84.     while(1)  
  85. {  
  86. KeyTask();      //按键的任务函数
  87. DisplayTask();  //数码管显示的上层任务函数
  88. RunTask();    //跑马灯的任务函数
  89.     }
  90. }

  91. void RunTask(void)    //跑马灯的任务函数,放在主函数内
  92. {
  93. if(0==Gu8RunStart) //如果是停止的状态
  94. {
  95. return;  //如果是停止的状态,退出当前函数,不扫描余下代码。
  96. }

  97. switch(Gu8RunStep) //屡见屡爱的switch又来了
  98. {
  99.    case 0:
  100. vGu8RunTimerFlag=0;   
  101. vGu16RunTimerCnt=0;  //定时器清零
  102.         Gu8RunStep=1;  //切换到下一步,启动

  103.        break;
  104.    case 1:
  105.        if(1==Gu8RunStatus&&0==vGu16RunTimerCnt) //当前处于“启动”状态,并且定时器等于0
  106. {
  107. P1_4=0;   //第1个灯亮
  108. P1_5=1;   //第2个灯灭
  109. P1_6=1;   //第3个灯灭
  110. P3_3=1;   //第4个灯灭

  111.     vGu8RunTimerFlag=0;   
  112. vGu16RunTimerCnt=Gu16RunSpeedTimeDate; //速度时间参数变量的大小,决定了速度
  113. vGu8RunTimerFlag=1;   //启动定时器

  114. //灵活切换“步骤变量”
  115. if(0==Gu8RunDirection) //往右跑
  116. {
  117.                Gu8RunStep=2;
  118. }
  119. else  //往左跑
  120. {
  121.     if(Gu8RunCounter<99)
  122. {
  123. Gu8RunCounter++; //往左边跑完一次,运行的计数器自加1
  124. }
  125.     Gu8PartUpdate_3=1;   //局部3的更新变量,更新显示计数器

  126.                 Gu8RunStep=4;
  127. }

  128. }

  129.        break;
  130.    case 2:
  131.        if(1==Gu8RunStatus&&0==vGu16RunTimerCnt) //当前处于“启动”状态,并且定时器等于0
  132. {
  133. P1_4=1;   //第1个灯灭
  134. P1_5=0;   //第2个灯亮
  135. P1_6=1;   //第3个灯灭
  136. P3_3=1;   //第4个灯灭

  137.     vGu8RunTimerFlag=0;   
  138. vGu16RunTimerCnt=Gu16RunSpeedTimeDate; //速度时间参数变量的大小,决定了速度
  139. vGu8RunTimerFlag=1;   //启动定时器

  140. //灵活切换“步骤变量”
  141. if(0==Gu8RunDirection) //往右跑
  142. {
  143.                Gu8RunStep=3;
  144. }
  145. else  //往左跑
  146. {
  147.                Gu8RunStep=1;
  148. }
  149. }

  150.        break;
  151.    case 3:
  152.        if(1==Gu8RunStatus&&0==vGu16RunTimerCnt) //当前处于“启动”状态,并且定时器等于0
  153. {
  154. P1_4=1;   //第1个灯灭
  155. P1_5=1;   //第2个灯灭
  156. P1_6=0;   //第3个灯亮
  157. P3_3=1;   //第4个灯灭

  158.     vGu8RunTimerFlag=0;   
  159. vGu16RunTimerCnt=Gu16RunSpeedTimeDate; //速度时间参数变量的大小,决定了速度
  160. vGu8RunTimerFlag=1;   //启动定时器

  161. //灵活切换“步骤变量”
  162. if(0==Gu8RunDirection) //往右跑
  163. {
  164.                Gu8RunStep=4;
  165. }
  166. else  //往左跑
  167. {
  168.                Gu8RunStep=2;
  169. }
  170. }

  171.        break;
  172.    case 4:
  173.        if(1==Gu8RunStatus&&0==vGu16RunTimerCnt) //当前处于“启动”状态,并且定时器等于0
  174. {
  175. P1_4=1;   //第1个灯灭
  176. P1_5=1;   //第2个灯灭
  177. P1_6=1;   //第3个灯灭
  178. P3_3=0;   //第4个灯亮

  179.     vGu8RunTimerFlag=0;   
  180. vGu16RunTimerCnt=Gu16RunSpeedTimeDate; //速度时间参数变量的大小,决定了速度
  181. vGu8RunTimerFlag=1;   //启动定时器

  182. //灵活切换“步骤变量”
  183. if(0==Gu8RunDirection) //往右跑
  184. {
  185.     if(Gu8RunCounter<99)
  186. {
  187. Gu8RunCounter++; //往右边跑完一次,运行的计数器自加1
  188. }
  189.     Gu8PartUpdate_3=1;   //局部3的更新变量,更新显示计数器

  190.                Gu8RunStep=1;
  191. }
  192. else  //往左跑
  193. {
  194.                Gu8RunStep=3;
  195. }
  196. }

  197.        break;

  198. }

  199. }

  200. void KeyTask(void)    //按键的任务函数
  201. {
  202. if(0==vGu8KeySec)
  203. {
  204. return;
  205. }

  206. switch(vGu8KeySec)
  207. {
  208.    case 1:     //【速度】按键K1
  209.         switch(Gu8Wd) //在某个窗口下
  210.         {
  211.             case 1:     //窗口1。
  212.                   //每按一次K1按键,Gu8RunSpeed就在1、2、3三者之间切换,
  213. //并且根据Gu8RunSpeed的数值,对Gu16RunSpeedTimeDate赋值
  214. //不同的速度时间参数,从而控制速度档位。

  215.                   if(1==Gu8RunSpeed)
  216. {
  217. Gu8RunSpeed=2;  //“中”档
  218. Gu16RunSpeedTimeDate=RUN_TIME_MIDDLE; //赋值“中”档的时间参数
  219. }
  220.                   else if(2==Gu8RunSpeed)
  221. {
  222. Gu8RunSpeed=3;   //“快”档
  223. Gu16RunSpeedTimeDate=RUN_TIME_FAST; //赋值“快”档的时间参数
  224. }
  225. else
  226. {
  227. Gu8RunSpeed=1;   //“慢”档
  228. Gu16RunSpeedTimeDate=RUN_TIME_SLOW; //赋值“慢”档的时间参数
  229. }

  230. Gu8PartUpdate_1=1;   //局部1的更新变量,更新显示“速度”

  231. vGu8BeepTimerFlag=0;  
  232. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  233. vGu8BeepTimerFlag=1;
  234.                       break;
  235.             }

  236. vGu8KeySec=0;  
  237. break;

  238.    case 2:     //【方向】按键K2

  239.         switch(Gu8Wd) //在某个窗口下
  240.         {
  241.             case 1:     //窗口1。
  242.                   //每按一次K2按键,Gu8RunDirection就在0和1之间切换,从而控制方向
  243.                   if(0==Gu8RunDirection)
  244. {
  245. Gu8RunDirection=1;
  246. }
  247. else
  248. {
  249. Gu8RunDirection=0;
  250. }

  251. Gu8PartUpdate_2=1;   //局部2更新显示,更新显示“方向”

  252. vGu8BeepTimerFlag=0;  
  253. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  254. vGu8BeepTimerFlag=1;  
  255.                       break;
  256.             }
  257. vGu8KeySec=0;  
  258. break;

  259.    case 3:     //【启动暂停】按键K3
  260.         switch(Gu8Wd) //在某个窗口下
  261.         {
  262.             case 1:     //窗口1。
  263.                   if(0==Gu8RunStatus) //当跑马灯处于“停止”状态时
  264.                   {
  265.                       Gu8RunStep=0;    //运行步骤从0开始
  266. Gu8RunStart=1;   //总开关“打开”。
  267. Gu8RunStatus=1;  //状态切换到“启动”状态
  268. }
  269.                   else if(1==Gu8RunStatus) //当跑马灯处于“启动”状态时
  270.                   {
  271. Gu8RunStatus=2;  //状态切换到“暂停”状态
  272. }
  273.                   else  //当跑马灯处于“暂停”状态时
  274.                   {
  275. Gu8RunStatus=1;  //状态切换到“启动”状态
  276. }

  277. vGu8BeepTimerFlag=0;  
  278. vGu16BeepTimerCnt=VOICE_TIME;  //蜂鸣器发出“滴”一声
  279. vGu8BeepTimerFlag=1;  
  280.                       break;
  281.             }

  282. vGu8KeySec=0;  
  283. break;
  284. }
  285. }

  286. void DisplayTask(void) //数码管显示的上层任务函数
  287. {
  288.   switch(Gu8Wd)  //以窗口选择Gu8Wd为支点,去执行对应的窗口显示函数。又一次用到switch语句
  289. {
  290.     case 1:
  291.         Wd1();   //窗口1。
  292.         break;
  293. }
  294. }

  295. void Wd1(void)   //窗口1。
  296. {
  297. //需要借用的中间变量,用来拆分数据位。
  298. static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量

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

  302. //属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。
  303. vGu8Display_Righ_Dot_4=1;  //显示小数点
  304. vGu8Display_Righ_Dot_3=1;  //显示小数点
  305. vGu8Display_Righ_Dot_2=0;  
  306. vGu8Display_Righ_Dot_1=0;  

  307. Gu8PartUpdate_1=1;  //局部1更新显示
  308. Gu8PartUpdate_2=1;  //局部2更新显示
  309. Gu8PartUpdate_3=1;  //局部3更新显示
  310. }

  311. if(1==Gu8PartUpdate_1) //局部1更新显示,速度
  312. {
  313. Gu8PartUpdate_1=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  314. Su8Temp_4=Gu8RunSpeed;

  315. vGu8Display_Righ_4=Su8Temp_4;  //过渡需要显示的数据到底层驱动变量
  316. }

  317. if(1==Gu8PartUpdate_2) //局部2更新显示,方向
  318. {
  319. Gu8PartUpdate_2=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  320. if(0==Gu8RunDirection) //往右跑
  321. {
  322. Su8Temp_3=13; //数码管的字模转换表序号13代表显示字符“r”
  323. }
  324. else
  325. {
  326. Su8Temp_3=12; //数码管的字模转换表序号12代表显示字符“L”
  327. }
  328. vGu8Display_Righ_3=Su8Temp_3;  //过渡需要显示的数据到底层驱动变量
  329. }

  330. if(1==Gu8PartUpdate_3) //局部3更新显示,计数器
  331. {
  332. Gu8PartUpdate_3=0; //及时清零,只更新一次显示即可,避免一直进来更新显示

  333. Su8Temp_2=Gu8RunCounter%100/10; //提取十位
  334. Su8Temp_1=Gu8RunCounter%10/1;   //提取个位

  335. vGu8Display_Righ_2=Su8Temp_2;  //过渡需要显示的数据到底层驱动变量
  336. vGu8Display_Righ_1=Su8Temp_1;  //过渡需要显示的数据到底层驱动变量
  337. }

  338. }


  339. void KeyScan(void)  //按键底层的驱动扫描函数,放在定时中断函数里
  340. {
  341.    static unsigned char Su8KeyLock1;
  342.    static unsigned int  Su16KeyCnt1;
  343.    static unsigned char Su8KeyLock2;
  344.    static unsigned int  Su16KeyCnt2;
  345.    static unsigned char Su8KeyLock3;
  346.    static unsigned int  Su16KeyCnt3;


  347.    if(0!=KEY_INPUT1)
  348.    {
  349.       Su8KeyLock1=0;
  350.       Su16KeyCnt1=0;   
  351.    }
  352.    else if(0==Su8KeyLock1)
  353.    {
  354.       Su16KeyCnt1++;
  355.       if(Su16KeyCnt1>=KEY_FILTER_TIME)
  356.       {
  357.             Su8KeyLock1=1;      
  358.             vGu8KeySec=1;
  359.       }
  360.    }

  361.    if(0!=KEY_INPUT2)
  362.    {
  363.       Su8KeyLock2=0;
  364.       Su16KeyCnt2=0;      
  365.    }
  366.    else if(0==Su8KeyLock2)
  367.    {
  368.       Su16KeyCnt2++;
  369.       if(Su16KeyCnt2>=KEY_FILTER_TIME)
  370.       {
  371.          Su8KeyLock2=1;  
  372.          vGu8KeySec=2;   
  373.       }
  374.    }

  375.    if(0!=KEY_INPUT3)
  376.    {
  377.       Su8KeyLock3=0;
  378.       Su16KeyCnt3=0;      
  379.    }
  380.    else if(0==Su8KeyLock3)
  381.    {
  382.       Su16KeyCnt3++;
  383.       if(Su16KeyCnt3>=KEY_FILTER_TIME)
  384.       {
  385.          Su8KeyLock3=1;  
  386.          vGu8KeySec=3;   
  387.       }
  388.    }

  389. }

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

  394. if(0==vGu16ScanTimerCnt)  
  395. {


  396.     P0=0x00;
  397. P1_0=1;  
  398. P1_1=1;  
  399. P1_2=1;  
  400. P1_3=1;  

  401.     switch(Su8ScanStep)
  402. {
  403.        case 1:
  404. Su8GetCode=Cu8DigTable[vGu8Display_Righ_1];

  405. if(1==vGu8Display_Righ_Dot_1)
  406. {
  407. Su8GetCode=Su8GetCode|0x80;  
  408. }
  409.     P0=Su8GetCode;
  410. P1_0=0;
  411. P1_1=1;  
  412. P1_2=1;  
  413. P1_3=1;            
  414. break;

  415.        case 2:
  416. Su8GetCode=Cu8DigTable[vGu8Display_Righ_2];
  417. if(1==vGu8Display_Righ_Dot_2)
  418. {
  419. Su8GetCode=Su8GetCode|0x80;  
  420. }
  421.     P0=Su8GetCode;
  422. P1_0=1;  
  423. P1_1=0;
  424. P1_2=1;
  425. P1_3=1;         
  426. break;

  427.        case 3:
  428. Su8GetCode=Cu8DigTable[vGu8Display_Righ_3];
  429. if(1==vGu8Display_Righ_Dot_3)
  430. {
  431. Su8GetCode=Su8GetCode|0x80;  
  432. }
  433.     P0=Su8GetCode;
  434. P1_0=1;  
  435. P1_1=1;  
  436. P1_2=0;  
  437. P1_3=1;         
  438. break;

  439.        case 4:
  440. Su8GetCode=Cu8DigTable[vGu8Display_Righ_4];
  441. if(1==vGu8Display_Righ_Dot_4)
  442. {
  443. Su8GetCode=Su8GetCode|0x80;  
  444. }
  445.     P0=Su8GetCode;
  446. P1_0=1;  
  447. P1_1=1;  
  448. P1_2=1;  
  449. P1_3=0;           
  450. break;

  451. }

  452. Su8ScanStep++;
  453. if(Su8ScanStep>4)
  454. {
  455. Su8ScanStep=1;
  456. }

  457. vGu8ScanTimerFlag=0;
  458. vGu16ScanTimerCnt=SCAN_TIME;  
  459. vGu8ScanTimerFlag=1;  
  460. }
  461. }

  462. void VoiceScan(void) //蜂鸣器的驱动函数
  463. {

  464.           static unsigned char Su8Lock=0;  

  465. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  466.           {
  467.                   if(0==Su8Lock)
  468.                   {
  469.                    Su8Lock=1;  
  470. BeepOpen();
  471.      }
  472.     else  
  473. {     

  474.                        vGu16BeepTimerCnt--;         

  475.                    if(0==vGu16BeepTimerCnt)
  476.                    {
  477.                            Su8Lock=0;     
  478. BeepClose();  
  479.                    }

  480. }
  481.           }         
  482. }

  483. void BeepOpen(void)
  484. {
  485. P3_4=0;  
  486. }

  487. void BeepClose(void)
  488. {
  489. P3_4=1;  
  490. }

  491. void T0_time() interrupt 1     
  492. {
  493. VoiceScan();    //蜂鸣器的驱动函数
  494. KeyScan();      //按键底层的驱动扫描函数
  495. DisplayScan();  //数码管底层的驱动扫描函数

  496. if(1==vGu8ScanTimerFlag&&vGu16ScanTimerCnt>0)  
  497. {
  498. vGu16ScanTimerCnt--;  
  499. }

  500. if(1==vGu8RunTimerFlag&&vGu16RunTimerCnt>0)  //用于控制跑马灯跑动速度的定时器
  501. {
  502. vGu16RunTimerCnt--;
  503. }

  504. TH0=0xfd;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  505. TL0=0x40;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  506. }

  507. void SystemInitial(void)
  508. {
  509. P0=0x00;
  510. P1_0=1;  
  511. P1_1=1;  
  512. P1_2=1;  
  513. P1_3=1;  

  514. TMOD=0x01;  
  515. TH0=0xfd;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  516. TL0=0x40;   //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms
  517. EA=1;      
  518. ET0=1;      
  519. TR0=1;      

  520. //上电初始化一些关键的数据

  521. Gu8Wd=1;   //窗口1。开机默认处于正常工作的窗口
  522. Gu8WdUpdate=1;  //整屏更新变量
  523. //跑马灯处于初始化的状态
  524. P1_4=0;   //第1个灯亮
  525. P1_5=1;   //第2个灯灭
  526. P1_6=1;   //第3个灯灭
  527. P3_3=1;   //第4个灯灭

  528. //根据当前的速度档位Gu8RunSpeed,来初始化速度时间参数Gu16RunSpeedTimeDate
  529. if(1==Gu8RunSpeed)
  530. {
  531. Gu16RunSpeedTimeDate=RUN_TIME_SLOW; //赋值“慢”档的时间参数
  532. }
  533. else if(2==Gu8RunSpeed)
  534. {
  535. Gu16RunSpeedTimeDate=RUN_TIME_MIDDLE; //赋值“中”档的时间参数
  536. }
  537. else
  538. {
  539. Gu16RunSpeedTimeDate=RUN_TIME_FAST; //赋值“快”档的时间参数
  540. }

  541. }

  542. void Delay(unsigned long u32DelayTime)
  543. {
  544.     for(;u32DelayTime>0;u32DelayTime--);
  545. }

  546. void PeripheralInitial(void)
  547. {

  548. }
复制代码


本帖子中包含更多资源

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

x
 楼主 | 2018-7-24 11:15 | 显示全部楼层
本帖最后由 jianhong_wu 于 2018-8-5 09:16 编辑

第一百二十五节: “双线”的肢体接触通信。

【125.1   “双线”的肢体接触通信。】

      芯片之间通信,都离不开“数据信号”和“时钟信号”,缺一不可。“数据信号”和“时钟信号”是什么关系,它们是怎样相互配合来实现通信的功能?其实原理也很简单。打个比喻,甲乙两个人,规定只能靠一只“手”和一只“脚”进行肢体接触的通信,他们之间如何传输数据?“手”可以产生“两种”状态“握紧”和“松开”,“脚”可以产生“一种”状态“踢一脚”。他们之间约定,甲发送数据给乙,乙每被甲“踢一脚”就去记录一次手的状态是“握紧”还是“松开”,“握紧”代表二进制的0,“松开”代表二进制的1,这样,如果他们之间想传输一个字节的十六进制数据0x59,只需把十六进制的数据0x59展开成二进制01011001,从右到左(从低位到高位)以“位”为单位挨个发送,过程如下:
       第一次“踢一脚”:手的状态是“松开”,记录1。
       第二次“踢一脚”:手的状态是“握紧”,记录0。
       第三次“踢一脚”:手的状态是“握紧”,记录0。
       第四次“踢一脚”:手的状态是“松开”,记录1。
       第五次“踢一脚”:手的状态是“松开”,记录1。
       第六次“踢一脚”:手的状态是“握紧”,记录0。
       第七次“踢一脚”:手的状态是“松开”,记录1。
       第八次“踢一脚”:手的状态是“握紧”,记录0。
       上述肢体接触的通信过程,其实一只“手”就代表了一根“数据线”,可以产生高电平“1”和低电平“0”这两种状态,而一只“脚”代表了一根“时钟线”,但是“踢一脚”代表了“时钟线”上的一种什么状态呢?注意,“踢一脚”既不是高电平“1”也不是低电平“0”,而是瞬间只产生一次的“上升沿”或者“下降沿”。何谓“上升沿”何谓“下降沿”?“上升沿”是代表“时钟线从低电平跳变到高电平的瞬间”,“下升沿”是代表“时钟线从高电平跳变到低电平的瞬间”。“踢一脚”、“上升沿”、“下降沿”此三者都可以统一理解成“节拍”。
       芯片之间通信,“时钟信号”只需1个足矣,而“数据信号”却可以不止1个。1个“数据信号”往往叫“串行”通信,一个节拍只能传输1位数据。8个以上并列的“数据信号”往往叫“并行”通信,一个节拍能传输8位以上的数据。可见,并行的“数据信号”越多,传输的速率越快。
       常见的系统中,串口,IIC,SPI,USB,CAN这类都是“串行”通信。而32位单片机与外部的nandflash,norflash,sdram,sram这些芯片通信往往是“并行”通信,并行的数据信号多达8个16个甚至32个。
       本节标题之所以强调“双线”,是因为“手”代表数据线,“脚”代表时钟线,一共两条线因此为“双线”。现在把上述的肢体通信过程翻译成C语言代码,如下:

  1. sbit Hand_DATA=P2^6;  //手的数据线
  2. sbit Foot_CLK=P2^7;   //脚的时钟线

  3. void SendByte(unsiged char u8Data) //肢体接触通信发送一个字节的数据的发送函数
  4. {
  5.     static unsigned char i;
  6.     for(i=0;i<8;i++) //一个字节包含8个位数据,需要循环8次
  7.     {
  8.        if(0==(u8Data&0x01))  //根据数据的每一位状态,发送对应的位数据。
  9.        {
  10.             Hand_DATA=0;  //0代表“握紧”
  11.        }
  12.        else
  13.        {
  14.             Hand_DATA=1;  //1代表“松开”
  15.        }

  16.        Foot_CLK=1;
  17.        Delay(); //为产生均匀的脉冲节拍,时钟线的高电平先延时一会
  18.        Foot_CLK=0;  //从高电平跳变到低电平,产生瞬间的“下降沿”,代表“踢一脚”
  19.        Delay(); //为产生均匀的脉冲节拍,时钟线的低电平先延时一会

  20.        u8Data=u8Data>>1; //右移一位,为即将发送下一位做准备
  21.     }
  22. }
复制代码



本帖子中包含更多资源

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

x
| 2018-7-25 22:57 | 显示全部楼层
支持!
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式
我要创建版块 申请成为版主

论坛热帖

关闭

热门推荐上一条 /4 下一条

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