【51单片机】花式流水灯

简介: 【51单片机】花式流水灯

任务要求:

1、按键 1、2、3、4 按下,使 8 个 LED 实现下面对应的模式 1、 2、        3、4,上电默认每种模式流水灯的流转时间间隔为 500ms。

1)模式1:按照L1、L2……L8的顺序,从左到右循环点亮。

2)模式2:按照L8、L7……L1的顺序,从右刀座循环点亮。

3)模式3:从两边向中间点亮(  (L1,L8)->(L2,L7)->(L3,L6)->(L4,L5)  )

4)模式4:从中间向两边点亮(  (L4,L5)->(L3,L6)->(L2,L7)->(L1,L8)  )

2、按键5按下流水灯的流转时间间隔增加100ms,超过1200ms从400ms开始,用定时器控制时间

3、代码简洁,注释简单易懂。

实现思路:

程序大体框架如下图:

429e329ed0483f56bed339753663c954_99039352122742eb9d1768a35054be4c.png

还没学过Visio画图,第一次尝试,轻喷~

按键部分

我们先把按键放在定时器里刷新,识别到几号按键按下,就对应LED灯按照第几个模式点亮,按键1按下就是模式1,按键2按下就是模式2……模式1234转换可以用一个全局变量实现,我代码中本变量名称为Light_Mode

77bf54d300c43cb6bacebf8a6d93348c_bdebc99d175046eb96ceb3e9360c5363.png

主函数要循环判断Light_Mode变量的数值,所以写一个无参数无返回值的LED处理的函数,先根据按键返回值,给Light_Mode变量赋相应的状态值,再根据这个状态值,实现四种不同模式的点灯。

定时器+中断

定时时间10ms,因为51单片机定时时间上限大约是70ms,按键消抖的部分大概是10ms,所以按键在定时器中以10ms时间扫描一次它的状态,每10ms扫描一次它的状态,就会滤除硬件抖动的部分,LED要求的500ms定时,可以在这个10ms基础上累加计数,就是每10ms计数变量自加1,计数变量==50的时候,就是500ms了

要求2 要改变LED闪烁时间间隔 ,那就再定义一个全局变量 unsigned int  SpaceT = 500;

名称SpaceT,初值500ms

按键5 按一下SpaceT变量自加100,超过1200,给SpaceT赋值400

定时器0初始化代码

1. void Timer0_Init(void)   //1毫秒@11.0592MHz
2. {
3.  TMOD &= 0xF0;   //设置定时器模式
4.  TMOD |= 0x01;   //设置定时器模式
5.  TL0 = 0x66;   //设置定时初值
6.  TH0 = 0xFC;   //设置定时初值
7.  TF0 = 0;    //清除TF0标志
8.  TR0 = 1;    //定时器0开始计时
9.  ET0=1;
10.   EA=1;
11.   PT0=0;  
12. }

的中断定时器0

1. void Timer0_Routine() interrupt 1  //定时器0的中断函数
2. {
3.  static unsigned char Button; //按键扫描时间变量 10ms
4.  static unsigned int T0Count;
5. 
6.  TL0 = 0x66;   //设置定时初值
7.  TH0 = 0xFC;   //设置定时初值
8.  Button++;
9.  T0Count++;
10. 
11.   if(T0Count >= SpaceT){
12.     i++;
13.     T0Count = 0;  //软件复位
14.   }
15. 
16.   if(Button >= 10){
17.     MatrixKey_Loop();
18.     Button = 0;
19.   }
20. 
21. }

LED处理

5a275b6a453410199b797a58ce379436_44caf85568b447e2b3efc68a4d95f6e9.png

上图是普中科技的51开发板 原理图上说明P2端口对应了8个发光二极管,从P20~P27

如果想点亮LED1(图示D1),就写P2_0 = 0;

9a9e420fe8057b020fe8f8f4e5a713cb_fc045aff37414724a16c1337552fc1ae.png

或者对P2整个端口赋值,P2 = 0xFE; 十六进制的0xFE转化二进制后为1111 1110,只有第一个LED点亮。

如果想先点亮第二个LED,那就P2 = 0xFD;        0xFD代表1111 1101,只有第二个灯点亮

如果想实现流水灯的效果,那就给P2端口依次赋值如下八个数

1111 1110

1111 1101

1111 1011

1111 0111

1110 1111

