51单片机学习笔记(清翔版)(13)——LED点阵、74HC595

如图3,点阵屏分单色和彩色,点阵屏是由许多点组成的,在一个点上,只有一颗一种颜色的灯珠,这就是单色点阵屏,彩色的在一个点上有三颗灯珠,分别是RGB三原色。

图4你可能没看出来,那么大块黄色的就是点阵屏,下面那个粉色的长条也是。

大的点阵屏实际上是由许多一小块一小块点阵屏拼接在一块的,最终形成一个大的,在做移动舞台时,它拆装方便,例如演唱会时,液晶的就不方便,运输也不方便,也容易损坏,而点阵屏就一快一块运输就可以,然后拼在一起。

单色点阵屏成本低,就不能像图4一样显示视频了,只能显示文字。

这是我们用的,一个点里面一个灯珠的单色点阵屏。

8*8就是8行8列的64个的点阵屏,左下方那是背面引脚图,在设计点阵屏的时候就需要了解这个引脚是怎么排序的,否则怎么驱动它。

看这有一个凹起的塑料,以这个点为参考点,右面是1脚,左边是8脚,右到左1~8,上方左到右是9~16。学会后要是自己想做一个点阵屏就要知道引脚的排序。

接下来就看点阵屏内部原理图。

内部结构比较简单,是由多颗LED灯珠连接在一起组成的

左边和上边的数字分别对应的是引脚

LED就是让指定的灯亮,来显示我们需要的字形

比如“上”

那么我们如何点亮呢?

例如左上角的那个,它的正极接到了9脚,负极接到了13脚,那么9接高电平,13给低电平就亮了,那么其它脚我们怎么赋值呢?

我们的LED点阵是共阳极的,和前面的数码管一样,也分为共阳极共阴极。

行,都是接了正极的,就是共阳极。反之,如果LED的正极接在了列上,那么就是共阴极。

回到刚才的问题,那么就是如下:

那么点亮许多灯怎么办?

比如我们再把这个点亮。

这就用到了动态扫描技术了。(和数码管一样,利用人眼的视觉暂留效果)

我们先让第一个亮,然后马上让第二个亮,然后又让第一个亮,一直循环。这样看起来就两个都亮了,实际上是不能静态显示同时亮这两个灯的。

原因呢,我们看看:

如果我们要想让这两个亮,那么9,14是高电平,13,3是低电平。那么第一行第二列的灯也会亮,第二行第一列也是。所以我们就需要用动态扫描的方式。

以上就是点阵屏静态显示和动态显示的工作原理。点阵屏在实际的应用中我们是用动态显示的方法显示我们需要的内容。

有人会问为什么不把这16个引脚都接到单片机的引脚上呢?因为接下来我们介绍74HC595芯片。

我们这个点阵是焊在了一个PCB板上做点阵模块,是由两片595级联在一起来驱动8*8点阵的。

级联在一起只需要3个单片机的IO口,就可以控制16个脚的点阵了。非常的节省IO口。这也是实际应用中的一个方案。在点阵屏行业中,几乎都是用74HC595级联驱动的。

有些同学会看到别的同学再做实验时,把16个引脚都接到了单片机上驱动,作为初学者看似很简单,实际和我们实际应用中是脱节的。实际应用中我们不可能用这种方式去驱动,这种方式太耗费硬件资源了,你看1个点阵就有16个脚,假设我们要让点阵显示的内容更加丰富,我们再加3个8*8的点阵,那就是64个引脚,如果我们直接接单片机,那么需要多少个单片机?以我们stc89c52为例,一个单片机有32个IO口,那么就需要两片单片机。(这种4块8*8的点阵合在一起我们称为16*16的点阵模块)上面这种方案显然是不可行的,一个单片机就好几块钱,你驱动4个屏就需要2个单片机,这样硬件开销成本太大了。

而595方法,两个595级联在一块就可以驱动一个8*8点阵,驱动4个点阵我们只需要8片595,而1个595只需要几毛钱(5毛)。可能有的同学还看到别的硬件驱动1个8*8点阵需要8个或者10个以上的IO口,这种硬件纯粹就是设计给你玩的,他和实际应用是严重脱轨的,就算你学单片机只是为了玩,那你玩也要玩跟实际接轨的东西,不然你也玩不出什么高端。


 74HC595

