专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

AVR 单片机学习笔记–LCD1602模块

作者:佚名   来源:本站原创   点击数:  更新时间:2013年10月21日   【字体:

学完51单片机再来学AVR,感觉很容易上手,LCD1602在学51的时候学过,所以可以直接修改相关的IO口操作即可。现在顺便也复习一下。1602可以显示两行字符,每行可以显示16个字符,可惜的是不能显示中文,在我调试的时候它帮我不少的忙。

一、主要技术参数:

显示容量:            16X2个字符(两行,每行16个字符)
       模块工作电源:              4.5~5.5V
       模块工作电流:              2.0mA (5.0V)
       模块最佳工作电源:        5.0V
       字符尺寸:                     2.95X4.35(WXH)mm

二、IO引脚功能

LCD1602A模块引脚功能

编号

符号

引脚说明

编号

符号

引脚说明

1

VSS

电源地(模块供电)

9

D2

Data I/O

2

VDD

电源正极(模块供电)

10

D3

Data I/O

3

VL

接在滑动电阻可以调节对比度

11

D4

Data I/O

4

RS

数据/命令选择端 (H/L)

12

D5

Data I/O

5

R/W

读/写选择端 (H/L)

13

D6

Data I/O

6

E

使能信号(通知芯片读取数据)

14

D7

Data I/O

7

D0

Data I/O

15

BLA

模块背光灯正极

8

D1

Data I/O

16

BLK

模块背光灯负极

 

三、基本操作时序:

读状态:输入:RS=L、RW=H、E=H                    模块输出:状态字=D0~D7
       写指令:输入:RS=L、RW=L、D0~D7=指令码、E=H       模块输出:无
       读数据:输入:RS=H、RW=H、E=H                               模块输出:数据=D0~D7
       写数据:输入:RS=H、RW=L、D0~D7=数据、E=H          模块输出:无

 状态字说明(因为单片机的速度可能快过1602,所以需要判断当1602是否在忙,或者延时。)

STA7

STA6

STA5

STA4

STA3

STA2

STA1

STA0

D7

D6

D5

D4

D3

D2

D1

D0

STA0-6:当前数据地址指针的数值

STA7

主要用来检查1602模块的是否可以写入或读出操作

1:禁止  0:允许

               

 

四、指令说明(这指令是用来设置1602显示的方式)

显示模式设置

指令码

功能

0

0

1

1

1

0

0

0

设置16X2显示,5X7点阵,8位数据接口

                 

 

显示开/关及光标设置

指令码

功能

0

0

0

0

1

D

C

B

D=1 开显示         D=0 关显示
C=1 显示光标       C=0 不显示光标
B=1 光标闪烁       B=0 光标不闪烁

0

0

0

0

0

1

N

S

N=1 当读或写一个字符后地址指针加1,且光
     标加1(即从左往右显示)
N=0 当读或写一个字符后地址指针减1,且光
     标减1(即从右往左显示)
S=1 当写一个字符,中国男排显示左移(N=1)

                 

 

五、时序图

 

 
 

 


写操作(写指令、写数据)时序图

 


时序参数

写指令时序,RS拉低表示写指令,R/W拉低表示写操作,给DB0-DB7赋值指令数据并保持>40ns时间,将E拉高表示数据已经准备好保持>150ns让1602读取,将E拉低,并延时>10ns一次写操作完毕。

写数据时序,RS拉高表示写数据,R/W拉低表示写操作,给DB0-DB7赋值指令数据并保持>40ns时间,将E拉高表示数据已经准备好保持>150ns让1602读取,将E拉低,并延时>10ns一次写操作完毕。 

六、电路图 
    
 

由于只是写操作,所以R/W直接接地即可。 

PORTA接1602的8位数据引脚

PORTD5接1602的RS引脚 区分数据或命令:H数据,L命令

    PORTD4接1602的E引脚 高脉冲使能,使能脉宽最小150ns 

 

七、完整代码。

// 代码功能:驱动LCD1602显示屏
// 平台:DB-51 Ver2.1开发板
// 控制芯片:ATmega16A
// 时钟:片内时钟
// 设计者:L、QQ:1007566569
//                2013-8-10
//*************************************** 包含必备文件 *******************************************************
#ifndef _STRING_H
   #include "string.h"
#endif

#ifndef uchar         
   #define uchar unsigned char
#endif                 

#ifndef uint
   #define uint  unsigned int
#endif

//*************************************** 移植必须修改 *******************************************************

// 共占用 PORTA一组IO 、PORTD中第4、5 IO  由于只是写操作,所以R/W直接接地即可。 

#define _1602_DATA        PORTA   // 接1602的8位数据引脚
#define _1602_CMDIO       PORTD   // 控制线所对应的IO口
#define _1602_CMDIO_RS    5       // 接1602的RS引脚 区分数据或命令:H数据,L命令
#define _1602_CMDIO_E     4       // 接1602的E引脚 高脉冲使能,使能脉宽最小150ns 

/**************************************************
设计者:L、QQ:1007566569
函数:自定义函数 void My_SetBit(unsigned char *Date, unsigned char Wei, unsigned char ms)
参数:Date:寄存器地址 Wei:更改指定位 ms:若为1则将指定位置1 若为0则将指定位置0
返回值:无返回值,函数里面会更改寄存器值
例子:My_SetBit((unsigned char*)&PORTB, 2, 1); 将PORTB寄存器中的第2位置1
函数描述:
    修改指定寄存器中的指定位,不影响其他位。
**************************************************/
/*** 第二版本 ***/
void My_SetBit(unsigned char *Date, unsigned char Wei, unsigned char ms)
{
   (((ms) == (0)) ? (*Date &= ~(0x1<<Wei)) : (*Date |= (0x1<<Wei)));
}