1101 1111

1011 1111

0111 1111

不难发现,只有0向前移位,一次移动移位,由此可以想到位运算中的移位运算符

279789e0a8d98ce1b4d02358eceda90f_39bf6f0d26d64b08a2f3a991e7ec9e54.jpeg

参考书 C primer plus

书上指出,左移运算符,高位舍弃,低位补0(补0的那些位LED就会亮起),所以不符合我们的要求

由于高位舍弃,低位补0,如果我们找到和上面八个数相反的数,再给它取反,就得到了想要的效果

留点空余时间思考一下

……

我们可以对0x01 (0000 0001)移位,左移

左移1次 0000 0010 (高位舍弃,低位补0), 取反 1111 1101

左移2次 0000 0100                                       取反 1111 1011

……

4adbc352029fb7aa231d2e9e54c9743f_9201011b2d014998a30b78ba4ff49cdc.jpeg

变量 i 也要定义为全局变量,因为 i 要在中断函数里改变

1. for(i=0;i<8;)  //i变量在定时器里加加,控制闪烁时间间隔
2. {
3.  P2 = ~(0x01<<i);
4. }

模式2流水

1. for(i=0;i<8;)  //i变量在定时器里加加,控制闪烁时间间隔
2. {
3.  P2 = ~(0x80>>i);
4. 
5. }

模式3

1. for(i=0;i<4;)
2. {
3.  P2 = ~((0x01<<i) | (0x80>>i));//两边向中间 亮灯
4. }

模式4

1. for(i=0;i<4;)
2. {
3.  P2 = ~((0x10<<i) | (0x08>>i));
4. }

程序源码

f5547b6f4b796afb64b078bc4af90aa8_410764e85d0c4bc19475c8509c5d8f38.png

本题要求简单,所以我就建了两个模块化的代码

Timer0.c

1. #include <REGX52.H>
2. #include "Timer0.h"
3. 
4. 
5. //定时器0初始化模板  1毫秒@11.0592MHz
6. void Timer0_Init(void)   //1毫秒@11.0592MHz
7. {
8.  TMOD &= 0xF0;   //设置定时器模式
9.  TMOD |= 0x01;   //设置定时器模式
10.   TL0 = 0x66;   //设置定时初值
11.   TH0 = 0xFC;   //设置定时初值
12.   TF0 = 0;    //清除TF0标志
13.   TR0 = 1;    //定时器0开始计时
14.   ET0=1;
15.   EA=1;
16.   PT0=0;  
17. }

Timer0.h

1. #ifndef __TIMER0_H__
2. #define __TIMER0_H__
3. 
4. void Timer0_Init(void);    //1毫秒@11.0592MHz
5. 
6. #endif

MatrixKeyT.c

矩阵按键模块化的代码在之前的文章有发过,需要的童鞋们可以去翻阅一下