在学会595后你会发现,他在驱动这类电子屏是非常适合的。

串入并出:串行输入,并行输出。

例如有1个数据,1个字节的8位数据,要从A传送到B点,如果是串行,串行只有一个数据通道,这8位要想从A到B只能从这一个通道过去,那么它一次只能走一个,比如第一次过去一个1,1完了后,2又过去,以此类推,8次。如果是8位并行,他就有8个通道,8个数据就可以一次传过去

由此可见,并行传输速度快,但是占用的硬件资源多,一共要占用8条传输用的数据线。串行占用硬件资源少,但传输速度要慢许多。

下面看看595的使用方法

比较重要,这一块学懂了,那么595芯片也就彻底懂了,使用方法懂了,编程也就不会有问题了。

(比较难,前面都是并行输入输出,现在已经到串行输入了)

中间上面是我们的引脚图,下面是我们模块的原理图,他只需要3个IO口就可驱动16个引脚的点阵屏了。

DS(14):串行数据输入端,串行输入只需要1个数据线,级联就接上一级的Q7',意思是如果要多加一个595芯片,那么DS就接到上一个的Q7'

看这个电路图就知道,当然这里标的不一样,这里标的是SER,我们看引脚就是,

 

为什么要级联?595是串入并出,8个IO口作并行输出,我们点阵有16个脚,1片只能控制8个脚,还有8个脚我们就再需要一片。

Q7'(9)(注意有两个Q7,是不同的,要区分开,那个9脚上面的点是“非”的意思):级联输出端,将他接到下一片595的DS端

看上面那个595的Q7'有个×,是悬空的,如果不级联悬空就行,如果级联就接到下一片的14脚。

 

如果级联3个,那么我们用3个IO口就可以做一个8*3=24位数据的并行输出了。这个方法用来扩展单片机的IO口非常实用,如果单片机的IO口不够用,我们就可以用这种方案,并且价格也便宜,而且成熟好用。

SH_CP(11):数据输入的时钟线,由于是串行输入,基本上所有的串行输入都是需要一个时钟线的,这个时钟线就是给他们一个节拍,你这个数据什么时候该走,什么时候不该走要给一个节拍,不然数据不知道是不是轮到我该传输过去了。上升沿时,数据从DS串行输入。输入到595内部的移位寄存器(8位)中,是怎么一个过程呢?下面演示一下。

DS脚应用的时候要接到单片机的个IO口上,假如给接到DS的IO口送一个数据1,那么DS脚就是1了(高电平),那么1是怎么进入到内部的移位寄存器呢?就需要在11脚产生一个上升沿,那么数据就可以进入到移位寄存器中了,什么是上升沿?一个低电平,然后变为高电平。对于5V单片机而言,低电平时0V,高电平时5V。从0V变到5V的这个过程就叫做一个上升沿。单片机产生一个上升沿是很简单的,给IO口一个0,单片机就输出一个低电平0V,再给单片机一个1,单片机就输出5V,从0V到5V的这么一个变化就产生了一个上升沿,这就是单片机产生一个上升沿的过程。

再详细说一下:

假设我们发1101 0011这么一个数据,1个字节8位的。右边是低位,左边是高位。先发低位。给先给DS一个1,用单片机IO口输出一个1,然后给11脚一个低电平一个高电平产生上升沿,这时这个1就会被送到移位寄存器第八位上,接着再发第二位,再给DS一个1,再产生一个上升沿,就会把最开始发的1挤到第七位移位寄存器上,而这次的1就发到了第八位,接着再发0,那么第八位移位寄存器就是0,第七位就是1,第六位就是1,依次类推。接着我们要输出,输出到Q0~Q7上,并行的输出出去,怎么办?这时候就是12脚了。

