上面两张都是 74HC595 芯片管脚图,左侧的 1 脚是 QB,而右侧芯片的 1 脚是 Q1,左侧芯片的 11 脚是 SCK,而右侧芯片的 11 脚是 SH_CP,还有很多其他管脚不一样,其实这个都没有什么,每个人在绘制芯片管脚图时命名可能不一样而已,看一个芯片重点是管脚功能。
15 和 1 到 7 脚 QA–QH:并行数据输出
9 脚 QH 非:串行数据输出
10 脚 SCLK 非( MR) : 低电平复位引脚
11 脚 SCK( SHCP) : 移位寄存器时钟输入
12 脚 RCK( STCP) : 存储寄存器时钟输入
13 脚 G 非( OE) : 低电平输出有效
14 脚 SER( DS) : 串行数据输入
74HC595 是具有 8 位移位寄存器(SCK)和一个存储器(RCK),三态输出功能。移位寄存器和存储器是单独的时钟。数据在 SCK 的时钟脉冲上升沿输入,在 RCK 的上升沿进入到存储器中。如果两个时钟连在一起,则移位寄存器总是比存储器早一个脉冲。移位寄存器有一个串行输入(DS),和一个串行输出(Q7 非),和一个异步的低电平复位,存储寄存器有一个并行 8 位的,具有三态的总线输出,当 MR 为高电平,OE 为低电平时,数据在 SHCP 上升沿进入移位寄存器,在 STCP 上升沿输出到并行端口。前一片芯片的Q7非连接下一片的DS,则两个74HC595芯片就实现了级联。
本实验使用到硬件资源如下:
(1)8*8LED 点阵模块
(2)74HC595(A)模块
开发板上的 74HC595 模块电路如下图所示:
A2-A4:
A5-A7:
在A2-A4板子上:当J24端子上的VCC与OE短接时,OE输出高电平,因为74HC595芯片是OE输入低电平有效,因此就无法使用LED点阵。而GND与OE短接时,OE输出低电平,此时使用LED点阵。
在A5-A7板子上:GND直接连接OE非,因此默认使用LED点阵。而在J24端子上的三个脚是用来连接74HC595芯片上的RCLK、SRCLK和SER的。可和A2-A4开发板一样使用P35连接第一个脚,P36连接第二个脚,P34连接第三个脚。
芯片工作时通过SER传入一个字节,按字节的八位从高到低传入,从而QA到QH输出SER的八位。QA为最低位,QH为最高位。SER数据的每一位在SRCLK的时钟脉冲上升沿每上升一次存储一位数据在寄存器SCK中,在RCLK上升沿时一次性传输到存储器RCK中。
从上图中可以看出,该电路是独立的,74HC595 模块内使用了 2 块 74HC595芯片,它们采用了级联方式,即 RCLK 和 SRCLK 管脚并联在一起,并且 74HC595(A)的输出 QH 非连接到 74HC595(B)的串行输入口 SER。并且每块芯片的输出端都连接到对应的端子上,74HC595(A)的输出连接到 J27 端子,74HC595(B)的输出连接到 J32 端子。74HC595 需要用到的控制管脚 RCLK、SRCLK、SER 并未直接连接到 51 单片机的 IO 上,而是连接到 J24 端子上。
如果要想 51 单片机能够控制 74HC595 输出数据,就必须将单片机管脚通过导线连接到 J24 端子上。因此需使用 3 根杜邦线将单片机的管脚与 J24 端子连接。由于 74HC595 模块电路是独立的,所以使用任意单片机管脚都可以,为了与A2-A4开发板同步方法,这里使用 P3.4-P3.6 管脚来控制 74HC595 输出数据,将 74HC595(A)的输出端子 J27 连接在点阵模块行端口上,将单片机 P0 口连接到点阵模块列端口上,即行端口连接 LED 发光二极管的阳极,LED 点阵的列则连接发光二极管的阴极。如果想让发光二极管点亮,则阴极为低电平,阴极为高电平。
#include"reg52.h"
typedef unsigned char u8;
typedef unsigned int u16;
sbit SRCLK=P3^6;
sbit rCLK=P3^5;//RCLK c51内已经定义过了,避免重定义,所以定义为rCLK
sbit SER=P3^4;
#define LEDDZ_COL_PORT P0//定义点阵列的控制端口P0
u8 ghc595_buf[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//全局数组存储595输入数据的缓存,实现点阵从下往上点亮
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
//HC595发送数据函数
void hc595_write_data(u8 dat)//不能定义u8 data,因为data为c51的内部关键字。
{
u8 i=0;
for(i=0;i<8;i++)
{
SER=dat>>7;
dat<<=1; //dat=dat<<1
SRCLK=0; //设置时钟脉冲
delay_10us(1);//设置持续时间
SRCLK=1;
delay_10us(1);
}
rCLK=0;
delay_10us(1);
rCLK=1;
}
void main()
{
u8 i=0;//c51规定变量必须在开头定义,否则会报错
LEDDZ_COL_PORT=0x00;
while(1)
{
for(i=0;i<8;i++)
{
hc595_write_data(ghc595_buf[i]);
delay_10us(50000);//延时500ms
hc595_write_data(ghc595_buf[0x00]);//消隐
}
}
}
首先定义好 74HC595 控制管脚,以及点阵列控制口,代码中重新定义了一个 ms 级延时函数delay_ms,该函数与前面 delay_10us类似,都是利用循环占用 CPU 起到延时效果。然后又定义了 74HC595 的控制函数hc595_write_data,该函数完全按照 74HC595 的通信时序要求编写,主要要注意
的是 74HC595 是先传输字节的高位后传输低位,所以需要将字节低位移动到高位传输,在传输数据时,要注意移位寄存器时钟和存储寄存器时钟的先后顺序,将要写入的数据先传输到 74HC595 寄存器中,即在准备好每位数据时要将 SRCLK进行一个上升沿变化,此时即可将数据传输到寄存器内,待循环 8 次即一个字节传输到寄存器中时,就可以来一个存储时钟上升沿,此时就可以将 74HC595 寄存器中的数据全部一次传输到 595 端口输出。最后就是在 main 函数中调用 74HC595的控制函数,将实验中要实现的效果数据写入进去,从而控制 LED 点阵的阳极,而阴极由 P0 口控制,默认初始化时已经设置为 0,也就是说只要 595 输出高电平,那么对应的行就会点亮。
最先发送的字节经过后面发送的字节的推送,依次往前推送。例如先发送0x01,在发送0x02,在发送0x03,再发送0x04。则经过四次传输,0x01存储在第四个595芯片,0x02存储在第三个595芯片,0x03存储在第二个595芯片,0x04存储在第一个595芯片。
74HC595(A和B)连接的是16 *16点阵的行;74HC595(C和D)连接的是16 *16点阵的列。
根据点阵中二极管的特性。点阵的列为发光二极管的阴极,点阵的行为发光二极管的阳极。想让某个二极管点亮,则使其所在行为高电平,列为低电平。
#include"reg52.h"
typedef unsigned char u8;
typedef unsigned int u16;
sbit SRCLK=P3^6;
sbit rCLK=P3^5;//RCLK c51内已经定义过了,避免重定义,所以定义为rCLK
sbit SER=P3^4;
u8 ghc595_buf[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//全局数组存储595输入数据的缓存,实现点阵从下往上点亮
void delay_1ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);//注意分号
}
//HC595发送数据函数
void hc595_write_data(u8 dat1,u8 dat2,u8 dat3,u8 dat4)//dat1对应HC595芯片A,dat2对应B...
{
u8 i=0;
for(i=0;i<8;i++)
{
SER=dat4>>7;
dat4<<=1; //dat4=dat4<<1
SRCLK=0; //设置时钟脉冲
delay_1ms(1);//设置持续时间
SRCLK=1;
delay_1ms(1);
}
for(i=0;i<8;i++)
{
SER=dat3>>7;
dat3<<=1; //dat3=dat3<<1
SRCLK=0; //设置时钟脉冲
delay_1ms(1);//设置持续时间
SRCLK=1;
delay_1ms(1);
}
for(i=0;i<8;i++)
{
SER=dat2>>7;
dat2<<=1; //dat2=dat2<<1
SRCLK=0; //设置时钟脉冲
delay_1ms(1);//设置持续时间
SRCLK=1;
delay_1ms(1);
}
for(i=0;i<8;i++)
{
SER=dat1>>7;
dat1<<=1; //dat1=dat1<<1
SRCLK=0; //设置时钟脉冲
delay_1ms(1);//设置持续时间
SRCLK=1;
delay_1ms(1);
}
rCLK=0;
delay_1m(1);//RCLK的上升沿
rCLK=1;
}
void main()
{
u8 i=0;//c51规定变量必须在开头定义,否则会报错
while(1)
{
for(i=0;i<8;i++)
{
hc595_write_data(ghc595_buf[i],0,0,0);
delay_1ms(500);//延时500ms
hc595_write_data(0,0,0,0);//消隐
}
for(i=0;i<8;i++)
{
hc595_write_data(0,ghc595_buf[i],0,0);
delay_1ms(500);//延时500ms
hc595_write_data(0,0,0,0);//消隐
}
}
}