1. #include <REGX52.H>
2. #include "MatrixKeyT.h"
3. 
4. unsigned char Key_KeyNumber;
5. 
6. 
7. /**
8.   *@brief   名称:矩阵键盘扫描,获取按键键码
9.   *@param   参数:无  (放在主函数while里 赋值给一个 代表键码的变量)
10.   *@retval返回值:KeyNumber 0代表无按键按下,1~16代表键码
11.   */
12. unsigned char Key(void)
13. {
14.   unsigned char Temp = 0;
15.   Temp = Key_KeyNumber; //赋值给暂存变量
16.   Key_KeyNumber = 0;    //清零
17.   return Temp;      //返回暂存变量
18. }
19. 
20. 
21. /**
22. *@brief   名称:矩阵键盘读取按键键码, 内部函数(不需要外部调用的)
23.   *@param   参数:无
24.   *@retval返回值:KeyNumber 0代表无按键按下,1~16代表键码
25.   */
26. unsigned char MatrixKey_GetState()
27. {
28.   unsigned char KeyNumber=0;
29. 
30.   P1=0xFF;
31.    P1_3=0;
32.   if(P1_7==0 && P1_3==0){KeyNumber=1;}
33.   if(P1_6==0 && P1_3==0){KeyNumber=5;}
34.   if(P1_5==0 && P1_3==0){KeyNumber=9;}
35.   if(P1_4==0 && P1_3==0){KeyNumber=13;}
36. 
37.   P1=0xFF;
38.   P1_2=0;
39.   if(P1_7==0 && P1_2==0){KeyNumber=2;}
40.   if(P1_6==0 && P1_2==0){KeyNumber=6;}
41.   if(P1_5==0 && P1_2==0){KeyNumber=10;}
42.   if(P1_4==0 && P1_2==0){KeyNumber=14;}
43. 
44.   P1=0xFF;
45.   P1_1=0;
46.   if(P1_7==0 && P1_1==0){KeyNumber=3;}
47.   if(P1_6==0 && P1_1==0){KeyNumber=7;}
48.   if(P1_5==0 && P1_1==0){KeyNumber=11;}
49.   if(P1_4==0 && P1_1==0){KeyNumber=15;}
50. 
51.   P1=0xFF;
52.   P1_0=0;
53.   if(P1_7==0 && P1_0==0){KeyNumber=4;}
54.   if(P1_6==0 && P1_0==0){KeyNumber=8;}
55.   if(P1_5==0 && P1_0==0){KeyNumber=12;}
56.   if(P1_4==0 && P1_0==0){KeyNumber=16;}
57. 
58.   return KeyNumber;
59. }
60. 
61. 
62. void MatrixKey_Loop(void)
63. {
64.   static unsigned char NowState,LastState;
65.   LastState = NowState;       //按键状态更新
66.   NowState  = MatrixKey_GetState();   //获取当前按键状态
67.   if(LastState == 1 && NowState == 0) {Key_KeyNumber=1;}
68.   if(LastState == 2 && NowState == 0) {Key_KeyNumber=2;}
69.   if(LastState == 3 && NowState == 0) {Key_KeyNumber=3;}
70.   if(LastState == 4 && NowState == 0) {Key_KeyNumber=4;}
71.   if(LastState == 5 && NowState == 0) {Key_KeyNumber=5;}
72.   if(LastState == 6 && NowState == 0) {Key_KeyNumber=6;}
73.   if(LastState == 7 && NowState == 0) {Key_KeyNumber=7;}
74.   if(LastState == 8 && NowState == 0) {Key_KeyNumber=8;}
75.   if(LastState == 9 && NowState == 0) {Key_KeyNumber=9;}
76.   if(LastState ==10 && NowState == 0) {Key_KeyNumber=10;}
77.   if(LastState ==11 && NowState == 0) {Key_KeyNumber=11;}
78.   if(LastState ==12 && NowState == 0) {Key_KeyNumber=12;}
79.   if(LastState ==13 && NowState == 0) {Key_KeyNumber=13;}
80.   if(LastState ==14 && NowState == 0) {Key_KeyNumber=14;}
81.   if(LastState ==15 && NowState == 0) {Key_KeyNumber=15;}
82.   if(LastState ==16 && NowState == 0) {Key_KeyNumber=16;}
83. }
84.

MatrixKeyT.h

1. #ifndef __MATRIXKEYT_H__
2. #define __MATRIXKEYT_H__
3. 
4. unsigned char Key(void);
5. void MatrixKey_Loop(void);
6. 
7. #endif

main.c

