对于8*8点阵首先要解决的是驱动的问题,可以采用74HC595驱动。
74HC595是移位寄存器,但它又不是普通的移位寄存器,它具有输出锁存和输出使能控制端,可以很方便的发挥出串口转并口的功能,可以方便的用于扩展单片机的IO口。
若是普通的移位寄存器,比如74LS164,因为74LS164本身没有并行输出控制端,因而在单片机对它串行输入过程中,其输出端状态会不断变化,所以在利用74LS164扩展IO口时,在它的输出端应加接输出三态门控制,以保证串行输入结束后再输出数据。而74HC595由于输出锁存功能和输出使能的存在,可以方便的等串行数据完全输入了再给输出锁存有效时钟信号使数据输出。
至于扩展IO口时的硬件连接则有以下两种方法。
方法一:单片机自身提供的串口的工作方式0,方式0就是专门用来扩展IO口的。连接时将RXD与扩展片的数据输入端相连,TXD与扩展片的移位时钟输入端相连即可。
方法二:可以利用单片机的任意2到3个IO口模拟串行发送数据的时序。(此时序细节要结合扩展片具体时序细节要求而定)
8*8点阵驱动的问题解决了,剩下的就是扫描显示的问题。
扫描有两种常用的方法:行扫描和列扫描。
所谓行扫描即是先让第一行全部置于有效位,再送所需的列码,接着再按同样的方法依次扫描所有行,并同时送去所有所需的相应列码即可。
所谓列扫描即是先让第一列全部至于有效位,再送所需的行码,接着再按同样的方法依次扫描所有列,并同时送去所需的相应行码即可。
以下是关于常用点阵取模软件PCtoLCD2002里的几个概念的解释:
(1)阴码和阳码
简单的讲,如果行接的是二极管的阳极,列接的是二极管的阴极,则采用行扫描时列取模方式即为阳码,采用列扫描时行的取模方式即为阴码。对应的若行接二极管阴极,列接二极管阳极,则采用行扫描时取模方式为阴码,采用列扫描时取模方式为阳码。
(2)逆向和顺向
分两种情况讨论如下:一、若采用行扫描,当点阵横向的数据位从左到右依次为高位到低位时,此时的取模方式为顺向取模,反之则为逆向取模;二、若采用列扫描,当点阵纵向的数据位从上到下依次为高位到低位时,此时的取模方式为顺向取模,反之为逆向取模。总之一句话:顺向即是指点阵数据位上高下低,左高右低
配合上合理的算法,点阵还可以实现滚动显示 。简单例程如下:(采用列扫描,两片74HC595驱动,自右向左滚动显示I
LOVE YOU)
//LED8*8滚动显示
//列扫描,低电平有效
//关于滚动的算法一定要深刻理解,只是让所扫描的行码间隔一定时间依次后移而已
//包含头文件
#include
#include "74HC595.H"
//此处的头文件是74HC595的驱动程序,附于后
//全局变量定义
unsigned char i;
unsigned int m,n;
//代码库
#define num sizeof(table) //字模长度
unsigned char code
aa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
unsigned char code table[]= {
//取模方式 阴码 列扫描 逆向
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
//雪岩注:这一行行码是为了造成一上电就从最右面开始移动的效果,同时也起到了隔离显示的作用
0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00,
0x00,0x7F,0x40,0x40,0x40,0x00,0x00,0x00,
0x00,0x7F,0x41,0x41,0x7F,0x00,0x00,0x00,
0x00,0x0F,0x70,0x70,0x0F,0x00,0x00,0x00,
0x00,0x7F,0x49,0x49,0x41,0x00,0x00,0x00,
0x03,0x04,0x78,0x04,0x03,0x00,0x00,0x00,
0x00,0x7F,0x41,0x41,0x7F,0x00,0x00,0x00,
0x00,0x3F,0x40,0x40,0x3F,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
//这最后一行的行码的存在非常关键,它帮助完成了全部滚动的算法,若没有他最后一个字母的滚动将很难实现
};
//显示函数
void Display(void)
{
Ser_IN((aa[i])); //列扫描数据
Ser_IN(table[i + n]);
//查表取出行扫描数据
Par_OUT();
//输出显示
i++; if(i == 8) i = 0;
//循环扫描
m++; if(m == 100) {m = 0; n++;}
//滚动速度控制,100毫秒滚动一下
if(n == num-7) n = 0;
//循环显示
}
//定时器初始化
void T0_init(void)
{
TMOD = 0x01;
TH0 = 0xfc; //1MS定时
TL0 = 0x18;
IE = 0x82;
TR0 = 1;
}
//定时器中断服务
void T0_intservice(void) interrupt 1 using 0
{
TH0 = 0xfC;
TL0 = 0x18;
Display();
}
//主函数
void main (void)
{
T0_init();
while(1);
}
所需头文件74HC595.H如下:
//Note: 74HC595驱动
// __
__
//Note: MR 主复位接电源正极, OE 使能端,输出有效接电源负极
#ifndef __74HC595_H__
#define __74HC595_H__
sbit SD = P1^4; //串行数据输入
sbit ST_CK = P1^5; //存储寄存器时钟输入
sbit SH_CK = P1^6; //移位寄存器时钟输入
//函数声明
void Ser_IN(unsigned char Data); //串行数据输入
void Par_OUT(void);
//并行数据输出
//void Ser_Par(unsigned char Data); //串行输入,并行输出
//串行数据输入
void Ser_IN(unsigned char Data)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
SH_CK = 0;
//CLOCK_MAX=100MHz
SD = (bit)(Data & 0x80);//雪岩注:此处不用强制转换符亦可
Data <<= 1;
SH_CK = 1;
}
}
//并行数据输出
void Par_OUT(void)
{
ST_CK = 0;
ST_CK = 1;
//由1到0并不是没有延时,因为ST_CK=1执行也需要1个机器周期,故相当于延时了
}
#endif
明天先完成一个利用点阵滚动显示的时钟,之后着手液晶1602的应用。
|