目录

任务要求:

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、代码简洁,注释简单易懂。

实现思路:

按键部分

定时器部分

LED处理部分

函数部分

程序源码


任务要求:

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、代码简洁,注释简单易懂。

实现思路:

程序大体框架如下图:

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

按键部分

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

主函数要循环判断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初始化代码

void Timer0_Init(void)       //1毫秒@11.0592MHz
{TMOD &= 0xF0;     //设置定时器模式TMOD |= 0x01;     //设置定时器模式TL0 = 0x66;       //设置定时初值TH0 = 0xFC;        //设置定时初值TF0 = 0;       //清除TF0标志TR0 = 1;      //定时器0开始计时ET0=1;EA=1;PT0=0;
}

定时器0的中断

void Timer0_Routine() interrupt 1  //定时器0的中断函数
{static unsigned char Button; //按键扫描时间变量 10msstatic unsigned int T0Count;TL0 = 0x66;       //设置定时初值TH0 = 0xFC;        //设置定时初值Button++;T0Count++;if(T0Count >= SpaceT){i++;T0Count = 0;    //软件复位}if(Button >= 10){MatrixKey_Loop();Button = 0;}}

LED处理

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

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

或者对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向前移位,一次移动移位,由此可以想到位运算中的移位运算符

参考书 C primer plus

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

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

留点空余时间思考一下

……

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

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

左移2次 0000 0100                                       取反 1111 1011

……

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

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

模式2流水

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

模式3

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

模式4

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

程序源码

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

Timer0.c

#include <REGX52.H>
#include "Timer0.h"//定时器0初始化模板  1毫秒@11.0592MHz
void Timer0_Init(void)      //1毫秒@11.0592MHz
{TMOD &= 0xF0;     //设置定时器模式TMOD |= 0x01;     //设置定时器模式TL0 = 0x66;       //设置定时初值TH0 = 0xFC;        //设置定时初值TF0 = 0;       //清除TF0标志TR0 = 1;      //定时器0开始计时ET0=1;EA=1;PT0=0;
}

Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__void Timer0_Init(void);     //1毫秒@11.0592MHz#endif

MatrixKeyT.c

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

#include <REGX52.H>
#include "MatrixKeyT.h"unsigned char Key_KeyNumber;/***@brief   名称:矩阵键盘扫描,获取按键键码*@param   参数:无  (放在主函数while里 赋值给一个 代表键码的变量)*@retval返回值:KeyNumber 0代表无按键按下,1~16代表键码*/
unsigned char Key(void)
{unsigned char Temp = 0;Temp = Key_KeyNumber; //赋值给暂存变量Key_KeyNumber = 0;        //清零return Temp;            //返回暂存变量
}/**
*@brief   名称:矩阵键盘读取按键键码, 内部函数(不需要外部调用的)*@param   参数:无*@retval返回值:KeyNumber 0代表无按键按下,1~16代表键码*/
unsigned char MatrixKey_GetState()
{unsigned char KeyNumber=0;P1=0xFF;P1_3=0;if(P1_7==0 && P1_3==0){KeyNumber=1;}if(P1_6==0 && P1_3==0){KeyNumber=5;}if(P1_5==0 && P1_3==0){KeyNumber=9;}if(P1_4==0 && P1_3==0){KeyNumber=13;}P1=0xFF;P1_2=0;if(P1_7==0 && P1_2==0){KeyNumber=2;}if(P1_6==0 && P1_2==0){KeyNumber=6;}if(P1_5==0 && P1_2==0){KeyNumber=10;}if(P1_4==0 && P1_2==0){KeyNumber=14;}P1=0xFF;P1_1=0;if(P1_7==0 && P1_1==0){KeyNumber=3;}if(P1_6==0 && P1_1==0){KeyNumber=7;}if(P1_5==0 && P1_1==0){KeyNumber=11;}if(P1_4==0 && P1_1==0){KeyNumber=15;}P1=0xFF;P1_0=0;if(P1_7==0 && P1_0==0){KeyNumber=4;}if(P1_6==0 && P1_0==0){KeyNumber=8;}if(P1_5==0 && P1_0==0){KeyNumber=12;}if(P1_4==0 && P1_0==0){KeyNumber=16;}return KeyNumber;
}void MatrixKey_Loop(void)
{static unsigned char NowState,LastState;LastState = NowState;             //按键状态更新NowState  = MatrixKey_GetState();      //获取当前按键状态if(LastState == 1 && NowState == 0) {Key_KeyNumber=1;}if(LastState == 2 && NowState == 0) {Key_KeyNumber=2;}if(LastState == 3 && NowState == 0) {Key_KeyNumber=3;}if(LastState == 4 && NowState == 0) {Key_KeyNumber=4;}if(LastState == 5 && NowState == 0) {Key_KeyNumber=5;}if(LastState == 6 && NowState == 0) {Key_KeyNumber=6;}if(LastState == 7 && NowState == 0) {Key_KeyNumber=7;}if(LastState == 8 && NowState == 0) {Key_KeyNumber=8;}if(LastState == 9 && NowState == 0) {Key_KeyNumber=9;}if(LastState ==10 && NowState == 0) {Key_KeyNumber=10;}if(LastState ==11 && NowState == 0) {Key_KeyNumber=11;}if(LastState ==12 && NowState == 0) {Key_KeyNumber=12;}if(LastState ==13 && NowState == 0) {Key_KeyNumber=13;}if(LastState ==14 && NowState == 0) {Key_KeyNumber=14;}if(LastState ==15 && NowState == 0) {Key_KeyNumber=15;}if(LastState ==16 && NowState == 0) {Key_KeyNumber=16;}
}

MatrixKeyT.h

#ifndef __MATRIXKEYT_H__
#define __MATRIXKEYT_H__unsigned char Key(void);
void MatrixKey_Loop(void);#endif

main.c

#include <REGX52.H>
#include "Timer0.h"
#include "MatrixKeyT.h"unsigned char i;//循环变量
unsigned char KeyNumer;
unsigned int  SpaceT = 500;//初值400ms
unsigned char Light_Mode;void LED_Func();void main()
{   Timer0_Init();while(1){KeyNumer = Key();LED_Func();}
}void LED_Func()
{if    (KeyNumer == 1) Light_Mode=1;else if(KeyNumer == 2) Light_Mode=2;else if(KeyNumer == 3) Light_Mode=3;else if(KeyNumer == 4) Light_Mode=4;else if(KeyNumer == 5){SpaceT += 100;}if(SpaceT>1200)SpaceT = 400;if(Light_Mode == 1){for(i=0;i<8;)   //i变量在定时器里加加,控制闪烁时间间隔{P2 = ~(0x01<<i);
//              if(Light_Mode != 1)    //for循环,按键键码数值改变,但是模式没及时改变,此处有个bug
//                  break;  //要小写}}else if(Light_Mode == 2){for(i=0;i<8;) //i变量在定时器里加加,控制闪烁时间间隔{P2 = ~(0x80>>i);
//              if(Light_Mode != 2)
//                  break;}}else if(Light_Mode == 3){for(i=0;i<4;){P2 = ~((0x01<<i) | (0x80>>i));//两边向中间 亮灯}}else if(Light_Mode == 4){for(i=0;i<4;){P2 = ~((0x10<<i) | (0x08>>i));}}
}void Timer0_Routine() interrupt 1  //定时器0的中断函数
{static unsigned char Button; //按键扫描时间变量 10msstatic unsigned int T0Count;TL0 = 0x66;       //设置定时初值TH0 = 0xFC;        //设置定时初值Button++;T0Count++;if(T0Count >= SpaceT){i++;T0Count = 0;    //软件复位}if(Button >= 10){MatrixKey_Loop();Button = 0;}}

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

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

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

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

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

欢迎大家讨论!

【51单片机】花式流水灯相关推荐

  1. 51单片机:流水灯蜂鸣器控制

    //51单片机:流水灯&蜂鸣器控制 //bglei@nuaa.edu.cn 2022/8/28<流水灯&蜂鸣器控制> #include<reg52.h> #in ...

  2. 51单片机循环流水灯源码

    51单片机循环流水灯源码 #include <reg52.h> #include <intrins.h> void delay(unsigned char a) {unsign ...

  3. 51单片机LED流水灯、走马灯的实现

    目录 1. 硬件设计 2. 软件设计 流水灯: 跑马灯: 1. 硬件设计 流水灯的原理: 图中主要包括51单片机芯片和流水灯模块,流水灯模块接在了单片机的P2口,500欧电阻是用来保护电路的.由原理图 ...

  4. 基于51单片机的流水灯设计

    三个按键:A按键启动.B按键控制不同流水速度(低中高).C按键 设计思路一(未用中断): 8个LED灯正极解电源,负极接单片机I/O口. 死循环:设置P2口为11111110,使用左移函数,循环七次. ...

  5. 51单片机花样流水灯

    目录 工作原理 51代码 总结 工作原理 许多单片机上面都有若干LED灯,在控制系统的控制下能够按照设定的顺序和时间点亮和熄灭,形成一定的视觉效果叫做跑马灯.其中若干个LED灯依次点亮,在视觉上感觉灯 ...

  6. 51单片机入门 - 流水灯 和 呼吸灯

    本文编程环境在 51单片机开发环境搭建 - VS Code 从编写到烧录 有过介绍.使用 VS Code + SDCC + stcgal. 关于软硬件的信息: Windows 10 STC89C52R ...

  7. 51单片机的流水灯实现仿真。

    使用KeilC51进行C语言的程序编写生成hex文件后导入51单片机中形成流水灯. 由于51单片机I/O口的驱动能力有限,因此在P0需要使用上拉电阻(限流电阻)来驱动二极管. 流水灯驱动程序如下: # ...

  8. 简单51单片机c语言编程流水灯,51单片机如何实现流水灯?51单片机实现流水灯的三种方法详细分析...

    描述 首先,介绍下原理.下图为主控芯片和流水灯模块的原理图.流水灯模块接在单片机的P1口,由原理图可以知道,在P1口给一个低电平即可点亮LED灯.相反,如果要LED灯熄灭,就要把P1口的电平变为高电平 ...

  9. 流水灯c语言程序tm,51单片机LED流水灯课程设计任务书+论文

    郑州工程技术学院课程设计 设计题目:LED流水灯设计 学    院:机电与车辆工程学院 专    业:16电气自动化技术专业班 级普招二班 姓    名:王*&刘* 指导教师:李* 日    ...

  10. 基于51单片机的流水灯循环点亮

    原理 基于单片机P1口的流水灯功能 通过在数组中存储每一个灯对应状态的十六进制,然后通过循环遍历和延迟来实现LED的循环点亮. 举例:前四个灯亮,后四个灯灭. 二进制为11110000,转为十六进制为 ...

最新文章

  1. 最短路径问题-Dijkstra
  2. MySQL索引知识总结
  3. Exported activity does not require permission
  4. 同一类的不同对象,在调用相同的成员函数时,入口地址是相同的
  5. 用动画切换按钮的状态
  6. 前端学习(1147):ES6学习目标
  7. 学生成绩管理系统java+mysql+swing入门级项目开发
  8. JavaScript-Date日期对象
  9. python 读取 Excel 文件的方法 csv.reader
  10. C++STL笔记(六):list详解
  11. 一文轻松搞懂-条件随机场CRF
  12. Win10设置文件夹背景色
  13. docker 阿里镜像加速
  14. 学校计算机教室用多大线径电缆,施工要用多大的电线电缆?本文教你怎么算
  15. 2021前端面试经常被问到的题(附答案)
  16. (20210116已解决)Windows下的CTF加载程序是什么?
  17. IE浏览器js 中http请求,中文传参报400错误-解决方法
  18. python 爬取数据(CBA所有球队数据) -爬虫
  19. 脉脉热帖:数仓真的是太无聊了...
  20. gitlab上创建新的分支并发布代码

热门文章

  1. 未来计算机的发展英文文章,计算机的现状和未来计算机发展(The status quo of computers and future computer development).doc...
  2. 【图论】网络流——最大流和最小费用流
  3. golang 后台管理系统框架
  4. Excel表格垂直居中后还是不在单元格中间(探究原因贴)
  5. LaTeX BibTeX的使用
  6. 同步,异步的定义和区别
  7. Linux CentOS7 VMware LAMP架构Apache用户认证、域名跳转、Apache访问日志
  8. Qt TableView的简单使用
  9. Matlab中结构体struct创建和使用
  10. ClearCase 基本操作介绍