第四篇:从驱动 lcd1602显示字符例程学习---软件驱动硬件的开发流程

第四篇 lcd1602

亚龙236实验台显示模块上的小长条液晶屏就是今天的主角:1602液晶。我们的任务是编程序驱动它来显示一些英文字母。1602这个名字来源于它可以每行显示16个字母,一共两行。
玩过乐高类的拼装玩具吗?厚厚的一叠说明书,按照说明一步一步拼。搞开发也一样,必须从第一个语句慢慢拼起来,这是急不得的事情。心平气和、头脑清醒是必须的。驱动1602也需要看看1602的说明书—亚龙资料里面有的,在datasheet文件夹里找RT1602.pdf。rt1602pdf文件—来自百度文库
最有用的是 第三页 引脚说明、第四页 读写时序、12页开始的指令解释。

pg3:

RS:寄存器选择输入端
RS=1:指向数据寄存器
RS=0:指向指令寄存器

RW:读写控制输入端
RW=0:写操作 RW=1:读操作

E:使能信号输入端
读操作时,高电平有效;
写操作时,下降沿有效;

pg4:

pdf自己看。关键点:
1、E信号频率:最小周期1000ns,意思就是每1us最多操作一次----lcd1602是个老牛车,反应慢的很,为了信号稳定,需要每次延时4us以上。
2、E信号脉宽:最小450ns。意思是 E信号的高电平最少保持0.4us,实际上可能需要1us或更长(数据线过长、没有屏蔽等都会影响传输)。
3、读忙不是必须,如果是竞赛等环境,可以用牺牲效率(延时到肯定不忙)的方法来避免读忙。这样可以少写几行程序。

编程过程:

1 、先定好硬件连接计划
#define  lcd_data  P0 
 sbit lcd_rs=P2^0;
 sbit lcd_rw =P2^1;
 sbit lcd_e =P2^2;

数据端口接P0,rs接P2.0,rw接P2.1,en接P2.2。
这几句可以看作是—硬件接口层。把硬件操作接口转换成四个符号,操作硬件就是读写这四个符号。

2、根据硬件手册写基本操作指令

比如:手册第12页第一个指令—清屏
第四篇:从驱动 lcd1602显示字符例程学习---软件驱动硬件的开发流程_第1张图片按照手册,我们应该把rs置0,rw置0,DB置00000001(16进制0x01),然后要把DB上的数据写入到寄存器,写入的方法在第4页
第四篇:从驱动 lcd1602显示字符例程学习---软件驱动硬件的开发流程_第2张图片时序图上明确了步骤:rs和rw置0,再把e置1,再送DB数据,再把e置0,在e下降沿这一瞬间锁存数据到寄存器。然后可以撤掉DB,撤掉rs、rw。
写成c语句:

	lcd_rs= 0;		//置0 rs
   	lcd_rw = 0;		//置0 rw
   	lcd_e = 1;		//置1 e
   	lcd_data = 0x01;	//送清屏命令到端口
   	lcd_e= 0;		//置0 e,产生e信号下降沿,写入指令到寄存器

看其它指令,大同小异,就是命令字节不一样,比如显示开关指令
第四篇:从驱动 lcd1602显示字符例程学习---软件驱动硬件的开发流程_第3张图片
只需要把0x01换成0x0c(00001000)就可以设置指令:开显示、关光标、关闪烁。
那就改一改,写成通用的写指令函数,如下:

void write_command(uchar com)
 { 
   	lcd_rs= 0;
   	lcd_rw = 0;
	lcd_e=1;
   	lcd_data = com;
   	lcd_e= 0;
 }

这样就可以直接传指令码写指令了。
使用方法如下:

write_command(0x01); 		//清屏指令
3、写要显示的数据

方法同写指令,只是rs不一样

void write_data(uchar dat)
 {
	lcd_rs = 1;
	lcd_rw = 0;
	lcd_e=1;
	lcd_data = dat;
	lcd_e= 0;
 }

注意,这里面都没有加读忙程序,有时候会显示不完全,可以这样改一改:加延时

void write_data(uchar dat)
 {
	lcd_rs = 1;
	lcd_rw = 0;
	lcd_e=1;
	lcd_data = dat;
	lcd_e= 0;
	delayms(1);			//延时1ms,确保lcd不忙。
 }

这样组合起来,已经可以写指令写数据。
上面的两个函数直接操作端口、端子,可以看作是数据传输层,主管数据传输路径和时序。
下面的工作,就是调用写好的两个函数,写指令、写数据来实现具体显示功能。相当于用户应用层。
看看1602工作流程:
看这个资料北京 同创 1602.pdf第10页初始化流程。
写初始化函数如下:

void LCD_init(void)
 { write_command(0x38); // 8-bits, 2 lines, 7x5 dots
   write_command(0x0C); // no cursor, no blink, enable display
   write_command(0x06); // auto-increment on
   write_command(0x01); // clear screen
 }

写显示字符串的函数:

void string(uchar line, uchar col,uchar *s)
 { 
	 if (line<2)	//如果行数小于二,显示在第一行
	 write_command(0x80+col);	//写指令,定位光标到第一行第col列。
	 else			//否则,应该显示在第二行
		 write_command(0xc0+col);	//写指令,定位光标到第二行第col列。
   while(*s>0)
    { write_data(*s++);	//写数据,显示
     delayms(1);	//如果不判忙就要加延时。
    }
 }

至此,已经可以在指定位置显示英文字库。

完整程序如下:(加了判忙,判忙程序看注释)

