数显仪表盘显示“速度、方向、计数器”的跑马灯

2020-09-08 17:19:49  阅读 2117 次 评论 0 条

微信公众号:嵌入式开发圈
关注可了解更多的教程。问题或建议,请公众号留言;
如果你觉得本文对你有帮助,欢迎赞赏

本节小项目,意在“人机界面”与“过程控制”如何关联的练习。
程序功能如下:
(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  3#define KEY_FILTER_TIME25  4  5#define SCAN_TIME1  6#define VOICE_TIME50  7  8#define RUN_TIME_SLOW500 //“慢”档速度的时间参数  9#define RUN_TIME_MIDDLE300 //“中”档速度的时间参数  10#define RUN_TIME_FAST100 //“快”档速度的时间参数  11  12void T0_time();  13void SystemInitial(void) ;  14void Delay(unsigned long u32DelayTime) ;  15void PeripheralInitial(void) ;  16  17void KeyScan(void);  18void KeyTask(void);  19void RunTask(void); //跑马灯的任务函数  20  21void VoiceScan(void);  22void DisplayScan(void);  23void DisplayTask(void);  24void Wd1(void); //窗口1。  25void BeepOpen(void);  26void BeepClose(void);  27  28sbitKEY_INPUT1=P2^ 2;  29sbitKEY_INPUT2=P2^ 1;  30sbitKEY_INPUT3=P2^ 0;  31  32sbitP1_0=P1^ 0;  33sbitP1_1=P1^ 1;  34sbitP1_2=P1^ 2;  35sbitP1_3=P1^ 3;  36  37sbitP3_4=P3^ 4;  38  39//4个跑马灯的输出口  40sbitP1_4=P1^ 4;  41sbitP1_5=P1^ 5;  42sbitP1_6=P1^ 6;  43sbitP3_3=P3^ 3;  44  45  46//数码管转换表  47code unsigned char Cu8DigTable[]=  48{  49 0x3f, //0序号0  50 0x06, //1序号1  51 0x5b, //2序号2  52 0x4f, //3序号3  53 0x66, //4序号4  54 0x6d, //5序号5  55 0x7d, //6序号6  56 0x07, //7序号7  57 0x7f, //8序号8  58 0x6f, //9序号9  59 0x00, //不显示序号10  60 0x40, //横杠-序号11  61 0x38, //字符L序号12  62 0x70, //字符r序号13  63};  64  65volatile unsigned char vGu8ScanTimerFlag= 0;  66volatile unsigned int vGu16ScanTimerCnt= 0;  67  68volatile unsigned char vGu8BeepTimerFlag= 0;  69volatile unsigned int vGu16BeepTimerCnt= 0;  70  71unsigned char Gu8Wd= 0; //窗口选择变量。人机交互程序框架的支点。  72unsigned char Gu8WdUpdate= 0; //整屏更新变量。  73  74unsigned char Gu8PartUpdate_1= 0; //局部1的更新变量,  75unsigned char Gu8PartUpdate_2= 0; //局部2的更新变量  76unsigned char Gu8PartUpdate_3= 0; //局部3的更新变量,  77  78volatile unsigned char vGu8Display_Righ_4= 0;  79volatile unsigned char vGu8Display_Righ_3= 0;  80volatile unsigned char vGu8Display_Righ_2= 0;  81volatile unsigned char vGu8Display_Righ_1= 0;  82  83volatile unsigned char vGu8Display_Righ_Dot_4= 1; //需要显示的小数点  84volatile unsigned char vGu8Display_Righ_Dot_3= 1; //需要显示的小数点  85volatile unsigned char vGu8Display_Righ_Dot_2= 0;  86volatile unsigned char vGu8Display_Righ_Dot_1= 0;  87  88volatile unsigned char vGu8KeySec= 0;  89  90unsigned char Gu8RunCounter= 0; //计数器,范围是0到99  91  92unsigned char Gu8RunStep= 0; //运行的步骤  93unsigned char Gu8RunStart= 0; //控制跑马灯启动的总开关  94  95unsigned char Gu8RunStatus= 0; //标识跑马灯当前的状态。0代表停止,1代表启动,2代表暂停。  96unsigned char Gu8RunDirection= 0; //标识跑马灯当前的方向。0代表往右跑,1代表往左跑。  97unsigned char Gu8RunSpeed= 1; //当前的速度档位。1代表“慢”,2代表“中”,3代表“快”。  98unsigned int Gu16RunSpeedTimeDate= 0; //承接各速度档位的时间参数的变量  99 100volatile unsigned char vGu8RunTimerFlag= 0; //用于控制跑马灯跑动速度的定时器 101volatile unsigned int vGu16RunTimerCnt= 0; 102 103void main() 104{ 105 SystemInitial(); 106 Delay(10000); 107 PeripheralInitial(); 108 109 while(1) 110 { 111 KeyTask(); //按键的任务函数 112 DisplayTask(); //数码管显示的上层任务函数 113 RunTask(); //跑马灯的任务函数 114 } 115} 116 117void RunTask(void) //跑马灯的任务函数,放在主函数内 118{ 119 if(0 ==Gu8RunStart) //如果是停止的状态 120 { 121 return; //如果是停止的状态,退出当前函数,不扫描余下代码。 122 } 123 124 switch(Gu8RunStep) //屡见屡爱的switch又来了 125 { 126 case 0: 127 vGu8RunTimerFlag= 0; 128 vGu16RunTimerCnt= 0; //定时器清零 129 Gu8RunStep= 1; //切换到下一步,启动 130 131 break; 132 133 case 1: 134 if(1 ==Gu8RunStatus&& 0 ==vGu16RunTimerCnt) //当前处于“启动”状态,并且定时器等于0 135 { 136 P1_4= 0; //第1个灯亮 137 P1_5= 1; //第2个灯灭 138 P1_6= 1; //第3个灯灭 139 P3_3= 1; //第4个灯灭 140 141 vGu8RunTimerFlag= 0; 142 vGu16RunTimerCnt=Gu16RunSpeedTimeDate; //速度时间参数变量的大小,决定了速度 143 vGu8RunTimerFlag= 1; //启动定时器 144 145//灵活切换“步骤变量” 146 if(0 ==Gu8RunDirection) //往右跑 147 { 148 Gu8RunStep= 2; 149 } 150 else //往左跑 151 { 152 if(Gu8RunCounter99) 153 { 154 Gu8RunCounter++; //往左边跑完一次,运行的计数器自加1 155 } 156 157 Gu8PartUpdate_3= 1; //局部3的更新变量,更新显示计数器 158 159 Gu8RunStep= 4; 160 } 161 162 } 163 164 break; 165 166 case 2: 167 if(1 ==Gu8RunStatus&& 0 ==vGu16RunTimerCnt) //当前处于“启动”状态,并且定时器等于0 168 { 169 P1_4= 1; //第1个灯灭 170 P1_5= 0; //第2个灯亮 171 P1_6= 1; //第3个灯灭 172 P3_3= 1; //第4个灯灭 173 174 vGu8RunTimerFlag= 0; 175 vGu16RunTimerCnt=Gu16RunSpeedTimeDate; //速度时间参数变量的大小,决定了速度 176 vGu8RunTimerFlag= 1; //启动定时器 177 178//灵活切换“步骤变量” 179 if(0 ==Gu8RunDirection) //往右跑 180 { 181 Gu8RunStep= 3; 182 } 183 else //往左跑 184 { 185 Gu8RunStep= 1; 186 } 187 } 188 189 break; 190 191 case 3: 192 if(1 ==Gu8RunStatus&& 0 ==vGu16RunTimerCnt) //当前处于“启动”状态,并且定时器等于0 193 { 194 P1_4= 1; //第1个灯灭 195 P1_5= 1; //第2个灯灭 196 P1_6= 0; //第3个灯亮 197 P3_3= 1; //第4个灯灭 198 199 vGu8RunTimerFlag= 0; 200 vGu16RunTimerCnt=Gu16RunSpeedTimeDate; //速度时间参数变量的大小,决定了速度 201 vGu8RunTimerFlag= 1; //启动定时器 202 203//灵活切换“步骤变量” 204 if(0 ==Gu8RunDirection) //往右跑 205 { 206 Gu8RunStep= 4; 207 } 208 else //往左跑 209 { 210 Gu8RunStep= 2; 211 } 212 } 213 214 break; 215 216 case 4: 217 if(1 ==Gu8RunStatus&& 0 ==vGu16RunTimerCnt) //当前处于“启动”状态,并且定时器等于0 218 { 219 P1_4= 1; //第1个灯灭 220 P1_5= 1; //第2个灯灭 221 P1_6= 1; //第3个灯灭 222 P3_3= 0; //第4个灯亮 223 224 vGu8RunTimerFlag= 0; 225 vGu16RunTimerCnt=Gu16RunSpeedTimeDate; //速度时间参数变量的大小,决定了速度 226 vGu8RunTimerFlag= 1; //启动定时器 227 228//灵活切换“步骤变量” 229 if(0 ==Gu8RunDirection) //往右跑 230 { 231 if(Gu8RunCounter99) 232 { 233 Gu8RunCounter++; //往右边跑完一次,运行的计数器自加1 234 } 235 236 Gu8PartUpdate_3= 1; //局部3的更新变量,更新显示计数器 237 238 Gu8RunStep= 1; 239 } 240 else //往左跑 241 { 242 Gu8RunStep= 3; 243 } 244 } 245 246 break; 247 248 } 249 250} 251 252void KeyTask(void) //按键的任务函数 253{ 254 if(0 ==vGu8KeySec) 255 { 256 return; 257 } 258 259 switch(vGu8KeySec) 260 { 261 case 1: //【速度】按键K1 262 switch(Gu8Wd) //在某个窗口下 263 { 264 case 1: //窗口1。 265 266 //每按一次K1按键,Gu8RunSpeed就在1、2、3三者之间切换, 267//并且根据Gu8RunSpeed的数值,对Gu16RunSpeedTimeDate赋值 268//不同的速度时间参数,从而控制速度档位。 269 270 if(1 ==Gu8RunSpeed) 271 { 272 Gu8RunSpeed= 2; //“中”档 273 Gu16RunSpeedTimeDate=RUN_TIME_MIDDLE; //赋值“中”档的时间参数 274 } 275 else if(2 ==Gu8RunSpeed) 276 { 277 Gu8RunSpeed= 3; //“快”档 278 Gu16RunSpeedTimeDate=RUN_TIME_FAST; //赋值“快”档的时间参数 279 } 280 else 281 { 282 Gu8RunSpeed= 1; //“慢”档 283 Gu16RunSpeedTimeDate=RUN_TIME_SLOW; //赋值“慢”档的时间参数 284 } 285 286 Gu8PartUpdate_1= 1; //局部1的更新变量,更新显示“速度” 287 288 vGu8BeepTimerFlag= 0; 289 vGu16BeepTimerCnt=VOICE_TIME; //蜂鸣器发出“滴”一声 290 vGu8BeepTimerFlag= 1; 291 break; 292 } 293 294 vGu8KeySec= 0; 295 break; 296 297 case 2: //【方向】按键K2 298 299 switch(Gu8Wd) //在某个窗口下 300 { 301 case 1: //窗口1。 302 303 //每按一次K2按键,Gu8RunDirection就在0和1之间切换,从而控制方向 304 if(0 ==Gu8RunDirection) 305 { 306 Gu8RunDirection= 1; 307 } 308 else 309 { 310 Gu8RunDirection= 0; 311 } 312 313 Gu8PartUpdate_2= 1; //局部2更新显示,更新显示“方向” 314 315 vGu8BeepTimerFlag= 0; 316 vGu16BeepTimerCnt=VOICE_TIME; //蜂鸣器发出“滴”一声 317 vGu8BeepTimerFlag= 1; 318 break; 319 } 320 321 vGu8KeySec= 0; 322 break; 323 324 case 3: //【启动暂停】按键K3 325 switch(Gu8Wd) //在某个窗口下 326 { 327 case 1: //窗口1。 328 if(0 ==Gu8RunStatus) //当跑马灯处于“停止”状态时 329 { 330 Gu8RunStep= 0; //运行步骤从0开始 331 Gu8RunStart= 1; //总开关“打开”。 332 Gu8RunStatus= 1; //状态切换到“启动”状态 333 } 334 else if(1 ==Gu8RunStatus) //当跑马灯处于“启动”状态时 335 { 336 Gu8RunStatus= 2; //状态切换到“暂停”状态 337 } 338 else //当跑马灯处于“暂停”状态时 339 { 340 Gu8RunStatus= 1; //状态切换到“启动”状态 341 } 342 343 vGu8BeepTimerFlag= 0; 344 vGu16BeepTimerCnt=VOICE_TIME; //蜂鸣器发出“滴”一声 345 vGu8BeepTimerFlag= 1; 346 break; 347 } 348 349 vGu8KeySec= 0; 350 break; 351 } 352} 353 354void DisplayTask(void) //数码管显示的上层任务函数 355{ 356 switch(Gu8Wd) //以窗口选择Gu8Wd为支点,去执行对应的窗口显示函数。又一次用到switch语句 357 { 358 case 1: 359 Wd1(); //窗口1。 360 break; 361 } 362} 363 364void Wd1(void) //窗口1。 365{ 366//需要借用的中间变量,用来拆分数据位。 367 static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量 368 369 if(1 ==Gu8WdUpdate) //如果需要整屏更新 370 { 371 Gu8WdUpdate= 0; //及时清零,只更新一次显示即可,避免一直进来更新显示 372 373//属于静态数据,起“装饰”作用,切换窗口后只扫描一次的代码。 374 vGu8Display_Righ_Dot_4= 1; //显示小数点 375 vGu8Display_Righ_Dot_3= 1; //显示小数点 376 vGu8Display_Righ_Dot_2= 0; 377 vGu8Display_Righ_Dot_1= 0; 378 379 Gu8PartUpdate_1= 1; //局部1更新显示 380 Gu8PartUpdate_2= 1; //局部2更新显示 381 Gu8PartUpdate_3= 1; //局部3更新显示 382 } 383 384 if(1 ==Gu8PartUpdate_1) //局部1更新显示,速度 385 { 386 Gu8PartUpdate_1= 0; //及时清零,只更新一次显示即可,避免一直进来更新显示 387 388 Su8Temp_4=Gu8RunSpeed; 389 390 vGu8Display_Righ_4=Su8Temp_4; //过渡需要显示的数据到底层驱动变量 391 } 392 393 if(1 ==Gu8PartUpdate_2) //局部2更新显示,方向 394 { 395 Gu8PartUpdate_2= 0; //及时清零,只更新一次显示即可,避免一直进来更新显示 396 397 if(0 ==Gu8RunDirection) //往右跑 398 { 399 Su8Temp_3= 13; //数码管的字模转换表序号13代表显示字符“r” 400 } 401 else 402 { 403 Su8Temp_3= 12; //数码管的字模转换表序号12代表显示字符“L” 404 } 405 406 vGu8Display_Righ_3=Su8Temp_3; //过渡需要显示的数据到底层驱动变量 407 } 408 409 if(1 ==Gu8PartUpdate_3) //局部3更新显示,计数器 410 { 411 Gu8PartUpdate_3= 0; //及时清零,只更新一次显示即可,避免一直进来更新显示 412 413 Su8Temp_2=Gu8RunCounter% 100 / 10; //提取十位 414 Su8Temp_1=Gu8RunCounter% 10 / 1; //提取个位 415 416 vGu8Display_Righ_2=Su8Temp_2; //过渡需要显示的数据到底层驱动变量 417 vGu8Display_Righ_1=Su8Temp_1; //过渡需要显示的数据到底层驱动变量 418 } 419 420} 421 422 423void KeyScan(void) //按键底层的驱动扫描函数,放在定时中断函数里 424{ 425 static unsigned char Su8KeyLock1; 426 static unsigned int Su16KeyCnt1; 427 static unsigned char Su8KeyLock2; 428 static unsigned int Su16KeyCnt2; 429 static unsigned char Su8KeyLock3; 430 static unsigned int Su16KeyCnt3; 431 432 433 if(0 !=KEY_INPUT1) 434 { 435 Su8KeyLock1= 0; 436 Su16KeyCnt1= 0; 437 } 438 else if(0 ==Su8KeyLock1) 439 { 440 Su16KeyCnt1++; 441 442 if(Su16KeyCnt1>=KEY_FILTER_TIME) 443 { 444 Su8KeyLock1= 1; 445 vGu8KeySec= 1; 446 } 447 } 448 449 if(0 !=KEY_INPUT2) 450 { 451 Su8KeyLock2= 0; 452 Su16KeyCnt2= 0; 453 } 454 else if(0 ==Su8KeyLock2) 455 { 456 Su16KeyCnt2++; 457 458 if(Su16KeyCnt2>=KEY_FILTER_TIME) 459 { 460 Su8KeyLock2= 1; 461 vGu8KeySec= 2; 462 } 463 } 464 465 if(0 !=KEY_INPUT3) 466 { 467 Su8KeyLock3= 0; 468 Su16KeyCnt3= 0; 469 } 470 else if(0 ==Su8KeyLock3) 471 { 472 Su16KeyCnt3++; 473 474 if(Su16KeyCnt3>=KEY_FILTER_TIME) 475 { 476 Su8KeyLock3= 1; 477 vGu8KeySec= 3; 478 } 479 } 480 481} 482 483void DisplayScan(void) //数码管底层的驱动扫描函数,放在定时中断函数里 484{ 485 static unsigned char Su8GetCode; 486 static unsigned char Su8ScanStep= 1; 487 488 if(0 ==vGu16ScanTimerCnt) 489 { 490 491 492 P0= 0x00; 493 P1_0= 1; 494 P1_1= 1; 495 P1_2= 1; 496 P1_3= 1; 497 498 switch(Su8ScanStep) 499 { 500 case 1: 501 Su8GetCode=Cu8DigTable[vGu8Display_Righ_1]; 502 503 if(1 ==vGu8Display_Righ_Dot_1) 504 { 505 Su8GetCode=Su8GetCode| 0x80; 506 } 507 508 P0=Su8GetCode; 509 P1_0= 0; 510 P1_1= 1; 511 P1_2= 1; 512 P1_3= 1; 513 break; 514 515 case 2: 516 Su8GetCode=Cu8DigTable[vGu8Display_Righ_2]; 517 518 if(1 ==vGu8Display_Righ_Dot_2) 519 { 520 Su8GetCode=Su8GetCode| 0x80; 521 } 522 523 P0=Su8GetCode; 524 P1_0= 1; 525 P1_1= 0; 526 P1_2= 1; 527 P1_3= 1; 528 break; 529 530 case 3: 531 Su8GetCode=Cu8DigTable[vGu8Display_Righ_3]; 532 533 if(1 ==vGu8Display_Righ_Dot_3) 534 { 535 Su8GetCode=Su8GetCode| 0x80; 536 } 537 538 P0=Su8GetCode; 539 P1_0= 1; 540 P1_1= 1; 541 P1_2= 0; 542 P1_3= 1; 543 break; 544 545 case 4: 546 Su8GetCode=Cu8DigTable[vGu8Display_Righ_4]; 547 548 if(1 ==vGu8Display_Righ_Dot_4) 549 { 550 Su8GetCode=Su8GetCode| 0x80; 551 } 552 553 P0=Su8GetCode; 554 P1_0= 1; 555 P1_1= 1; 556 P1_2= 1; 557 P1_3= 0; 558 break; 559 560 } 561 562 Su8ScanStep++; 563 564 if(Su8ScanStep> 4) 565 { 566 Su8ScanStep= 1; 567 } 568 569 vGu8ScanTimerFlag= 0; 570 vGu16ScanTimerCnt=SCAN_TIME; 571 vGu8ScanTimerFlag= 1; 572 } 573} 574 575void VoiceScan(void) //蜂鸣器的驱动函数 576{ 577 578 static unsigned char Su8Lock= 0; 579 580 if(1 ==vGu8BeepTimerFlag&&vGu16BeepTimerCnt> 0) 581 { 582 if(0 ==Su8Lock) 583 { 584 Su8Lock= 1; 585 BeepOpen(); 586 } 587 else 588 { 589 590 vGu16BeepTimerCnt--; 591 592 if(0 ==vGu16BeepTimerCnt) 593 { 594 Su8Lock= 0; 595 BeepClose(); 596 } 597 598 } 599 } 600} 601 602void BeepOpen(void) 603{ 604 P3_4= 0; 605} 606 607void BeepClose(void) 608{ 609 P3_4= 1; 610} 611 612void T0_time() interrupt1 613{ 614 VoiceScan(); //蜂鸣器的驱动函数 615 KeyScan(); //按键底层的驱动扫描函数 616 DisplayScan(); //数码管底层的驱动扫描函数 617 618 if(1 ==vGu8ScanTimerFlag&&vGu16ScanTimerCnt> 0) 619 { 620 vGu16ScanTimerCnt--; 621 } 622 623 if(1 ==vGu8RunTimerFlag&&vGu16RunTimerCnt> 0) //用于控制跑马灯跑动速度的定时器 624 { 625 vGu16RunTimerCnt--; 626 } 627 628 TH0= 0xfd; //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms 629 TL0= 0x40; //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms 630} 631 632void SystemInitial(void) 633{ 634 P0= 0x00; 635 P1_0= 1; 636 P1_1= 1; 637 P1_2= 1; 638 P1_3= 1; 639 640 TMOD= 0x01; 641 TH0= 0xfd; //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms 642 TL0= 0x40; //此参数可根据具体的时间来修改,尽量确保每定时中断一次接近1ms 643 EA= 1; 644 ET0= 1; 645 TR0= 1; 646 647//上电初始化一些关键的数据 648 649 Gu8Wd= 1; //窗口1。开机默认处于正常工作的窗口 650 Gu8WdUpdate= 1; //整屏更新变量 651//跑马灯处于初始化的状态 652 P1_4= 0; //第1个灯亮 653 P1_5= 1; //第2个灯灭 654 P1_6= 1; //第3个灯灭 655 P3_3= 1; //第4个灯灭 656 657//根据当前的速度档位Gu8RunSpeed,来初始化速度时间参数Gu16RunSpeedTimeDate 658 if(1 ==Gu8RunSpeed) 659 { 660 Gu16RunSpeedTimeDate=RUN_TIME_SLOW; //赋值“慢”档的时间参数 661 } 662 else if(2 ==Gu8RunSpeed) 663 { 664 Gu16RunSpeedTimeDate=RUN_TIME_MIDDLE; //赋值“中”档的时间参数 665 } 666 else 667 { 668 Gu16RunSpeedTimeDate=RUN_TIME_FAST; //赋值“快”档的时间参数 669 } 670 671} 672 673void Delay(unsigned long u32DelayTime) 674{ 675 for(;u32DelayTime> 0;u32DelayTime--); 676} 677 678void PeripheralInitial(void) 679{ 680 681}

长期商务合作服务:

数显仪表盘显示“速度、方向、计数器”的跑马灯第1张-YMS黑板报

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本文地址:https://www.yms7.com/post/7820.html
版权声明:本文为原创文章,版权归 yms 所有,欢迎分享本文,转载请保留出处!

发表评论 取消回复


表情

还没有留言,还不快点抢沙发?