51单片机LED流水灯拖尾效果.docx
《51单片机LED流水灯拖尾效果.docx》由会员分享,可在线阅读,更多相关《51单片机LED流水灯拖尾效果.docx(18页珍藏版)》请在冰豆网上搜索。
51单片机LED流水灯拖尾效果
高级流水灯--水滴效果(渐变带拖尾效果)实现和讲解
简介
学习嵌入式第一个例子通常都是控制一个LED亮灭,然后是花样繁多的流水灯,但不管灯的花样如何变化,单个LED的亮度没有变化,只有亮、灭两个状态,本章我们实现如何控制LED的亮度。
1什么是PWM
脉冲宽度调制(PulseWidthModulation,简称PWM),是利用微处理器的数字输出来对模拟电路进行控制的一种技术。
在本章的应用中可以认为PWM就是一种方波。
比如图1:
http:
E
6E.png(原文件名:
120611_
0."png)图1方波
是周期为10ms,占空比为60%的PWM。
占空比:
高电平在一个周期之内所占的时间比率。
2硬件设计
在例说51单片机的第三章,我们讲过如何控制开发板上LED的亮灭。
首先译码器输出端LEDS6为低,T10导通,给8个LED供电,然后通过缓冲器8个输出端BD0~BD7的控制LED的亮灭(低亮高灭)。
http:
9."png(原文件名:
120611_
1."png)图2LED硬件连接
如果BD口输出高低不断变化,则LED会闪烁;如果这种高低电平变化非常快,由于人的视觉暂留现象,LED就会出现不同的亮度。
3软件设计
3.1PWM能否控制亮度
下面我们就用实践验证PWM是否能够控制LED的亮度,测试代码如下:
程序清单L1:
验证PWM能否控制LED的亮度
1#include52."h>
2#include"my_type.h"
3#include"hw_config.h"45
6voidmain(void)
7{
8u8i=0;910//使能独立LED的供电,即LEDS6输出低电平
11LEDEN=0;
12ADDR0=0;
13ADDR1=1;
14ADDR2=1;
15ADDR3=1;
16
17//第一个LED亮
18P0=0xFE;
19
20while
(1)
21{
22for(i=0;i<250;i++)
23{
24if(i<10)
25{
26P0&=0xFD;//第二个灯亮
27}
28else
29{
30P0|=0x02;//第二个灯灭
31}
32}
33}
34}
L1(22-32):
这段代码实现P
0."1输出占空比为96%的方波,而P
0."0恒为低。
P
0."1输出如图3所示(受纸张限制,图中高低电平长度比例和实际有偏差)。
http:
1."png(原文件名:
120611_
2."png)图3
下载验证:
从开发板上可以看到运行效果,D1比D2亮。
(这里说明一点:
当P0输出低电平时,LED亮,所以,PWM的占空比越小越亮)。
3.2产生8个亮度级别
3.1节的例子证实了我们的设想,PWM可控制LED的亮度,下面我们设计几组占空比不同的PWM,看看对LED亮度的控制效果。
代码如下:
程序清单L2:
不同占空比对LED亮度的控制
1#include52."h>
2#include"hw_config.h"
3#include"my_type.h"45
6//亮度级别表
7codeu8LightLevel={0,1,2,4,8,16,32,64};89voidmain(void)
10{
11u8i=0;
12u8j=0;
13u8k=0;
14u8temp=0;
15
16//使能独立LED的供电,即LEDS6输出低电平
17LEDEN=0;
18ADDR0=0;
19ADDR1=1;
20ADDR2=1;
21ADDR3=1;
22
23//开始全灭
24P0=0xFF;
25
26while
(1)
27{
28//P0端口输出8组占空比不同的PWM
29for(i=0;i<64;i++)
30{
31for(j=0;j<8;j++)
32{
33if(LightLevel<=i)
34{
35temp|=(1<36}
37else
38{
39temp&=~(1<40}
41}
42
43P0=temp;
44}
45}
46}
L2(29-45).此段程序是让P0口输出8组占空比不同的PWM,如图4:
http:
3."png)图4
下载验证:
从开发板上可以看到运行效果,从D1到D8的亮度逐渐增大。
3.3水滴下落效果
根据PWM可控制LED亮度的原理,我们用8个LED实现水滴下落的效果。
第一步,水滴逐渐变大,用D1从暗变亮模拟;第二步,水滴下落,带有拖尾效果,LED逐个亮,移动速度加快,且越靠前的LED亮度越大。
程序清单L3水滴流水灯
1#include52."h>
2#include"hw_config.h"
3#include"my_type.h"45//亮度级别表
6codeu8LightLevel={0,1,2,4,8,16,32,64};78//水滴时间,实现加速效果
9codeu8LightTime={16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
10
11
12voidmain(void)
13{
14u8i,j,k;
15u8temp,count;
16u8state;
17
18//使能独立LED的供电,即LEDS6输出低电平
19LEDEN=0;
20ADDR0=0;
21ADDR1=1;
22ADDR2=1;
23ADDR3=1;
24
25while
(1)
26{
27//开始全灭
28P0=0xFF;
29
30//---------------水滴逐渐变大(第一个LED亮度逐渐变大)---------
31for(i=0;i<64;i++)
32{
33//一个亮度级别发送64个脉冲
34for(j=0;j<64;j++)
35{
36P0=0xFE;
37//以i为亮度级别,随着i的增大,占空比增大
38for(k=0;k<64;k++)
39{
40if(k>i)
41{
42P0=0xFF;
43}
44}
45}
46}
47
48//----------------------水滴降落过程---------------------
49for(state=0;state<16;state++)
50{
51//每一状态维持LightTime个脉冲
52for(count=0;count<=LightTime;count++)
53{
54//temp记录8个LED的状态,0代表亮,1代表灭
55temp=0x00;
56
57//一个脉冲长度j从0到63
58for(j=0;j<64;j++)
59{
60//根据亮度表,依次确定8个LED当前状态,亮或灭
61for(k=0;k<8;k++)
62{
63//以j为亮度级别,每个LED亮度不一样
64if(LightLevel==j)
65{
66temp|=(1<67}
68}
69
70if(state<=7)
71{
72P0=~((~temp)>>(7-state));
73}
74else
75{
76P0=~((~temp)<<(state-7));
77}
78}
79}
80}
81}
82}
L2(31-46).实现水滴变大效果,这段代码的作用可用图形表达,如图5:
http:
4."png)图5
控制D1由暗变亮,用了64个亮度级别,每个级别发送64个脉冲。
L2(49-81).实现水滴下落。
代码就不逐行解释了,大家可根据注释自己分析,主要说一下实现的方法。
定义LED有8个亮度级别,若用开发板上的8个LED表示,如图6:
http:
5."png)图6
图中的红色面积代表亮度程度。
实现流水效果的方法就是:
让所有的亮度依次经过在所有LED,如图7:
http:
6."png)图7
状态的持续时间从0-15逐渐减小,以模拟水滴加速。
下载验证:
下载到开发板上,可以看到水滴下落效果。
3.4定时器产生PWM
前面3个例子中,我们用循环语句虽然能产生占空比不同的PWM,但PWM的周期不好控制,对此,我们学习如何用定时器产生特定周期PWM。
关于8051定时器的使用方法,大家可以参考例说51单片机的4章和5章。
我们用定时器0产生PWM,代码如下:
程序清单L4定时器0产生PWM
1#include52."h>
2#include"hw_config.h"
3#include"my_type.h"45
6//亮度级别表
7codeu8LightLevel={1,2,4,8,16,28,50,64};89//函数声明
10voidtimer0_init(void);
11
12voidmain(void)
13{
14//使能独立LED的供电,即LEDS6输出低电平
15LEDEN=0;
16ADDR0=0;
17ADDR1=1;
18ADDR2=1;
19ADDR3=1;
20
21timer0_init();
22
23while
(1)
24{
25}
26}
27
28/**********************************************************
29函数名称:
timer0_init
30功能:
初始化定时器0
31**********************************************************/
32voidtimer0_init(void)
33{
34TMOD=0x01;//运行模式1
35TH0=0xFF;//10us中断
36TL0=0xFA;
37EA=1;//开启中断
38ET0=1;
39TR0=1;//启动定时器
40
41}
42
43/************************************************************44函数名称:
timer0_overflow
45功能:
定时器0溢出中断
46************************************************************/47voidtimer0_overflow(void)interruptTIMER0_OVERFLOW
48{
49u8i,temp=0;
50staticu8count=0;
51
52count++;
53count%=64;
54
55for(i=0;i<8;i++)
56{
57if(LightLevel<=count)
58{
59temp|=(1<
60}
61else
62{
63temp&=~(1<
64}
65}
66
67P0=temp;
68
69TR0=0;
70TH0=0xFF;//重新赋值
71TL0=0xF7;
72TR0=1;
73}
L4
(32).初始化定时器0,没10us产生一次中断。
L4(55-65).控制输出8组不同占空比的PWM。
这段代码功能和程序清单2中的功能一致。
下载验证:
下载到开发板上,可以看到D1到D8亮度逐渐增大。
3.5亮度不同的点阵
学习了用定时器产生PWM,我们可以控制更多的LED,比如LED点阵的亮度。
下面的例子实现LED点阵每行的亮度都不同。
程序清单5亮度不同的点阵
1#include52."h>
2#include"hw_config.h"
3#include"my_type.h"45
6//亮度级别表
7codeu8LightLevel={1,2,4,8,16,32,50,64};89//函数声明
10voidtimer0_init(void);
11
12voidmain(void)
13{
14//使能控制点阵的译码器
15LEDEN=0;
16ADDR3=0;
17
18timer0_init();
19
20while
(1)
21{}
22}
23
24/*****************************************************************25函数名称:
timer0_init
26功能:
初始化定时器0
27*****************************************************************/28voidtimer0_init(void)
29{
30TMOD=0x01;//运行模式1
31TH0=0xFF;//中断时间10us
32TL0=0xF7
33EA=1;//开启中断
34ET0=1;
35TR0=1;//启动定时器
36}
37
38/*****************************************************************39函数名称:
timer0_overflow
40功能:
定时器0溢出中断
41*****************************************************************/42voidtimer0_overflow(void)interruptTIMER0_OVERFLOW
43{
44u8i;
45u8p1_value=0;
46staticu8state=0;//点阵状态(扫描行数)
47staticu8count=0;
48
49TR0=0;
50
51count++;
52if(count==64)
53{
54state++;
55state%=8;
56count=0;
57}
58
59if(count60{
61P0=0x00;
62}
63else
64{
65P0=0xFF;
66}
67
68p1_value=P1&0xf8;
69p1_value|=state;
70P1=p1_value;
71
72TH0=0xFF;//重新赋值
73TL0=0xFA;
74TR0=1;
75}
L5
(28).初始化定时器,每10us中断一次。
L5(51-57).每中断64次,点阵扫描移动到下一行,用state记录当前行数。
L5(59-66).扫描每一行输出的PWM都不一样,使用的方式和处理独立LED一致。
L5(68-70).输出点阵对应的位码。
下载验证:
下载到开发板上,可以看到运行效果,点阵第一行最暗,越往下越亮。
3.6点阵模拟音乐频谱分析效果
在很多音乐播放软件上,都有频谱分析的图形,如图8:
http:
7."png)图8
我们用也可以模拟相似的图形,代码如下:
程序清单6:
点阵模拟音乐频谱分析
1#include52."h>
2#include"hw_config.h"
3#include"my_type.h"45//频谱波形表
6codeu8Wave=
7{
8{0xFF,0xFF,0xFF,0xFF,0xFE,0xBB,0xFE,0xAA},
9{0xFF,0xFF,0xFF,0xFE,0xFB,0xAE,0xFA,0xAA},
10{0xFF,0xFF,0xFF,0xFE,0xEB,0xBE,0xEA,0xAA},
11{0xFF,0xFF,0xFE,0xFB,0xAF,0xFE,0xAA,0xAA},
12{0xFF,0xFE,0xFB,0xBE,0xEA,0xBA,0xAA,0xAA},
13{0xFF,0xFE,0xBB,0xEE,0xBA,0xBA,0xAA,0xAA},
14{0xFE,0xBB,0xEE,0xBA,0xAA,0xAA,0xAA,0xAA},
15{0xBA,0xEF,0xBE,0xAA,0xAA,0xAA,0xAA,0xAA},
16{0xEE,0xBB,0xFE,0xAA,0xAA,0xAA,0xAA,0xAA},
17{0xEE,0xBB,0xFE,0xEA,0xAA,0xAA,0xAA,0xAA},
18{0xFE,0xEB,0xBE,0xFE,0xAA,0xAA,0xAA,0xAA},
19{0xFF,0xEE,0xBB,0xFF,0xAE,0xAA,0xAA,0xAA},
20{0xFF,0xFE,0xAF,0xFB,0xEE,0xAA,0xAA,0xAA},
21{0xFF,0xFF,0xFE,0xBB,0xEF,0xBA,0xAA,0xAA},
22{0xFF,0xFF,0xFF,0xFE,0xAB,0xFF,0xEE,0xAA},
23{0xFF,0xFF,0xFF,0xFF,0xFE,0xEB,0xBE,0xAA}
24};
25
26//亮度级别表
27codeu8LightLevel={1,2,4,8,16,32,50,64};
28
29//函数声明
30voidtimer0_init(void);
31
32voidmain(void)
33{
34//使能控制点阵的译码器
35LEDEN=0;
36ADDR3=0;
37
38timer0_init();
39
40while
(1)
41{
42}
43}
44
45/*****************************************************************46函数名称:
timer0_init
47功能:
初始化定时器0
48*****************************************************************/49voidtimer0_init(void)
50{
51TMOD=0x01;//运行模式1
52TH0=0xFF;//10us中断
53TL0=0xFA;
54EA=1;//开启中断
55ET0=1;
56TR0=1;//启动定时器
57
58}
59
60/*****************************************************************61函数名称:
timer0_overflow
62功能:
定时器0溢出中断
63*****************************************************************/64voidtimer0_overflow(void)interruptTIMER0_OVERFLOW
65{
66u8i;
67u8p1_value=0;
68staticu8state=0;//点阵状态(扫描行数)
69staticu8count=0;
70
71staticu8wave_state=0;//波形状态
72staticu16wave_count=0;
73
74TR0=0;
75
76//每中断1000次,改变波形状态
77wave_count++;
78if(wave_count==1000)
79{
80wave_count=0;
81wave_state++;
82wave_state%=16;
83}
84
85//每中断64次,改变扫描的行
86count++;
87if(count==64)
88{
89state++;
90state%=8;
91count=0;
92}
93
94if(count95{
1P0=Wave;
2}
3else
4{
5P0=0xFF;
6}78//输出位码
9p1_value=P1&0xf8;
10p1_value|=state;
11P1=p1_value;
12
13//定时器重新赋值
14TH0=0xFF;
15TL0=0xF7;
16TR0=1;
17}
L6
(6).波形表,共16个状态,每个状态下有8个数据,即一个点阵界面。
扫描点阵时循环发送,实现动态效果。
L6(77-83).每中断1000次,更换一个状态。
L6(86-101).和L5的功能一致,只是点阵段码输出的数据变成了波形表。