2 NC RD S L OE A B C D N 20
1 N N N N N N N N N N 19
其中,RD是数据,S是时钟,L是锁存,OE是使能,ABCD是16行的地址信号,N是接地。
诸昌钤的《LED显示屏系统及工程技术》中提到,对于串行传输方式来说,列数据准备时间可能相当长,在行扫描周期确定的情况下,留给行显示的时间就太少了,以至影响到LED亮度。解决串行传输中列数据准备和列数据显示的时间矛盾问题,可以采用重叠处理方法。即在显示本行各列数据的同时,准备下一行的数据。为了达到重叠处理的目的,列数据的显示就需要具有锁存功能。
Donald的这个LED板,硬件设计原理,就是按这种思路来的,虽然只是64x16,不大,但电路是现成的,按专家的这个思路来编驱动软件,将具有更强的扩展性。
Donald让51单片机的串行口工作在方式0,这样就是同步串行方式,RXD为数据,TXD为时钟,分别接LED条屏的RD和S信号。只要对SBUF操作就行了。
对于64x16的显示大小,Donald用了一个全局变量来作为一行显示的缓存:
uchar g_ucLineBuf[8]
这样,一个函数就实现将一行数据串行移入到LED条屏的寄存器里:
void OutLineBuf()
{
char i;
for(i = 0; i < 8; i++){
SBUF = g_ucLineBuf[i];
while(TI == 0); // Wait for sending complite.
TI = 0;
}
}
要显示一屏的图文,就需要结合地址线来显示,按一定顺序(从上到下,或从下到上)将每行的数据输出到LED屏上,并加上必要延时,使图像清晰。
如下,是Donald的静态显示一屏的函数。
/**************************************************************
Name: DispPage(uchar ucPage, uchar ucEft, uchar ucTime)
Func:
Display a page of data. Just a LED screen. But not include
the switching to the next.
Para:
uchar ucPage: the number of the page.(是页号,我已经将每页的数据存在CODE段里了)
uchar ucEft: the effect of displaying the page(显示效果,有闪烁,反转,全亮,全灭)
uchar ucTime: the showing time(显示的时间,定时器定时单位的个数,我是按0.2秒一个单位)
Return: Null
**************************************************************/
void DispPage(uchar ucPage, uchar ucEft, uchar ucTime)
{
char i, k;
uint addrSrc;
if(ucTime == 0) return;
g_ucShowTime = ucTime; // 一个全局变量,在定时器中断里修改
TR0 = 1; // Start timer0
while(g_ucShowTime){
if((ucEft == DISP_EFT_BLINK) && (g_ucBlink > 4)){
LED_E = 1;
}
else{
addrSrc = ucPage * 128; // 每一屏显示数据的起始地址
for(i = 0; i < 16; i++){ // 16行的显示
for(k = 0; k < 8; k++){
if(ucEft == DISP_EFT_CLEAR){ // All Led dark
g_ucLineBuf[k] = 0xff;
}
else if(ucEft == DISP_EFT_ALL){ // All Led bright
g_ucLineBuf[k] = 0x00;
}
else{
g_ucLineBuf[k] = ucDataSrc[addrSrc + i * 8 + k];
if(ucEft == DISP_EFT_REVERSE){ // Reverse
g_ucLineBuf[k] = ~g_ucLineBuf[k];
}
}
}
OutLineBuf();
LED_E = 1;
LED_AD = i << 4;
LED_L = 0; // Low to High, 595 Lock signal
LED_L = 1;
LED_E = 0;
delay();
}
}
}
TR0 = 0; // Stop timer0
}
上述函数显示每一行的思路是这样的,先将第一行的数据准备好放在输出缓存g_ucLineBuf里,然后调用OutLineBuf()函数输出,这时,数据还在595的寄存器里,还没最后显示。接着,不让LED条屏显示,设好地址后,通过一个由低到高的锁存信号,将数据锁存到595的输出端,再将LED条屏使能,数据就显示出来了,加上必要延时,使图像清晰。
为什么准备地址(即行信号)时要先禁止LED条屏再使用使之显示,因为开始时Donald没有这么做,发现屏幕有鬼影,原因就是串行输出数据后准备地址,这个执行过程有相当短暂的时间,会将原来上一行数据短暂残留在本行,产生鬼影。通过使用端的应用,就能避免这个问题。
当然,除了静态显示,还有切换过程的显示,只需使用一定算法,将下一个页要显示的数据准备在g_ucLineBuf输出缓存中就行了。这样,就有了如下左右移动切换和上下移动切换的函数。
void DispSwitchLR(uchar ucPageNo, uchar ucEft, uchar ucTime);
void DispSwitchUD(uchar ucPageNo, uchar ucEft, uchar ucTime);
最后,编写一个总的显示函数void Display(uchar ucPage),通过调用DispPage()、DisSwitchLR()和DispSwitchUD函数,就实现将每一页按闪烁,反转的方式显示一段自己设好的时间,再以左右移动,上下移动,或直接切换的方式切换到下一页。
每一页的显示时间和切换方式都存在CODE段的ucPageInfo[]中,每一页的显示数据放在CODE段的ucDataSrc[]中。
下面这个简单的程序是Donald编来生成ucPageInfo信息的。