/* 延时函数 */
void delay_50us_1602(unsigned int x)
{  
   unsigned char y;
   for(;x>0;x--)
      for(y=19;y>0;y--);
}

/* 写入指定命令 由于是写操作 R/W 可以直接接地保持低电平即可*/
void lcdwrcom_1602(unsigned char cdat)
{  
   My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 0);      // E置0,即将改变数据。不允许1602读取数据(IO口状态)
   My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_RS, 0);      // RS拉低 表示是命令
   _1602_DATA = cdat;                                    // 将命令送至与1602的8位数据口相连接的IO口              
   delay_50us_1602(4);                                    // 数据建立时间要 >40ns
   My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 1);      // E使能,命令已经准备好,通知1602读取命令(IO口状态)
   delay_50us_1602(8);                                    // E高电平保持时间要 >150ns,让1602有时间读取完整
   My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 0);      // E置0,不允许1602读取数据(IO口状态)可能改变。
   delay_50us_1602(2);                                    // 根据时序图,让数据再保持一小段时间 >10ns
}
/* 将指定数据写入显示屏 由于是写操作 R/W 可以直接接地保持低电平即可*/
void lcdwrdata_1602(unsigned char dat)
{  
   My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 0);      // E置0,即将改变数据。不允许1602读取数据(IO口状态)
   My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_RS, 1);      // RS拉高 表示是数据
   _1602_DATA = dat;                                    // 将命令送至与1602的8位数据口相连接的IO口              
   delay_50us_1602(4);                                    // 数据建立时间要 >40ns
   My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 1);      // E使能,命令已经准备好,通知1602读取命令(IO口状态)
   delay_50us_1602(8);                                    // E高电平保持时间要 >150ns,让1602有时间读取完整
   My_SetBit((unsigned char*)&_1602_CMDIO, _1602_CMDIO_E, 0);      // E置0,不允许1602读取数据(IO口状态)可能改变。
   delay_50us_1602(2);                                    // 根据时序图,让数据再保持一小段时间 >10ns                                 // 根据时序图,让数据保持一小段时间
}

/* 初始化 1602 */
void lcd_init_1602()
{   // 具体含义请参考 1602官方使用手册
   lcdwrcom_1602(0x38);//0x38设置显示模式为:16X2 显示,5X7 点阵,8 位数据接口
   lcdwrcom_1602(0x0C);//打开显示光标闪烁
   lcdwrcom_1602(0x06);//显示光标移动设置
   lcdwrcom_1602(0x01);//显示清屏    
}

/* 综合函数 API */
/* 参数:Show_1602(第一行显示的数据, 第二行显示的数据, 每个字符显示的时间)*/
void Show_1602(unsigned char Text_1[], unsigned char Text_2[], unsigned int Time)
{
   unsigned char n;
   unsigned int Tmp;
   unsigned char TmpA, TmpC, TmpD;
  
   // 备份IO口原来的配置以备复原,避免影响其他模块代码
   TmpA = DDRA;  
   TmpC = DDRC;
   TmpD = DDRD;
   // 端口初始化
   DDRA = 0xFF;     
   DDRC = 0xC0;
   DDRD = 0x30;

   // 1602 初始化
   lcd_init_1602();
  
   lcdwrcom_1602(0x80+0x00);         // 设置要显示(写入)的位置 0x80-0x8F 第一行的位置
   Tmp = strlen(Text_1);      
   if(Tmp)
   {
      for(n=0; n<Tmp; n++)
      {
         lcdwrdata_1602(Text_1[n]);   // 一个字节一个字节写入
         delay_50us_1602(Time);      // 延时指定时间 可以达到一个字符一个字符慢慢依次显示的效果
      }
   }

   lcdwrcom_1602(0x80+0x40);         // 设置要显示(写入)的位置 0xC0-0xCF 第二行的位置  
   Tmp = strlen(Text_2);
   if(Tmp)
   {
      for(n=0; n<Tmp; n++)
      {
         lcdwrdata_1602(Text_2[n]);   // 一个字节一个字节写入
         delay_50us_1602(Time);      // 延时指定时间 可以达到一个字符一个字符慢慢依次显示的效果
      }
   }
  
   // 恢复端口原来的设置避免影响其他模块的使用。
   DDRA = TmpA;
   DDRC = TmpC;
   DDRD = TmpD;
}

/*  写完这个函数后,灵光一现!擦,被误导了!霖锋老师和郭天祥老师是不是都写懵
   了,搞那么复杂,靠 一个 sprintf 就能搞定。
void Show_1602_Int(unsigned int Shuzi, unsigned int Time)
{
   unsigned char Tdate[]="0123456789";
   char Text[33] = {0};
   unsigned int Tmp;
   unsigned int Weishu = 10;
   char Textlen = 0;

   // 判断位数 ↓
   if(Shuzi < 10 )                        // 如果是一位数 则不必拆分 直接引用
   {
      Text[Textlen] = Tdate[Shuzi];
      Show_1602_Char(Text, "", Time);
      return ;
   }

   while(1)                           // 如果是两位数以上 则先判断几位
   {
      if(Shuzi>=Weishu && Shuzi<Weishu*10)
         break;
      Weishu*=10;
   }
   // 判断位数 ↑

   // 将每一位拆分翻译 ↓
   for(Textlen=0; Weishu; Textlen++)
   {                          
      Tmp = Shuzi/Weishu;        
      Text[Textlen] = Tdate[Tmp];
      Shuzi = Shuzi % Weishu;
      Weishu /= 10;
   }
   // 将每一位拆分翻译 ↑

   Show_1602_Char(Text, "", Time);
}
*/

关闭窗口

相关文章