ST_CP(12):输出存储器锁存时钟线,12也接单片机一个IO,给他一个上升沿,那么刚刚的那8位数据就一下子从Q0到Q7输出出去了,这就是串行输入并行输出。输入的时候只从DS一个口,通过时钟控制,依次放到8位的内部移位寄存器上,再给12脚输出锁存器一个上升沿,锁存器中的8位数据就一下子输出到Q0~Q7。12脚我们还有一个需要注意的,它是一个锁存器,我们学数码管的时候也讲过,573就是一个锁存器,这里锁存器什么意思呢?假设经过8次后,数据放入移位寄存器中了,给一个上升沿,那么数据就输出到Q0~Q7上了,只要595没有断电的情况下,Q0~Q7的数据是保持不变的,除非我们再通过DS串行输入端输入8位数据过去,把这数据覆盖掉,否则在没断电的情况下是保持不变的。这有一个什么好处呢?这对我们驱动电子屏是非常实用,他可以实现不闪屏,我现在要这8个灯亮,它就一直保持着亮,不会因为其他情况让数据发生变化,假设后面这几个IO口还要拿来做别的用(11、12),Q0~Q7要保持不变,这就是他的锁存器的功能。在数码管那节课就体现出来了。

/MR(10):低电平清0移位寄存器,我们不需要把它清零,所以在电路图上直接接了VCC。电路图上标的不一样,标的/SRCLR。

/OE(13):高电平为进制输出状态(高阻态),我们要用到他的串入并出所以这个脚必须接到GND上。电路图上标的E。如果这个脚为高电平,那么Q0~Q7和点阵屏的IO口就是断开的了,断开后没有电压电流,就息屏了,不工作了。如果在IO口多的情况下,我们可以把这个脚接IO,给一个高电平就会息屏,点阵的16个脚相当于悬空,没有任何能量过去,LED灯就会全熄灭,再给个低电平,两个芯片的Q0~Q7又都接上了,数据是保持不变的,上一次是什么内容,这一次还是那个内容,就实现了闪屏的效果。

VCC(16):接电源,2~6V。

GND(8):接地。

级联:

DS输入的数据,当11脚SH_CP产生上升沿,就会把第一位数据送到第一片的移位寄存器最高位上,再发一位数据,那么之前的第一位数据就跑到移位寄存器第七位上,第二位数据跑到移位寄存器第八位上,依次进行16次,最终第一位数据就跑到第二片的移位寄存器的第一位上,第二位数据跑到第二片移位寄存器的第二位上。第16位数据跑到第一片的第八位上,再给ST_CP一个上升沿,16位数据就会都输出出去,通过两片你的Q0~Q7。第一片输出的9~16位,第二片输出的1~8位。

再详细看下原理图:

两个595的时钟线(11和12脚分别接在一起)接在一起,输入端接在第一片(14),另一片的输入接在级联的输出端。电源端加上滤波电容(C1、C2)。

市面上的大点阵屏基本都是由8*8拼接的,就像之前看到的。室外的都是防水的,室内的防水效果就没那么好,其它都是相同的。

看看手册

供电电压2~6V。

引脚图和真值表:

G如果为高,那么QA~QH其它三个不管什么状态,输出为高阻态。G为低,SCLR为低,那么就清除移位锁存器内容。

SCK位上升沿时,移位寄存器移位。RCK为上升沿就把移位寄存器内容传输到输出锁存器。

工作电源要求:

最小2V,最大6V,输出电压最小0,最大VCC。

直流电气特性,作为初学者不用看,成为高手自己设计电路需要了解。

VIN最小输入高电平电压有一个要求,VCC为4.5V,输入还有温度限制,不能低于3.15V,不然就检测不到,不认为是高电平。

VIL最大低电平电压,VCC为4.5V,低电平最大电压不能超过1.35V,超过就不认为是低电平。

这个输出电流只有在级联多片点阵的时候需要考虑的到,如果电流过低,点阵屏就会很暗,就需要做一个电流放大。

 交流特性:

内部结构图:

时序图:

直插的外部封装:


上面,了解了点阵工作原理,点亮灯,动态扫描,学习了74HC595,串入并出,优点节省IO口,熟悉怎么进行串行输入和并行输出,给14输入,再给11接上时钟线,给一个上升沿,数据就进入了移位寄存器,还没输出,输出时,只要给12一个上升沿,8位移位寄存器的数据就输出了。级联,9脚接到下一个芯片的14脚,不论级联多少都只需要3个IO口做输入。串行输入时要搞清楚级联时数据的高低字节。

为了使电子显示屏不闪屏,都是需要用并行输出的,串行可能会闪屏。

