【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、代码简洁,注释简单易懂。
实现思路:
按键部分
定时器部分
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单片机】花式流水灯相关推荐
- 51单片机:流水灯蜂鸣器控制
//51单片机:流水灯&蜂鸣器控制 //bglei@nuaa.edu.cn 2022/8/28<流水灯&蜂鸣器控制> #include<reg52.h> #in ...
- 51单片机循环流水灯源码
51单片机循环流水灯源码 #include <reg52.h> #include <intrins.h> void delay(unsigned char a) {unsign ...
- 51单片机LED流水灯、走马灯的实现
目录 1. 硬件设计 2. 软件设计 流水灯: 跑马灯: 1. 硬件设计 流水灯的原理: 图中主要包括51单片机芯片和流水灯模块,流水灯模块接在了单片机的P2口,500欧电阻是用来保护电路的.由原理图 ...
- 基于51单片机的流水灯设计
三个按键:A按键启动.B按键控制不同流水速度(低中高).C按键 设计思路一(未用中断): 8个LED灯正极解电源,负极接单片机I/O口. 死循环:设置P2口为11111110,使用左移函数,循环七次. ...
- 51单片机花样流水灯
目录 工作原理 51代码 总结 工作原理 许多单片机上面都有若干LED灯,在控制系统的控制下能够按照设定的顺序和时间点亮和熄灭,形成一定的视觉效果叫做跑马灯.其中若干个LED灯依次点亮,在视觉上感觉灯 ...
- 51单片机入门 - 流水灯 和 呼吸灯
本文编程环境在 51单片机开发环境搭建 - VS Code 从编写到烧录 有过介绍.使用 VS Code + SDCC + stcgal. 关于软硬件的信息: Windows 10 STC89C52R ...
- 51单片机的流水灯实现仿真。
使用KeilC51进行C语言的程序编写生成hex文件后导入51单片机中形成流水灯. 由于51单片机I/O口的驱动能力有限,因此在P0需要使用上拉电阻(限流电阻)来驱动二极管. 流水灯驱动程序如下: # ...
- 简单51单片机c语言编程流水灯,51单片机如何实现流水灯?51单片机实现流水灯的三种方法详细分析...
描述 首先,介绍下原理.下图为主控芯片和流水灯模块的原理图.流水灯模块接在单片机的P1口,由原理图可以知道,在P1口给一个低电平即可点亮LED灯.相反,如果要LED灯熄灭,就要把P1口的电平变为高电平 ...
- 流水灯c语言程序tm,51单片机LED流水灯课程设计任务书+论文
郑州工程技术学院课程设计 设计题目:LED流水灯设计 学 院:机电与车辆工程学院 专 业:16电气自动化技术专业班 级普招二班 姓 名:王*&刘* 指导教师:李* 日 ...
- 基于51单片机的流水灯循环点亮
原理 基于单片机P1口的流水灯功能 通过在数组中存储每一个灯对应状态的十六进制,然后通过循环遍历和延迟来实现LED的循环点亮. 举例:前四个灯亮,后四个灯灭. 二进制为11110000,转为十六进制为 ...
最新文章
- 最短路径问题-Dijkstra
- MySQL索引知识总结
- Exported activity does not require permission
- 同一类的不同对象,在调用相同的成员函数时,入口地址是相同的
- 用动画切换按钮的状态
- 前端学习(1147):ES6学习目标
- 学生成绩管理系统java+mysql+swing入门级项目开发
- JavaScript-Date日期对象
- python 读取 Excel 文件的方法 csv.reader
- C++STL笔记(六):list详解
- 一文轻松搞懂-条件随机场CRF
- Win10设置文件夹背景色
- docker 阿里镜像加速
- 学校计算机教室用多大线径电缆,施工要用多大的电线电缆?本文教你怎么算
- 2021前端面试经常被问到的题(附答案)
- (20210116已解决)Windows下的CTF加载程序是什么?
- IE浏览器js 中http请求,中文传参报400错误-解决方法
- python 爬取数据(CBA所有球队数据) -爬虫
- 脉脉热帖:数仓真的是太无聊了...
- gitlab上创建新的分支并发布代码
热门文章
- 未来计算机的发展英文文章,计算机的现状和未来计算机发展(The status quo of computers and future computer development).doc...
- 【图论】网络流——最大流和最小费用流
- golang 后台管理系统框架
- Excel表格垂直居中后还是不在单元格中间(探究原因贴)
- LaTeX BibTeX的使用
- 同步,异步的定义和区别
- Linux CentOS7 VMware LAMP架构Apache用户认证、域名跳转、Apache访问日志
- Qt TableView的简单使用
- Matlab中结构体struct创建和使用
- ClearCase 基本操作介绍