前天成功完成了1602八线制的实验,今天在之前的实验基础上稍加修改完成了四线制的操控。四线制和八线制的区别主要在四线制少连了数据线的低四位DB0-DB3,这样一来LCD_E、LCD_WR、LCD_RS、四条数据线DB4-DB7,总共占用七个引脚,比原来占用11个引脚好多了,其实还可以把LCD_WR也省了,这样就只占用了6个引脚。我的电路图和上次一样,就是去掉DB0-DB3:
代码如下:
#include"LCD_1602_4wire_solution.h"
void LcdWrite(alt_u8 data,alt_u8 RS)
{
//set LCD_DATA on write state
IOWR_ALTERA_AVALON_PIO_DIRECTION(LCD_DATA_BASE, 0xff);
//set 1 to LCD_RS for writing data,0 for writing command
IOWR_ALTERA_AVALON_PIO_DATA(LCD_RS_BASE, RS);
//set 0 to LCD_RW for writing pattern
IOWR_ALTERA_AVALON_PIO_DATA(LCD_RW_BASE, WRITE);
//write data
IOWR_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE, data >> 4);
//set LCD_E
usleep(1);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x01);
//clear LCD_E
usleep(3);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x00);
//usleep(1000);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE, data);
//set LCD_E
usleep(1);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x01);
//clear LCD_E
usleep(3);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x00);
}
void LcdWriteCmd(alt_u8 command)
{
LcdCheckBusy();
LcdWrite(command,CMD);
}
void LcdWriteData(alt_u8 data)
{
LcdCheckBusy();
LcdWrite(data,DATA);
}
alt_u8 LcdRead(alt_u8 RS)
{
alt_u8 read_data;
//set LCD_DATA on read state
IOWR_ALTERA_AVALON_PIO_DIRECTION(LCD_DATA_BASE, 0x00);
//set 1 to LCD_RS for reading data,0 for reading command
IOWR_ALTERA_AVALON_PIO_DATA(LCD_RS_BASE, RS);
//set 1 to LCD_RW for reading pattern
IOWR_ALTERA_AVALON_PIO_DATA(LCD_RW_BASE, READ);
//set LCD_E
usleep(1);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x01);
//read data
usleep(1);
read_data = IORD_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE);
//clear LCD_E
usleep(2);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x00);
usleep(1);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x01);
read_data = read_data << 4;
//read data
usleep(1);
read_data |= IORD_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE);
//clear LCD_E
usleep(2);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x00);
return read_data;
}
alt_u8 LcdReadCmd()
{
alt_u8 read_cmd;
LcdCheckBusy();
read_cmd = LcdRead(CMD);
return read_cmd;
}
alt_u8 LcdReadData()
{
alt_u8 read_data;
LcdCheckBusy();
read_data = LcdRead(DATA);
return read_data;
}
void LcdCheckBusy()
{
while(LcdRead(CMD) & 0x80);
}
void LcdClearScreen()
{
LcdWriteCmd(0x01);
}
void InitLcd()
{
//clear the LCD_E
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x00);
usleep(200000);
//write third time 0x38 to LCD 1602,do not check busy,third time to ensure initial succeed
LcdWrite(0x28,CMD);
usleep(5000);
LcdWrite(0x28,CMD);
usleep(5000);
LcdWrite(0x28,CMD);
usleep(5000);
//write 0x28 once more,set display mode(16*2,5*7,4bit)
LcdWriteCmd(0x28);
//close screen
LcdWriteCmd(0x08);
//clear screen
LcdWriteCmd(0x01);
//set cursor mode(N=1)
LcdWriteCmd(0x06);
//open the screen
LcdWriteCmd(0x0c);
}
void LocateXY(alt_u8 x,alt_u8 y)
{
x &= 0x0f; //ensure the x between 0 and 31
y &= 0x01; //ensure that the y is 1 or 0
if(y)
{
LcdCheckBusy();
LcdWriteCmd(x | 0x40 | 0x80);
}
else
{
LcdCheckBusy();
LcdWriteCmd(x | 0x80);
}
}
void PutChar(char value,alt_u8 x,alt_u8 y)
{
LocateXY(x,y);
LcdWriteData(value);
}
alt_u8 PutStr(char *value,alt_u8 x,alt_u8 y)
{
alt_u8 len = 0,i;
while(value[len] > 31) len ++;
for(i = 0;i < len;i ++)
{
PutChar(value[i],x ++,y);
if(x == 16) //Cyclic display
{
x = 0;
y ^= 0x01;
}
}
return len; //return the length of the string
}
头文件:
#ifndef LCD_1602_4WIRE_SOLUTION_H_
#define LCD_1602_4WIRE_SOLUTION_H_
#define DATA 1
#define CMD 0
#define READ 1
#define WRITE 0
#include
#include
#include
#include
//check if the LCD 1602 is in busy
void LcdCheckBusy();
//set the beginning coordinate of the word
void LocateXY(alt_u8 x,alt_u8 y);
//LCD writing function,the argument RS decide a data or a command to be written
void LcdWrite(alt_u8 data,alt_u8 RS);
//LCD reading function,the argument RS decide a data or a command to be read
alt_u8 LcdRead(alt_u8 RS);
//write a command to LCD 1602
void LcdWriteCmd(alt_u8 command);
//write a byte of data to LCD 1602
void LcdWriteData(alt_u8 data);
//read a command from LCD 1602
alt_u8 LcdReadCmd();
//read a data from LCD 1602
alt_u8 LcdReadData();
/*use the following function to initial the LCD 1602 and print words */
//initial the LCD 1602
void InitLcd();
//print a char
void PutChar(char value,alt_u8 x,alt_u8 y);
//print a string
alt_u8 PutStr(char *value,alt_u8 x,alt_u8 y);
//clear screen
void LcdClearScreen();
#endif /* LCD_1602_4WIRE_SOLUTION_H_ */
#include"LCD_1602_4wire_solution.h"
int main()
{
InitLcd();
PutChar('1',0,0);
PutStr("hello",1,1);}
return 0;
}
其实四线制的代码要点就是每次写和读操作时要将数据分两次写或读,先写或读高四位再写或读低四位。
上面七个引脚的一下就成功了,从网上发现有人只用了六个引脚就是了一下,却搞了好久,现在说说自己的经验。
六个引脚是将LED_WR直接接地了,这样一直是写模式。事实上,我们会发现读模式只有在读busy的时候才会用到,而我们是可以用延时来代替检测忙的。刚开始不成功,后来发现LCD_WR不能直接接地,要串个电阻再接地(关键),我串了个100欧姆的。还有关于要用延时多久来代替检测忙,我也做了测试,我的LCD延时700us可以工作,600us就不能了,是不是所有LCD1602都这样我就不知道了,推荐延时时多延时一点比较保险,比如延时1ms。
代码只要在上面修改两个地方:
void LcdWrite(alt_u8 data,alt_u8 RS)
{
//set LCD_DATA on write state
IOWR_ALTERA_AVALON_PIO_DIRECTION(LCD_DATA_BASE, 0xff);
//set 1 to LCD_RS for writing data,0 for writing command
IOWR_ALTERA_AVALON_PIO_DATA(LCD_RS_BASE, RS);
/*这里注释掉*/
//IOWR_ALTERA_AVALON_PIO_DATA(LCD_RW_BASE, WRITE);
//write data
IOWR_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE, data >> 4);
//set LCD_E
usleep(1);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x01);
//clear LCD_E
usleep(3);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x00);
//usleep(1000);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE, data);
//set LCD_E
usleep(1);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x01);
//clear LCD_E
usleep(3);
IOWR_ALTERA_AVALON_PIO_DATA(LCD_E_BASE, 0x00);
}
忙检测改为下代码:
void LcdCheckBusy()
{
usleep(700);
}
至于关于四线制比八线制显示速度慢多少是可以计算的,每次写操作时要多4us左右,显示一个字符大约多十几us,这个估算没有考虑指令执行时间,对于高速FPGA,应该还在十几或几十us。