LED点阵屏
作者:姜蘅洋
1.LED灯:
- LED的英文全称是:Light Emitting Diode,即发光二极管。
- 发光二极管具有单向导电性。
- 当左边接正极,右边接地的时候,LED发光二极管被点亮。
2.LED点阵屏介绍:
LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以LED灯亮灭来显示文字、图片、视频等。
实验室使用的LED点阵屏是8 x 8的点阵屏:
- 每行有8个LED灯,一共有8行,即64个LED灯。
点阵屏使用方式和数码管类似,都是利用人眼的余晖效应。
- 数码管:
- 一个数码管可以直接点亮,多个数码管采用逐个扫描的方式,即利用余晖效应一次扫描点亮每个数码管。
- LED点阵屏:
- 一次可以对一行的LED灯进行操作,采用逐行扫描的方式控制点阵屏中的所有LED灯。
- 对于该LED点阵屏,同样支持逐列操作。
- 由于a、b、c、d、e、f、g、h表示段码值,因此我们采用逐行扫描操作。
- 一次可以对一行的LED灯进行操作,采用逐行扫描的方式控制点阵屏中的所有LED灯。
- 数码管:
3.点阵屏单图显示:
单图显示爱心:
- 由于采用逐行扫描,因此对应一个8字节的段码数组,每个字节操控每一行。
取模软件:
- 这里用的取模软件是:CopyLeft by Horse2000
- 设置取模方式是:横向取模
- 因为段码控制的是一行。
- 字节倒序:
- 段码中的a来自D0,h来自D7,即段码段的数据按照D0 ~ D7排列的,而不是D7 ~ D0,因此要采用字节倒序。
code关键字:
- keil中提供了一个特殊的关键字"code",这个关键字在标准C中是没有的。
- 我们知道,在单片机中一般都有两块存储区域,ROM和RAM,程序代码存储在ROM(只读存储器,相当于硬盘)中,程序要用的变量存储在RAM(随机读取存储器,相当于内存)中。
- **"code"**的作用就是将其修饰过的变量存储在ROM中而非RAM。
- 在单片机中,RAM空间都比较小,是比较宝贵的,**"code"**的意义就是将一些初始化后值一直保持不变的变量(如固定的常数、表格、常量数组、只读常量等)放置于ROM区,从而节省了RAM空间。
- c
//对点阵屏的每个LED灯进行检测的函数 void dotMatrix_Examine(void) { uint_8 Row,Col; for (Row = 0 ; Row < 8 ; Row++) { DIOLA = 1; DIOLA_Byte = 1 << Row; DIOLA = 0; for (Col = 0 ; Col < 8 ; Col++) { DULA = 1; DULA_Byte = ~(1 << Col); DULA = 0; dotMatrixExamineDelay_1s(); } } DIOLA = 0; DULA = 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 - c
//dotMatrixScreen.h #ifndef __DOTMATRIXSCREEN_H__ #define __DOTMATRIXSCREEN_H__ #include "overwhelm.h" #include <intrins.h> extern code uint_8 DMS_1[]; #define DULA P2_6 #define DIOLA P2_5 #define WELA P2_7 #define DULA_Byte P0 #define DIOLA_Byte P0 void dotMatrix_Init(void); void dotMatrix_Examine(void); void Dynamic_dotMatrix(uint_8 *DMS); #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 - c
//dotMatrixScreen.c code uint_8 DMS_1[] = {~0x00,~0x66,~0xFF,~0xFF,~0xFF,~0x7E,~0x3C,~0x18};//由于字模软件生成的段码适合段码端是阳极点亮的LED灯,但我们这款LED点阵屏的断码端对应阴极点亮,因此取反即可。 void dotMatrix_Init(void)//初始化函数,由于P0端被复用,因此我们这里要对复用的锁存器的Latch Enable进行失能。 { DULA = 0; WELA = 0; DIOLA = 0; } void dotMatrixDelay_1ms() //@11.0592MHz { unsigned char data i, j; _nop_(); i = 4; j = 192; do { while (--j); } while (--i); } void Static_dotMatrix(uint_8 i , uint_8 num)//静态点亮1行。 { DIOLA = 1; DIOLA_Byte = 1 << i; DIOLA = 0; DULA = 1; DULA_Byte = num; DULA = 0; } void Colse_dotMatrix(void) { DIOLA = 1; DIOLA_Byte = 0x00; DIOLA = 0; DULA = 1; DULA_Byte = 0xFF; DULA = 0; } void Dynamic_dotMatrix(uint_8 *DMS)//动态点亮8行。 { Static_dotMatrix(0 , DMS[0]); dotMatrixDelay_1ms(); Colse_dotMatrix(); //如果点亮完之后不关闭,那么LED点阵屏刷新的时候,不该亮的灯会微微发光。 Static_dotMatrix(1 , DMS[1]); dotMatrixDelay_1ms(); Colse_dotMatrix(); Static_dotMatrix(2 , DMS[2]); dotMatrixDelay_1ms(); Colse_dotMatrix(); Static_dotMatrix(3 , DMS[3]); dotMatrixDelay_1ms(); Colse_dotMatrix(); Static_dotMatrix(4 , DMS[4]); dotMatrixDelay_1ms(); Colse_dotMatrix(); Static_dotMatrix(5 , DMS[5]); dotMatrixDelay_1ms(); Colse_dotMatrix(); Static_dotMatrix(6 , DMS[6]); dotMatrixDelay_1ms(); Colse_dotMatrix(); Static_dotMatrix(7 , DMS[7]); dotMatrixDelay_1ms(); Colse_dotMatrix(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 将上面的动态显示放在定时器中断里面:
这样做的目的是方便后面动画代码的实现。
- c
//bsp_time_0.c //点阵屏的静态显示函数没有发生变化 #include "bsp_time_0.h" uint_8 count_time0 = 0; void Timer0_Isr(void) interrupt 1 //2ms * 20 { TL0 = 0xCD; TH0 = 0xF8; Colse_dotMatrix(); Static_dotMatrix(count_time0 , DMS_1[count_time0]); if (++count_time0 == 8) count_time0 = 0; } void Timer0_Init(void) //@11.0592MHz,定时2ms { TMOD &= 0xF0; TMOD |= 0x01; TL0 = 0xCD; TH0 = 0xF8; TF0 = 0; TR0 = 1; ET0 = 1; EA = 1; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
附一张爱心照片送给大家:
4.点阵屏动画显示:
让8 x 8点阵屏滚动显示:CCNUIOT
- 正常来讲,每一个字符对应一张图片,那么CCNUIOT对应7张图片,这七张图片对应的点阵区域是(8 x 7) x 8 = 56 x 8。
- 点阵屏动画显示的关键:
- 每 8 行组成一张点阵图片,每向上移动一行就出现一张新图片。
- 对于56 x 8的点阵来讲,一共有56 - 8 = 48张图片。
- 用一个变量DMS_index来代表每张图片的起始位置,[DMS_index,DMS_index + 7]代表了当前的图片,500ms 改变一张图片,并且每张图片都伴随着动态刷新,这样图片就变成动画了。
- 在本例中,DMS_index的最大值是48,即最后一张图片对应的起始位置。
未优化版代码:
- c
//其他代码均没有变化 uint_8 count_time0 = 0;//用于扫描8行LED灯 uint_8 count_time1 = 0;//用于定时500ms code uint_8 DMS_1[] = { 0x01,0x00,0xFC,0xFC,0xFC,0x00,0x01,0xFF, 0x01,0x00,0xFC,0xFC,0xFC,0x00,0x01,0xFF, 0x3C,0x30,0x20,0x04,0x0C,0x3C,0x3C,0xFF, 0x3C,0x3C,0x3C,0x3C,0x3C,0x00,0x00,0xFF, 0x00,0x00,0xE7,0xE7,0xE7,0x00,0x00,0xFF, 0xC3,0x99,0x3C,0x7E,0x3C,0x99,0xC3,0xFF, 0x00,0x00,0xE7,0xE7,0xE7,0xE7,0xE7,0xFF }; uint_8 DMS_index = 0;//用于定位每张图片的起始位置 void Timer0_Isr(void) interrupt 1 //2ms * 20 { TL0 = 0xCD; TH0 = 0xF8; Colse_dotMatrix(); Static_dotMatrix(count_time0 , DMS_1[count_time0 + DMS_index]); //DMS_index是每张图片的起始位置,加上count_time0就能表示每张图片的所有行。 if (++count_time0 == 8) count_time0 = 0;//扫描8行LED灯 if (++count_time1 == 250)//定时500ms { if (DMS_index++ == 48)//由于DMS_index最大是48,因此当DMS_index = 49的时候,清0。 DMS_index = 0; count_time1 = 0; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 该版本代码的问题:
- 当显示最后一张图片的时候,也就是完整的T,接下来的图片是完整的C,没有滚动到C的效果。
- 这是因为在我们的程序中,DMS_index最大是48,因为当count_time0 = 7的时候,DMS_index+count_time0 = 55,也就是数组的最后一个元素,因此DMS_index不能再大了。
- 但如果想显示滚动的效果,DMS_index要到55*(对应上图的最后一行黑边),并且DMS_index的下一个值是0。
- 通过上面的问题我们也找到了问题:DMS_1数组的索引。
- 因此,我们采用取模的思想,对DMS_1数组的索引进行取模,即模56,这样,DMS_index的值就能到达55(使用一个if判断语句让下一个值是0),同时取模不影响静态显示的正确性。
- 这是因为在我们的程序中,DMS_index最大是48,因为当count_time0 = 7的时候,DMS_index+count_time0 = 55,也就是数组的最后一个元素,因此DMS_index不能再大了。
- 当显示最后一张图片的时候,也就是完整的T,接下来的图片是完整的C,没有滚动到C的效果。
优化版代码:
- c
//其他代码均没有变化 uint_8 count_time0 = 0;//用于扫描8行LED灯 uint_8 count_time1 = 0;//用于定时500ms code uint_8 DMS_1[] = { 0x01,0x00,0xFC,0xFC,0xFC,0x00,0x01,0xFF, 0x01,0x00,0xFC,0xFC,0xFC,0x00,0x01,0xFF, 0x3C,0x30,0x20,0x04,0x0C,0x3C,0x3C,0xFF, 0x3C,0x3C,0x3C,0x3C,0x3C,0x00,0x00,0xFF, 0x00,0x00,0xE7,0xE7,0xE7,0x00,0x00,0xFF, 0xC3,0x99,0x3C,0x7E,0x3C,0x99,0xC3,0xFF, 0x00,0x00,0xE7,0xE7,0xE7,0xE7,0xE7,0xFF }; uint_8 DMS_index = 0;//用于定位每张图片的起始位置 void Timer0_Isr(void) interrupt 1 //2ms * 20 { TL0 = 0xCD; TH0 = 0xF8; Colse_dotMatrix(); Static_dotMatrix(count_time0 , DMS_1[(count_time0 + DMS_index) % 56]); //DMS_index是每张图片的起始位置,加上count_time0就能表示每张图片的所有行。 //为了避免数组索引越界,采用模56(即数组大小) if (++count_time0 == 8) count_time0 = 0; if (++count_time1 == 250) { if (DMS_index++ == 55)//注意,这里到55,如果到56的话,56和0实际上取模56之后是相同的,即产生了两张一样的点阵图片。 DMS_index = 0; count_time1 = 0; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30