proteus仿真问题:

  1. CON6是接插件、插针端子,叫CONN-H6
  2. 点阵屏上面引脚是列,高有效,从左到右是1~8列,下面引脚是行,低有效,从左到右是1~8行。即共阴极点阵屏。
  3. Q0是最高位,Q7是最低位,不知道你们记错没有,反正我记错了哈哈

接下来开始编程

亮左上角第一个灯(应该说是靠近9脚那侧的)

这里应该是讲错了,视频中说Q0是低位,可实际Q0是高位

 1 #include<reg51.h>
 2 
 3 #define uchar unsigned char
 4 #define uint unsigned int
 5 
 6 sbit DIO=P3^4;
 7 sbit S_CLK=P3^5;
 8 sbit R_CLK=P3^6;
 9 
10 //uchar code Table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
11 //                    0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F   无显示
12 
13 void main()
14 {
15     //点亮点阵左上方第一个点row 0x80 , col 0xfe
16     //串入并出,所以要一位一位的发送给DIO
17     uchar i=0,dat;//一个字节数据是一个常量,发数据要进行移位、运算,常量不可以
18     dat=0xfe;//先给列的值,因为列是第二个芯片控制,先给了第一个芯片,当再赋值行时,会把列挤给第二块芯片
19     //并且先发送低位再发送高位
20     for(i=0;i<8;i++)
21     {
22         S_CLK=0;
23         R_CLK=0;
24         if(dat&0x01)
25             DIO=1;
26         else
27             DIO=0;
28         S_CLK=1;
29         dat>>=1;
30     }
31     dat=0x80;
32     for(i=0;i<8;i++)
33     {
34         S_CLK=0;
35         R_CLK=0;
36         if(dat&0x01)
37             DIO=1;
38         else
39             DIO=0;
40         S_CLK=1;
41         dat>>=1;
42     }
43     R_CLK=1;
44     while(1);
45     
46 }

 实际电路板是这样

查了一遍595个引脚输出,发现输出不对。原因暂未发现,但不影响,将错就错吧。

先把串行输入数据写成函数的形式,再让亮另外一个灯,上面那个

 1 #include<reg51.h>
 2 
 3 #define uchar unsigned char
 4 #define uint unsigned int
 5 
 6 sbit DIO=P3^4;
 7 sbit S_CLK=P3^5;
 8 sbit R_CLK=P3^6;
 9 
10 //uchar code Table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
11 //                    0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F   无显示
12 
13 void Send_Byte(uchar dat);
14 
15 void main()
16 {
17     //串入并出,所以要一位一位的发送给DIO
18     //先给列的值,因为列是第二个芯片控制,先给了第一个芯片,当再赋值行时,会把列挤给第二块芯片
19     //并且先发送低位再发送高位
20     Send_Byte(0xfe);
21     Send_Byte(0x40);
22     R_CLK=1;
23     while(1);
24     
25 }
26 void Send_Byte(uchar dat)
27 {
28     uchar i=0;
29     S_CLK=0;
30     R_CLK=0;
31     for(i=0;i<8;i++)
32     {
33         if(dat&0x01)
34             DIO=1;
35         else
36             DIO=0;
37         S_CLK=1;
38         dat>>=1;
39         S_CLK=0;
40     }
41 }

接下来显示汉字:电

取字模,我们用到了软件PCtoLCD2002

 设置

然后新建8,8

 

点击生成字模就可以了

{0xEF,0x01,0x6D,0x01,0x6D,0x01,0xEE,0xE0}

这是列选的数据

 1 #include<reg51.h>
 2 #include<intrins.h>
 3 
 4 #define uchar unsigned char
 5 #define uint unsigned int
 6 
 7 sbit DIO=P3^4;
 8 sbit S_CLK=P3^5;
 9 sbit R_CLK=P3^6;
