51单片机LED点阵实验(74HC595)
使用74HC595 点亮LED 点阵
(以普中开发板为例)
关于74HC595(串转并)
74HC595的原理图
74HC595的原理图如下所示(PS:图中二极管的方向错了)
HC595芯片的功能:串行输入,并行输出。
各个引脚的功能
看如何给芯片接线,并使用:(注:引脚上加一个“非横线”,表示低有效)
1、D0~D7,并行输出,接LED。
2、16号和8号引脚,分别接Vcc,Gnd。
3、OE,13号引脚,低电平才输出有效,让它接地。
4、10号引脚,主复位引脚,低有效。目前不需要复位,直接接VCC就好.
5、14号引脚SER,接在集成板的P3^4,为串行输入端。
6、11号引脚SRCLK,接在集成板的P3^6;上升沿时,将串行口RCLK的一位,移位输入。
7、12号引脚RCLK,接在集成板的P3^5;上升沿时,将移入的8位数据,送到存储寄存器。
高位注释:SDI先发送高位,在发送低位,高位发送到对应的Q管脚的相应位置,每一次的发送为伴随着一个上升沿。
上面的只是使用方法,如果还想继续深究,可以查看数电当中的串并行输入输出。
关于点阵
点阵的实际原理图
(列的标号从上到下依次为为1-7,行的标号从左到右依次为a-h)
8*8 点阵共由 64 个发光二极管组成,且每个发光二极管是放置在行线和列线
的交叉点上,当对应的某一行置 1 电平,某一列置 0 电平,则相应的二极管就亮;
如要将第一个点点亮,则 1 脚接高电平 a 脚接低电平,则第一个点就亮了;如果
要将第一行点亮,则第 1 脚要接高电平,而(a、b、c、d、e、f、g、h )这些
引脚接低电平,那么第一行就会点亮;如要将第一列点亮,则第 a 脚接低电平,
而(1、2、3、4、5、6、7、8)接高电平,那么第一列就会点亮。
代码
#include <reg51.h> //此文件中定义了单片机的一些特殊功能寄存器
#include <intrins.h>
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
//--定义使用的IO口--//
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
sbit LED=P0^7;
/* 函 数 名 : Hc595SendByte(u8 dat1,u8 dat2)
* 函数功能 : 通过595发送2个字节的数据
* 输 入 : dat1:第2个595输出数值
* * dat2: 第1个595输出数值
* 输 出 : 无
**************************/
void Hc595SendByte(u8 dat) // 调用函数SendByte
{
u8 a;
SRCLK = 1;
RCLK = 1;
for(a=0;a<8;a++) //发送8位数
{
SER = dat >> 7; //从最高位开始发送
dat <<= 1;
SRCLK = 0; //发送时序
_nop_(); //调用nop函数,进行一个机器周期的延时
_nop_(); // 延时两个机器周期
SRCLK = 0; //初始化SRCLK,重回低电平,为下一个上升沿输入做准备
}
RCLK = 0; //RCLK从0到1,上升沿到达,输出前面接收的段码数据
_nop_();
_nop_();
RCLK = 1; //初始化RCLK,重回低电平,为下一个上升沿输出做准备
}
void main()
{
Hc595SendByte(0x01); // 0000 0001
P0= 0X7F ; // 0111 1111
while(1) ;
}
实物展示略
用点阵显示爱心
代码:
(实现的是空心的爱心,代码来自网络)
#include "reg51.h" //注意这里使用“reg52.h"可能会出现重复定义错误,因为52.h中已定义过RCLK
#include<intrins.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
u8 ledduan[]={0x38,0x44,0x42,0x21,0x42,0x44,0x38,0x00};//每一列的7~0二极管状态
u8 ledwei[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//第1列到第8列位选
void delay(u16 i)
{
while(i--);
}
void Hc595SendByte(u8 dat)
{
u8 a;
SRCLK=0;
RCLK=0;
for(a=0;a<8;a++) //重复发送ledduan[i]中的段码,使每一列对应二极管亮
{
SER=dat>>7; //从最高位开始发送
dat<<=1; //左移依次左移1位,发送次高位,
SRCLK=1; //0-1,上升沿到达,发送一位数据(段码中的1位二进制值)
_nop_();
_nop_(); // 内置延时函数
SRCLK=0; //初始化SRCLK,重回低电平,为下一个上升沿输入做准备
}
RCLK=1; //RCLK从0到1,上升沿到达,输出前面接收的段码数据
_nop_();
_nop_();
RCLK=0; //初始化RCLK,重回低电平,为下一个上升沿输出做准备
}
void main()
{
u8 i;
while(1)
{
P0=0x7f;
for(i=0;i<8;i++)
{
P0=ledwei[i]; //位选
Hc595SendByte(ledduan[i]); //发送段选数据
delay(100); //时间足够短,视觉暂留
Hc595SendByte(0x00); //消隐,将dat置回初值隐
}
}
}
实物展示略
字模软件
如何显示自己心仪的图形
在这里我们要借助于取字模软件,借助这个软件我们可以轻松的得到控制显示图案的段码。点阵就是由64个led灯组成的,所以他的发光原理其实和数码管差不多。接下来就是取字模软件的使用。
1. 下载取字模软件
软件下载地址:添加链接描述
链接:https://pan.baidu.com/s/1-Zm8lmzGhydn_cSaZ-7-IQ
提取码:cop8
2. 单击“新建图像”命令,根据开发板上的点阵,把宽度和高度调成 8 x 8 ,然后点击“确定” 。
- 单击左侧的“模拟动画”命令,再单击“放大格点”,一直放大到最大,就可以在 8 x 8的点阵图形中用鼠标填充黑色画图形了。
4.可以按照自己的喜好画出图形或字
(此处可以结合开发板上面的点阵原理图设置“纵向取模”、“字节倒序”等方式取出符合自己要求的图形)
5.点击“取模命令”,单击“C51格式”后,在“点阵生成区”自动生成了8个字节的数据。
- 将生成的代码放入下面数组中
(例如0x78,0xFC,0x7E,0x3F,0x3F,0x7E,0xFC,0x78)
u8 ledduan[]={0x78,0xFC,0x7E,0x3F,0x3F,0x7E,0xFC,0x78};
其余的程序代码与上面爱心的代码基本差不多,只需要替换 ” ledduan ” 的数组即可。
点阵的显示运用
点阵的循环显示
**·**点阵的循环显示可以用二维数组来实现,利用二维数组和延时函数,可以让点阵按照要循环显示。
例如下列代码:
//循环显示0123456789SNOW
#include<reg51.h>
#include<intrins.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit DS = P3^4; //数据输入(0或1)
sbit RCLK = P3^5; //并行输出开关
sbit SRCLK = P3^6; //串行输入开关
unsigned char code ledwei[8] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe}; //列选通控制
unsigned char code ledduan[14][8]= //点阵字码 二维数组
{
{0x00,0x00,0x3e,0x41,0x41,0x41,0x3e,0x00}, //0
{0x00,0x00,0x00,0x00,0x21,0x7f,0x01,0x00}, //1
{0x00,0x00,0x27,0x45,0x45,0x45,0x39,0x00}, //2
{0x00,0x00,0x22,0x49,0x49,0x49,0x36,0x00}, //3
{0x00,0x00,0x0c,0x14,0x24,0x7f,0x04,0x00}, //4
{0x00,0x00,0x72,0x51,0x51,0x51,0x4e,0x00}, //5
{0x00,0x00,0x3e,0x49,0x49,0x49,0x26,0x00}, //6
{0x00,0x00,0x40,0x40,0x40,0x4f,0x70,0x00}, //7
{0x00,0x00,0x36,0x49,0x49,0x49,0x36,0x00}, //8
{0x00,0x00,0x32,0x49,0x49,0x49,0x3e,0x00}, //9
{0x00,0x00,0x24,0x52,0x4A,0x24,0x00,0x00}, //S
{0x00,0x00,0x7E,0x20,0x18,0x04,0x7E,0x00}, //N
{0x00,0x00,0x3e,0x41,0x41,0x41,0x3e,0x00}, //O
{0x00,0x70,0x08,0x14,0x60,0x14,0x08,0x70}, //W
};
void delay(u16 time);
void Hc595SendByte(u8 dat);
void main()
{
u8 tab, j = 0;
u16 i;
while(1)
{
for(i= 0; i<50; i++ ) //两个字之间的扫描间隔时间
{
for(tab=0;tab<8;tab++) //每一个字符对应8列
{
Hc595SendByte(0x00); //消隐
P0 = ledwei[tab]; //位选,选通某一列
Hc595SendByte(ledduan[j][tab]); //通过HC595发送序列i对应的段选的真值
delay(2);
}
}
j++;
if(j == 14)
{
j = 0;
}
}
}
void delay(u16 time) //延时函数
{
u16 i,j;
for(i=0;i<time;i++)
for(j=0;j<121;j++);
}
void Hc595SendByte(u8 dat)
{
u8 a=0;
RCLK = 0; //输出寄存器状态保持
SRCLK = 0; //移位寄存器状态保持
for(a = 0;a<8;a++) //循环8次,存进8位进制二进制数字
{
DS = dat>>7; //串行输入最高位数据化
dat = dat<<1; //次高位移向最高位
SRCLK = 1; //移位寄存器存储
_nop_(); //延时,保证脉冲宽度足够
_nop_();
SRCLK = 0; //移位寄存器状态保持,为下一次上升沿到来做准备
}
RCLK = 1; //输入移位寄存器数据
_nop_(); //延时,保证脉冲宽度足够
_nop_();
RCLK = 0; //输出寄存器状态保持,为下一次上升沿到来做准备
}
点阵的纵向移动
示例代码如下:
(仅写出主函数部分的代码,但是不要忘了改前面函数相关声明)
void main()
{
int i,b=0;
while(1)
{
int num = 12;
while(num --)
{
P0=0x7f;
for(i=0;i<8;i++)
{
P0=ledwei[i]; //位选 先位选后段选
Hc595SendByte(ledduan[i+b]); //发送段选数据,而且 “i+b”能够实现对duan 数组里面的循环运行从而实现纵向的循环移动
delay(100); //时间足够短,视觉暂留
Hc595SendByte(0x00); //消隐,将dat置回初值隐
}
}
b++;
if(b>=24)
b=0; /* 至于为什么是24而不是32,目前还不清楚,但是我认为是这样的:当为32时运行时最后会出现整个点阵都是亮的现象,因此用24是为了消除此现象 (目前我还没有搞清楚这个问题,目前只是个人的猜测) */
}
}
点阵的横向移动
方法一:因为点阵的横向移动比较麻烦,所以我们可以采用纵向移动的方法来实现,将板子横着侧过来,纵向取模就可以完成了
方法二:主要是运用二维数组。在横向显示的时候需要将所需要的效果把需要的图片都给画下来,再逐一进行取模,比如通过程序每250ms改变一张图片,一张图片为一帧,逐一作出来,即可完成横向显示的效果。每一张的取模取出8个字节的数据,需要存放在一个二维数组内。
例如: