C51编程入门(八):一灯的故事(下)玩转流水灯

温馨提示: 为了获得更好的阅读效果,建议移步到微信PC客户端阅读。

通过前面的两课,相信大家已经掌握如何用单片机点亮一盏LED。 本节一灯大师要和大家探讨流水灯的实现,一起玩转流水灯。

设单片机的P0.3~P0.0外接了4只发光二极管(LED),如图1所示。二极管的负极连接到单片机,正极通过一个限流电阻连接到VCC。

当单片机输出低电平,发光二极管正向导通,LED点亮;当单片机输出高电平,LED熄灭。

图1 流水灯电路(以4只LED为例)

下面以图1为例,实现4只流水灯效果。我们将使用2种不同的方法。至于哪种方法更好,希望大家能有自己的判断。

让我们先按照流水灯状态,写出P0.3~P0.0的输出值,注意高位写在左边,低位写在右边。

表1 流水灯状态与单片机控制I/O的关系

由表1可知,P0口低4位顺序输出1110、1100、1000和0000就可以实现流水灯,此为方法1。

图1只使用了P0的低4位,编程时一般以P0为整体操作,要避免对单个I/O操作,指令越少越好,一是代码好维护,二是代码阅读性好。

高四位输出全1(即F),而不是0,这是为什么呢? 请大家先思考。

方法1源码:

#include

//函数声明

void delayMS(unsigned int x); //ms延时函数声明

void main()

{

while(1)

{

P0 = 0xFF; delayMS(1000); //初始化所有灯全熄灭

P0 = 0xFE; delayMS(1000); //D1亮

P0 = 0xFC; delayMS(1000); //D1、D2亮

P0 = 0xF8; delayMS(1000); //D1、D2、D3亮

P0 = 0xF0; delayMS(1000); //D1、D2、D3、D4亮

}

}

/****************************

函数:delayMS@12MHz

功能:ms级延时函数

参数: x:unsigned int, 延时时间,单位:ms

返回值:无

****************************/

void delayMS(unsigned int x) //delayMS函数实现