/* 
 * Created:   2020.7.31
 * Processor: AT89C51
 * Compiler:  Keil for 8051
 *作者:西峰职专 李枝蔚(修改自proteus例程)
 */

#include 
#include 
#include 

//---------------------------------------------
// 定义硬件连接
#define  lcd_data  P0 
 sbit lcd_rs=P2^0;
 sbit lcd_rw =P2^1;
 sbit lcd_e =P2^2;
// Define new types
typedef unsigned char   uchar;
typedef unsigned int    uint;

// 函数声明
void check_busy(void);
void write_command(uchar Com);
void write_data(uchar datab);
void LCD_init(void);
void string(uchar line, uchar col,uchar *s);
//------------------------------------
/*******************************************
    LCD1602 Driver mapped as IO peripheral
*******************************************/  
// 判忙
void check_busy(void)
 { do								//do循环,最少执行一次。
    { 
		lcd_data =0x80;		//BF置1,假设 忙
		lcd_e=0;
		lcd_rs=0; 
		lcd_rw = 1;
		lcd_e = 1;  				//读状态字
		_nop_(); 					//脉冲展宽 ,手册上对lcd_e宽度有要求。
    } while(lcd_data & 0x80);  //读端口看BF是1吗,是1 则继续循环。
   lcd_e = 0;
 }

// 写指令
void write_command(uchar com)
 { check_busy();
	lcd_rs= 0;
	lcd_rw = 0;
	lcd_e=1;
	lcd_data = com;
   lcd_e= 0;
 }

// 写数据
void write_data(uchar dat)
 {
	check_busy();
	lcd_rs = 1;
	lcd_rw = 0;
	lcd_e=1;
	lcd_data = dat;
	lcd_e= 0;
 }

// 初始化
void LCD_init(void)
 { write_command(0x38); // 8-bits, 2 lines, 7x5 dots
   write_command(0x0C); // no cursor, no blink, enable display
   write_command(0x06); // auto-increment on
   write_command(0x01); // clear screen
 }

// 在指定位置显示字符串
 //line: 显示目的行取值 1或2
 //col:显示目的列 取值0-15
 //*s :要显示的字符串
void string(uchar line, uchar col,uchar *s)
 { 
	 if (line<2)
	 write_command(0x80+col);			//光标定位第一行第col列
	 else
		 write_command(0xc0+col);
   while(*s>0)
    { write_data(*s++);
    }
 }
//------------------------------------
void main(void)
 { 
   // Write your code here
	LCD_init(); 
	string(1,0,"Have a nice day!");
	string(2,1,"  Hello world!");
	 while(1)
		{ 
			;
		} 
 }

仿真结果:
第四篇:从驱动 lcd1602显示字符例程学习---软件驱动硬件的开发流程_第4张图片二次编辑 ----添加无判忙的程序。 取消判忙的优点:rw始终为零,可以直接接地,可以不写判忙程序(少记12行)。缺点:要加延时,仿真表示延时少于600us不能正常显示—Have的H木有了。如图,如果延时太小就直接显示白板。
第四篇:从驱动 lcd1602显示字符例程学习---软件驱动硬件的开发流程_第5张图片去判忙加延时方案的程序:

/* Main.c file generated by New Project wizard
 *
 * Created:   周日 11月 25 2018
 * Processor: AT89C51
 * Compiler:  Keil for 8051
 */

#include 
#include 
#include 

//---------------------------------------------
// Define P3 pins
#define  lcd_data  P0 
 sbit lcd_rs=P2^0;
 sbit lcd_rw =P2^1;
 sbit lcd_e =P2^2;
// Define new types
typedef unsigned char   uchar;
typedef unsigned int    uint;

// Function Prototypes

void write_command(uchar Com);
void write_data(uchar datab);
void LCD_init(void);
void string(uchar ad ,uchar *s);
void lcd_test(void);
void delay();
//------------------------------------
/*******************************************
    LCD1602 Driver mapped as IO peripheral
*******************************************/  
// Delay

 void delay()
 { 
	 uint j=80;
    while(--j); 
 }

// Write a command
void write_command(uchar com)
 { //check_busy();
   lcd_e = 0;
   lcd_rs= 0;
   lcd_rw = 0;
   lcd_data = com;
   lcd_e= 1;
   _nop_();
   lcd_e= 0;
   delay();
	 lcd_e= 0;
 }

// Write Data
void write_data(uchar dat)
 { //check_busy();
   lcd_e= 0;
   lcd_rs = 1;
   lcd_rw = 0;
   lcd_data = dat;
   lcd_e= 1;
   _nop_();
   lcd_e= 0;
   delay();   
 }

// Initialize LCD controller
void LCD_init(void)
 { write_command(0x38); // 8-bits, 2 lines, 7x5 dots
   write_command(0x0C); // no cursor, no blink, enable display
   write_command(0x06); // auto-increment on
   write_command(0x01); // clear screen
 }

// Display a string
void string(uchar ad, uchar *s)
 { write_command(ad);
   while(*s>0)
    { write_data(*s++);
    }
 }
//------------------------------------
void main(void)
 { 
   // Write your code here
	LCD_init(); 
	 string(0x80,"Have a nice day!");
      string(0xC0,"  Proteus VSM");
	 
   while(1)
    { 
      delay();
		//write_command(0x40);
    } 
	 
   
 }

你可能感兴趣的:(第四篇:从驱动 lcd1602显示字符例程学习---软件驱动硬件的开发流程)