1. #include <REGX52.H>
2. #include "Timer0.h"
3. #include "MatrixKeyT.h"
4. 
5. unsigned char i;//循环变量
6. unsigned char KeyNumer;
7. unsigned int  SpaceT = 500;//初值400ms
8. unsigned char Light_Mode;
9. 
10. void LED_Func();
11. 
12. void main()
13. { 
14.   Timer0_Init();
15. 
16.   while(1)
17.   {
18.     KeyNumer = Key();
19.     LED_Func();
20. 
21.   }
22. }
23. 
24. void LED_Func()
25. {
26.   if     (KeyNumer == 1) Light_Mode=1;
27.   else if(KeyNumer == 2) Light_Mode=2;
28.   else if(KeyNumer == 3) Light_Mode=3;
29.   else if(KeyNumer == 4) Light_Mode=4;
30.   else if(KeyNumer == 5)
31.   {
32.     SpaceT += 100;
33.   }
34.   if(SpaceT>1200)
35.     SpaceT = 400;
36. 
37.   if(Light_Mode == 1)
38.   {
39.     for(i=0;i<8;) //i变量在定时器里加加,控制闪烁时间间隔
40.     {
41.       P2 = ~(0x01<<i);
42. //        if(Light_Mode != 1) //for循环,按键键码数值改变,但是模式没及时改变,此处有个bug
43. //          break;  //要小写
44.     }
45.   }
46. 
47.   else if(Light_Mode == 2)
48.   {
49.     for(i=0;i<8;) //i变量在定时器里加加,控制闪烁时间间隔
50.     {
51.       P2 = ~(0x80>>i);
52. //        if(Light_Mode != 2)
53. //          break;
54.     }
55.   }
56. 
57.   else if(Light_Mode == 3)
58.   {
59.     for(i=0;i<4;)
60.     {
61.       P2 = ~((0x01<<i) | (0x80>>i));//两边向中间 亮灯
62.     }
63.   }
64. 
65.   else if(Light_Mode == 4)
66.   {
67.     for(i=0;i<4;)
68.     {
69.       P2 = ~((0x10<<i) | (0x08>>i));
70.     }
71.   }
72. }
73. 
74. void Timer0_Routine() interrupt 1  //定时器0的中断函数
75. {
76.   static unsigned char Button; //按键扫描时间变量 10ms
77.   static unsigned int T0Count;
78. 
79.   TL0 = 0x66;   //设置定时初值
80.   TH0 = 0xFC;   //设置定时初值
81.   Button++;
82.   T0Count++;
83. 
84.   if(T0Count >= SpaceT){
85.     i++;
86.     T0Count = 0;  //软件复位
87.   }
88. 
89.   if(Button >= 10){
90.     MatrixKey_Loop();
91.     Button = 0;
92.   }
93. 
94. }

LED流水实现部分有个BUG:因为我写的是for循环,虽然这时候按下按键,变量Light_Mode已经被重新赋值了,所以不能立刻跳出循环,执行下一个流水灯效果。之前有测试过跳出那个for循环,但是没有实现过,有兴趣的小伙伴可以尝试修改一下bug。

不过有一个解决方案,把每一个流水灯状态否存放在数组里,依次实现,这样就避免使用for循环~

补充一个模式1流水灯的方法

1. /*   void forward(void)函数说明
2. 第一次给P1端口赋值 0xfe = 1111 1110
3. 延时200ms
4. 1111 1110<<1    1111 1100     最后一个跟着亮了,给最后一位清零,即灭,“或”0x01,变成1111 1101 ,下次赋值本数
5. 第二次赋值后,延时200ms
6. 1111 1101<<1    1111 1010    也要把最后一位清零,|0x01,变成1111 1011…… 
7. */
8. 
9. void forward(void)
10. {
11.   unsigned char TempOut = 0xfe,j;
12.   for (j=0;j<8;j++)
13.   {
14.     P1 = TempOut;
15.     Delay_ms(200);
16.     TempOut = (TempOut << 1) | 0x01;
17. 
18.   }
19. }

小生文采拙劣,多多包涵!

欢迎大家讨论!


相关文章
|
2月前
|
数据格式
用C51单片机制作LED流水的灯
用C51单片机制作LED流水的灯
64 0
定时器+按键控制LED流水灯模式+定时器时钟——“51单片机”
定时器+按键控制LED流水灯模式+定时器时钟——“51单片机”
|
2月前
|
C语言
点亮一个LED+LED闪烁+LED流水灯——“51单片机”
点亮一个LED+LED闪烁+LED流水灯——“51单片机”
|
2月前
|
C语言
###51单片机学习-----如何通过C语言运用延时函数设计LED流水灯
###51单片机学习-----如何通过C语言运用延时函数设计LED流水灯
97 0
|
12月前
|
芯片
51单片机--点亮LED灯和流水灯
51单片机--点亮LED灯和流水灯
129 0
|
11月前
【单片机期中测试】4.按键不同时长控制流水灯
【单片机期中测试】4.按键不同时长控制流水灯
46 0
|
11月前
【单片机期中测试】3.按键控制流水灯循环
【单片机期中测试】3.按键控制流水灯循环
132 0
|
11月前
【单片机期中测试】1.简单的流水灯程序
【单片机期中测试】1.简单的流水灯程序
64 0
|
12月前
|
存储 调度
51单片机--定时器与按键控制流水灯模式
51单片机--定时器与按键控制流水灯模式
387 0
|
编译器
【51单片机】按键操作(单个灯闪烁&&流水灯)
按键操作(单个灯闪烁&&流水灯)
474 0