LCD16032驱动程序开发
软硬件环境:CPU : S3C2410 OS : WINCE420.NET
ST7920具有4位/8位并行、2线或3线串行多种接口方式,内部含有国标一级、二级简体中文字库的点阵图形液晶显示模块;其显示分辨率为160×32, 内置8192个16*16点汉字,和128个16*8点ASCII字符集.其外型兼容字符型20*2LCM, “汉显”:可以显示10个×2行16×16点阵的汉字. 也可完成图形显示.
串口驱动:
串口方便调试,也方便硬件的测试,因此我选择使用串口来驱动液晶,连接时只需要简单的三根线就OK了。
ST7920的串口,是非标准串口,不能使用标准串口来驱动,因此我智能用GPIO口来模拟他的串口驱动,首先,我们来看看ST7920说明书上的数据传输协议:
串口数据线模式数据传输过程
串口方式写数据时序图
找出三个板上未使用的GPIO口
GPB5 —— SID 数据
GPB7 —— SCLK 时钟
GPB10 —— CS 片选
首先我们来看片选的函数:
//GPB10 片选
void LCD_CS(BOOL x)
{
if (TRUE == x)
{//设置为高电平
v_pIOPRegs->rGPBDAT |= (1<<10);
}
else
{
(v_pIOPRegs->rGPBDAT &= ~(1<<10));
}
}
使用GPB7模拟的时钟函数:
//GPB7 模拟时钟
void LCD_SCLK_JUMP()
{
v_pIOPRegs->rGPBDAT |= (1<<7);
Delay_n_us(1);
(v_pIOPRegs->rGPBDAT &= ~(1<<7));
}
数据传输:
//GPB5 数据传输
void LCD_SET_DAT(BOOL x)
{
if (x != 0)
{//设置为高电平
v_pIOPRegs->rGPBDAT |= (1<<5);
}
else
{
(v_pIOPRegs->rGPBDAT &= ~(1<<5));
}
}
根据图中的时序我们可以知道这个LCD的数据传输方式,发送一个字节需要分成两个部分,首先传送一个字节的高4位,后面添加0,然后再传送低4位,每次发送的时候又是从字节的最高位开始发送!根据这个可以写出传输一个字节的子函数:
void LCD_SEND_BYTE(UCHAR dat)
{
UCHAR temp, i;
temp = dat;
temp &= 0xf0;
for(i = 0; i < 8; i++)
{
LCD_SET_DAT(temp&0x80);
LCD_SCLK_JUMP();
temp <<= 1;
}
temp = dat;
temp &=0x0f;
temp <<= 4;
temp &= 0xf0;
for(i = 0; i < 8; i++)
{
LCD_SET_DAT(temp&0x80);
LCD_SCLK_JUMP();
temp <<= 1;
}
}
刚开始的时候我认为这个字节传送函数能够通用于指令和字符的传送,但是仔细研究时序图之后我发现传送指令和显示字符的时候,同步字是不同的。
传送指令需要先发送0xf8,紧跟着是控制指令,这个时候我们可以连续发送多个控制指令而不需要重复发送同步字。这和传送显示字符不同,他需要先发送0xfa作为显示字符的同步字,同样在发送同步字之后可以连续发送需要显示的字符码(ASCII)。这里我封装了两个函数,一个指令传送函数,一个是“打印”函数:
void CtlCode(UCHAR command_data)
{
UCHAR i, temp;
temp = 0xf8;
Delay_n_us(100);
LCD_CS(TRUE);
for(i = 0; i < 8; i++)
{
LCD_SET_DAT(temp&0x80);
LCD_SCLK_JUMP();
temp <<= 1;
}
LCD_SEND_BYTE(command_data);
LCD_CS(FALSE);
}
void LCDPrint(PCHAR DatStr, CHAR Position)
{
UCHAR i, temp;
temp = 0xfa;
Sleep(1);
if (Position != NULL) //这个增加一个显示位置,这样可以增加打印灵活
{
CtlCode(Position);
}
LCD_CS(TRUE);
for(i = 0; i < 8; i++)
{
LCD_SET_DAT(temp&0x80);
LCD_SCLK_JUMP();
temp <<= 1;
}
while(*DatStr != '\0')
{
LCD_SEND_BYTE(*DatStr);
DatStr++;
}
LCD_CS(FALSE);
}
这样一来基本的显示函数就封装完成了,接下来就是驱动的初始化函数,这需要根据lcd的特性来编写,我是这样写的:
BOOL lcd_init()
{
UCHAR command_data;
Delay_n_us(40);
command_data=0x30;
CtlCode(command_data); /*功能设置:一次送8位数据,基本指令集*/
command_data=0x06;
CtlCode(command_data); /*点设定:显示字符/光标从左到右移位,DDRAM地址加1*/
command_data=0x0c;
CtlCode(command_data); /*显示设定:开显示,不显示光标,不允许当前显示位反白闪动*/
command_data=0x01;
CtlCode(command_data); /*清DDRAM*/
command_data=0x02;
CtlCode(command_data); /*DDRAM地址归位*/
command_data=0x80;
CtlCode(command_data); /*把显示地址设为0X80,即为第一行的首位*/
return TRUE;
}//显然我吧过程复杂化了许多^=^
LCD驱动完成了,接下来编写一个测试程序吧!
Note:这个液晶不需要我们同步更新,在需要更改显示内容的时候才又必要向他发送信息!汉字和字符同事显示的时候需要注意字符型是8位,汉字是16位,基数个字符后面如果紧跟汉字的话就会出现显示乱码,当然如果字符后面已经没有汉字了,我们就不要做特殊处理了。解决方法其实很简单,我们可以在打印函数中增加一些检测代码,检测字符后面是否还要显示汉字,如果是,我们再检测字符个数,如果是偶数则不需要任何处理,如果是基数,则在字符后面添加一个空格(0x20)即可。