{

unsigned int i,j;

for(i=0;i

for(j=0;j

}

//高四位输出全1(即F),而不是0,这是因为单片机默认输出高电平,此处输出高电平,一般不会影响外部电路(假设此引脚外接了其它电路)。

编译下载验证,方法1代码可以实现由左向右的流水灯效果。

假设,要求流水灯由右向左,速度为500ms,如何修改源代码?由于代码较少,修改还算比较顺利,对吧,但是稍不注意,可能会改错。

当流水的灯有16只、32只或更多,方法1的局限性暴露无遗.......费人力,费时间,费工夫。

怎么应对多变的流水需求(速度,方向,花样等)?稍安勿躁。 让我们回看表1,标出各个流水状态下单片机输出低电平的情况(点亮LED)。

我的天呢(借用小岳岳的台词),下一个状态刚好是上一个状态左移一位。我们发现了规律,达到了科学的目的之一(科学的目的:发现并掌握规律)。于是乎,一个主意顺势而生,

使用移位操作实现流水灯——此为方法二。

为了便于调用和代码维护,我们将流水灯操作用一个函数封装起来,void ledFlow(unsigned int speed), 支持流水速度可编程。

//方法2源码

#include

//函数声明

void delayMS(unsigned int x); //ms延时函数声明

void ledFlow(unsigned int speed); //流水灯函数定义

void main()

{

while(1)

{

ledFlow(1000);

ledFlow(500);

}

}

/****************************

函数:delayMS@12MHz

功能:ms级延时函数

参数: x:unsigned int, 延时时间,单位:ms

返回值:无

****************************/

void delayMS(unsigned int x) //delayMS函数实现

{

unsigned int i,j;

for(i=0;i

for(j=0;j

}

/****************************

函数:ledFlow

功能:流水灯函数,LED数量4

参数: speed:unsigned char, 流水灯速度,单位:ms

返回值:无

****************************/

void ledFlow(unsigned int speed)

{

unsigned char i;

P0 = 0xFF; //初始化全灭

delayMS(speed);

for(i=0;i

{

P0 = P0

delayMS(speed);

}

}

方法2验证:

其实,流水可以玩很多花样,如参数化设置流水方向,设置流水运行次数,例如:

void ledFlow(unsigned int speed, bit dir, unsigned char times);

实现起来比简单。方向由移位方向决定,流水运行次数用循环实现即可。代码略。

写到此,一灯大师要退场了。走之前,一灯跟大家分享一个单片机初学者编写的心形流水灯文章,链接见http://forum.eepw.com.cn/thread/251409/1/#6

过程全有,独无代码,总觉得缺了点什么。于是有了下面的内容,纯属自娱。

为什么登山,因为山在那里——新西兰登山运动家埃德蒙德·希拉里。

为什么玩单片机,因为单片机在那里。

图2 心形流水灯电路图(32只LED,分别接P0~P3)

表2接口对应表

源代码:

#include

//函数声明

void delayMS(unsigned int x); //ms延时函数声明

void heartBeat(unsigned int speed,unsigned char n); //心闪烁函数声明

void heartSpark(unsigned int speed,unsigned char n); //心形流水灯函数声明

//主程序

void main()

{

while(1)

{

heartBeat(750,18); //心闪烁18次,频率:1分钟80次左右

heartSpark(500,3); //心形流水灯18次

heartBeat(600,100);//心闪烁100次,频率:1分钟100次左右

}

}

/****************************

函数:delayMS@12MHz

功能:ms级延时函数

参数: x:unsigned int, 延时时间,单位:ms

返回值:无

****************************/

void delayMS(unsigned int x) //delayMS函数实现

{

unsigned int i,j;

for(i=0;i

for(j=0;j

}

/****************************

/****************************

函数:heartBeat

功能:心闪烁函数,闪烁速度可设置

参数: speed:unsigned char, 闪烁速度,越小闪烁越快,单位:ms;n:执行次数,不做检查

返回值:无

****************************/

void heartBeat(unsigned int speed,unsigned char n)

{

while(n--) //先判断再执行--

{

//取反,灯状态翻转

P0=~P0;

P1=~P1;

P2=~P2;

P3=~P3;

delayMS(speed); //延时越短,闪烁越快

}

}

/****************************

函数:heartSpark

功能:心形流水灯函数,有底部分左右同时向上流水,流水速度可设置

参数: speed:unsigned char, 流水灯速度,越小速度越快,单位:ms;n:执行次数,不做检查

返回值:无

****************************/

void heartSpark(unsigned int speed,unsigned char n)

{

unsigned char nCount = 16; //16次,完成一次流水灯动作

unsigned char i;

unsigned int leftHeart; //P0-P1作为一组,由2字节的无符号整型变量移位实现流水灯,P0为低字节,P1为高字节

unsigned int rightHeart; //P2-P3作为一组,由2字节的无符号整型变量移位实现流水灯,P2为低字节,P2为高字节

while(n--)

{

leftHeart=0xFFFF; //心灯熄灭

rightHeart=0xFFFF; //心灯熄灭

delayMS(speed); //延时1s

for(i=0;i

{

leftHeart = leftHeart

rightHeart = rightHeart>>1;

P0 = leftHeart&0x00FF; //取leftHeart低八位,控制P0口外接的led7-led0

P1 = (leftHeart&0xFF00)>>8; //取leftHeart高八位,控制P1口外接的led15-led8

P2 = rightHeart&0x00FF; //取rightHeart低八位,控制P2口外接的led23-led16

P3 = (rightHeart&0xFF00)>>8; //取rightHeart高八位,控制P0口外接的led31-led24

delayMS(speed);

}

}

}

仿真结果:

文末附上一段视频,“战胜自己,克服自己”。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181013G0WECC00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券