10 
11 uchar code Table[]={0xEF,0x01,0x6D,0x01,0x6D,0x01,0xEE,0xE0};//电,列选table表
12 
13 void Send_Byte(uchar dat);
14 
15 void main()
16 {
17     uchar j=0,row;
18     
19     while(1)
20     {
21         row=0x80;
22         for(j=0;j<8;j++)
23         {
24             Send_Byte(Table[j]);//送每一行的列选值
25             Send_Byte(row);//分别扫描每一行,一次一行
26             R_CLK=1;//Send_Byte中置低了
27             R_CLK=0;//再次变低
28             row=_cror_(row,1);
29         }
30     }
31 }
32 void Send_Byte(uchar dat)
33 {
34     uchar i=0;
35     S_CLK=0;
36     R_CLK=0;
37     for(i=0;i<8;i++)
38     {
39         if(dat&0x01)
40             DIO=1;
41         else
42             DIO=0;
43         S_CLK=1;
44         dat>>=1;
45         S_CLK=0;
46     }
47 }

不过呢,这是倒着的,仿真出不来

但是这个取模软件可以帮助我们

点完后就这样了,然后再生成字模就可以了

接下来显示:电子

 1 #include<reg51.h>
 2 #include<intrins.h>
 3 
 4 #define uchar unsigned char
 5 #define uint unsigned int
 6 
 7 sbit DIO=P3^4;
 8 sbit S_CLK=P3^5;
 9 sbit R_CLK=P3^6;
10 
11 uchar code Table[][8]={
12     0xE0,0xEE,0x01,0x6D,0x01,0x6D,0x01,0xEF,
13     0xE7,0xF7,0xF7,0xF7,0x80,0xF7,0xFB,0xC3
14 };//电,列选table表
15 
16 void Send_Byte(uchar dat);
17 
18 void main()
19 {
20     uchar j=0,k=0,row;
21     
22     while(1)
23     {
24         for(k=0;k<2;k++)
25         {
26             row=0x80;
27             for(j=0;j<8;j++)
28             {
29                 Send_Byte(Table[k][j]);//送每一行的列选值
30                 Send_Byte(row);//分别扫描每一行,一次一行
31                 R_CLK=1;//Send_Byte中置低了
32                 R_CLK=0;//再次变低
33                 row=_cror_(row,1);
34             }
35         }
36     }
37 }
38 void Send_Byte(uchar dat)
39 {
40     uchar i=0;
41     S_CLK=0;
42     R_CLK=0;
43     for(i=0;i<8;i++)
44     {
45         if(dat&0x01)
46             DIO=1;
47         else
48             DIO=0;
49         S_CLK=1;
50         dat>>=1;
51         S_CLK=0;
52     }
53 }

会发现电和子一起显示了,怎么办?自己想一下办法

你会发现延时不行,原因是,延时的话,每一个点显示的时间很长,而不是一个字

所以我们需要把一个字的时间加长,也就是里面的for循环多来几次

 1 #include<reg51.h>
 2 #include<intrins.h>
 3 
 4 #define uchar unsigned char
 5 #define uint unsigned int
 6 
 7 sbit DIO=P3^4;
 8 sbit S_CLK=P3^5;
 9 sbit R_CLK=P3^6;
10 
11 uchar code Table[2][8]={
12     0xE0,0xEE,0x01,0x6D,0x01,0x6D,0x01,0xEF,
13     0xE7,0xF7,0xF7,0xF7,0x80,0xF7,0xFB,0xC3
14 };//电,列选table表
15 
16 void Send_Byte(uchar dat);
17 
18 void main()
19 {
20     uchar j=0,k=0,row;
21     uint z;//别写成了uchar...uchar最大256
22     while(1)
23     {
24         for(k=0;k<2;k++)
25         {
26             row=0x80;
27             for(z=0;z<1000;z++)
28             {
29                 for(j=0;j<8;j++)
30                 {
31                     Send_Byte(Table[k][j]);//送每一行的列选值
32                     Send_Byte(row);//分别扫描每一行,一次一行
33                     R_CLK=1;//Send_Byte中置低了
34                     R_CLK=0;//再次变低
35                     row=_cror_(row,1);
36                 }
37             }
38         }
39     }
40 }
41 void Send_Byte(uchar dat)
42 {
43     uchar i=0;
44     S_CLK=0;
45     R_CLK=0;
46     for(i=0;i<8;i++)
47     {
48         if(dat&0x01)
49             DIO=1;
50         else
51             DIO=0;
52         S_CLK=1;
53         dat>>=1;
54         S_CLK=0;
55     }
56 }
原文地址:https://www.cnblogs.com/IceHowe/p/10739213.html