使用LCD 1602显示内容时首先要初始化LCD1602的显示模式,然后实现对LCD 1602稳定读/写的函数,包括读状态和写内容,对LCD 1602写内容又包括写指令和写数据。
可编写专门的初始化函数来实现,函数内容可包含如下:
(1) 程序运行时,等待约15ms的时间让LCD VDD电压达4.5V。
(2) 用编写的LCD写指令函数设置LCD的显示模式,如设置LCD1602用16 x 2显示、5x7点库、使用8位数据端口。对应送往LCD 1602 8个端口的数据为0x38。
(3) 设置LCD开显示模式,是否显示光标,光标是否闪烁。都设置时8个数据端口的数据为0x0f。
(4) 设置LCD读写字符后地址指针、光标位置是否往后增1,屏幕移动与否。地址指针、光标在读写字符后增1,屏幕不移动时8个端口的数据为0x06。
(5) 清屏。清屏指令为0x01.可在LCD写指令模式设置下写入清屏指令。
清屏指令主要是为了下一次的显示不受上一次显示的干扰。
可专门编写函数来实现。函数内容可包含如下:
(1) 检测LCD忙信号:LCD的D7位是否为1,如果此位为1则表示LCD处于忙碌状态,需要等到D7状态位为0时方可进入写指令操作。
(2) 设置LCD的RS端口为0来表示对LCD进行指令操作,RW端口为0表示往LCD内写东西。二者构成往LCD内写指令。延时等待(时间不长,需参考手册中的时间)保证RS,RW两端口彻底的到达低电平。
(3) 将指令置于LCD的D0-D7位,延时等待保证D0-D7电平得到彻底变化。
(4) 将LCD的EN使能位置为1,延时等待其位彻底变高后将EN置为1,使得EN端口得到一个负脉冲。
(5) EN端负脉冲产生LCD就开始将D0-D7上面的指令往LCD里面写入,此时延时等待一段时间(如5ms,合适的时间可以不断的调试得来)
当要对LCD写数据时,步骤跟写指令时一样,只是需要在以上步骤中将RS设置为高电平,表示对LCD进行数据操作。
在对LCD 1602进行写操作的过程中,有几个写指令是比较常用的。
(1) 清屏指令0x01。在进入当前界面之前只要界面显示,就应该在进入当前界面时进行清屏操作。否则可能会遗留上一个界面的字符。
(2) 置光标LCD 1602光标指令0x80 + LCD显示地址值。此指令可以将光标移到对应的地址上,而且重新输入数据内容时可覆盖上一次的数据。
要使用LCD 1602做一个字符界面的菜单十分容易。就是把在程序数据区域定义好的数据往LCD 1602中写即可。由于此时不一定在LCD显示屏上面每个地方都会显示内容,可能会按照一定的格式和排版显示字符,这个时候就要用到置光标位置指令将光标置位到想要的位置之上再进入数据写入。同时,可能需要在用户输入的时候实现Tab键的功能,能够在用户输入的地方自动切换输入和反复输入。
在程序开始启动时,LCD就应有一个主界面菜单供用户选择。用编写的LCD写指令、写数据函数就可以来实现LCD 1602字符界面菜单的实现。LCD写指令函数用来对LCD输入位置(光标位置)进行置位,LCD写数据函数用来写入数据。Following code is my main menu in LCD 1602:
void LCD_menu()
{
int i;
//第一行第一列开始
lcd_write_command( 0x80 );
for( i = 0; i < strlen( d_line ); i++){
lcd_write_data( d_line[i] );
}
//第一行第二个菜单项
lcd_write_command( 0x80 + ( strlen( d_line ) ) );
for(i = 0; i < strlen( d_circle ); i++){
lcd_write_data( d_circle[i] );
}
//第二行x坐标值
lcd_write_command( 0x80 + 0x40 );
for(i = 0; i < strlen( x_axis ); i++){
lcd_write_data( x_axis[i] );
}
//第二行y坐标值
lcd_write_command( 0x80 + 0x40 + ( strlen( d_line ) ) );
for(i = 0; i < strlen( y_axis ); i++){
lcd_write_data( y_axis[i] );
}
lcd_write_command( 0x80 + 0x40 + strlen( x_axis ) );
}
lcd_write_command、lcd_write_data为编写的LCD写指令、写数据函数。编写这个字符界面的思维很简单,就是用写指令函数将光标置入需要输入字符的位置然后用写数据函数写入数据。最后再次将光标移到需要用户输入的地方。基于前两个程序和在keil中搭建的一个程序框架,编译源文件生成.hex文件下载到单片机显示的效果如下:lcd_write_command、lcd_write_data为编写的LCD写指令、写数据函数。编写这个字符界面的思维很简单,就是用写指令函数将光标置入需要输入字符的位置然后用写数据函数写入数据。最后再次将光标移到需要用户输入的地方。基于前两个程序和在keil中搭建的一个程序框架,编译源文件生成.hex文件下载到单片机显示的效果如下:
LCD 字符主菜单界面
第一行的“*:Line”和“#:Circle”是进一步的子菜单界面。第二行就是接受键盘输入的地方了,在输入x坐标后就要跳到y坐标处等待用户输入坐标。而输入完y坐标后还可能要返回到输入x坐标处重新输入x坐标,再输入y坐标。这就类似于Tab键的功能了笔记于2.3。
矩阵键盘采用线反转法实现按键的扫描,在按键按下之后一定要等待按键的释放之后才能对LCD写入按键的值。因为程序执行的速度比人的动作快许多,我们按一次键程序就已经运行了好几次,会被当做按了好几次键。所以,线反转法扫面按键需要在程序的某个地方等待按键的释放。
在线反转法中,按键被按下时,在线反转后(键盘行列高低电平反转后),只需要等待原本为高电平的引脚值是否变回了高电平,如果为高电平说明按键已经被释放,就可以往LCD中写入按键值。
//键盘反转检测
//键盘反转检测的时候可以确定是哪个引脚,
//然后等待此引脚为高电平,此时代表按键被释放
P3 = ROW_HIGH_COLUMN_LOW;
temp = P3;
temp &= ROW_KEY_ALL_UP;
if( temp != ROW_KEY_ALL_UP ){
temp = P3;
switch( temp ){
case KEY_DOWN_IN_FIRST_ROW:
key_index.row = FIRST_ROW_INDEX;
while( !P3_port4 );
break;
case KEY_DOWN_IN_SECOND_ROW:
key_index.row = SECOND_ROW_INDEX;
while( !P3_port5 );
break;
case KEY_DOWN_IN_THIRD_ROW:
key_index.row = THIRD_ROW_INDEX;
while( !P3_port6 );
break;
case KEY_DOWN_IN_FOURTH_ROW:
key_index.row = FOURTH_ROW_INDEX;
while( !P3_port7 );
break;
default:
key_index.row = READ_KEY_ERROR;
}
}
这是在线反转法检测按键函数中的后一段程序,程序中的while( !P3_portn );语句就是在等待对应的引脚回到高电平。在编写程序的时候确定到底哪一个按键释放后对应的哪一个引脚即可。
在解决了每次按键就只有一个输入后,菜单输入区就可以接受字符了。多个输入区彼此切换应成为一个菜单界面基备的。
要让各个输入区实现Tab式的切换,首先是要用一个按键来触发(Tab按键)。而且还要用标志变量来保存到底该切换到哪一个输入区。在程序的模块化设计中,可以专门的编写一个函数来实现。如果用一个函数来实现,可以用两种思路来实现这个功能。
(1) 用一个全局变量来作为各个输入区的标志变量。用此变量的值来判断LCD光标该跳往各处。
(2) 用局部static的变量来作为各个输入区的标志变量。此时可以将变量定义来函数内部。
至于全局变量和局部静态变量到底哪个好,还得学习一下,空口还说不出来太多。
举一个用局部static变量来实现上图两个坐标的切换,很简单的了:
void x_to_y_to_x()
{
if( in_menu_flag ){
static unsigned char x_cur = 1;
static unsigned char y_cur = 0;
//光标首先在输入x值处,如果用户输入x坐标按确定就跳将LCD的输入y坐标处
if( x_cur ){
lcd_write_command( 0x80 + 0x40 + strlen( d_line ) + strlen( y_axis) );
//光标在输入y坐标处
x_cur = 0;
y_cur = 1;
//如果是在
}else if( y_cur ){
lcd_write_command( 0x80 + 0x40 + strlen( x_axis ) );
//光标在输入x坐标处
x_cur = 1;
y_cur = 0;
}
}else if( in_line_menu_flag ){
static unsigned char location = 1;
if( location == 1 ){
lcd_write_command( 0x80 + BEFOR_X_LENGHT + 5 );
}else if( location == 2 ){
lcd_write_command( 0x80 + 0x40 + BEFOR_X_LENGHT );
}else if( location == 3){
lcd_write_command( 0x80 + 0x40 + BEFOR_X_LENGHT + 5);
}else if( location == 4){
lcd_write_command( 0x80 + BEFOR_X_LENGHT );
location = 0;
}
location++;
}
}
程序表明,如果当前处于in_menu_flag这个菜单界面(上图界面)内,则在接收到切换按键时实现切换。这里用了static变量生命周期长,可以记住变量本身状态(值)的特点实现了这一要求。
后面那段处于in_line_menu_flag中的界面时(每次只有一个界面状态为真),用一个static变量实现了4个输入区的切换。
芯片:STC89C52
显示器: LCD 1602
键盘:一个杜邦线外接的塑料键盘(不晓得专业名)
此次笔记记录完毕。