ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LCD1602液晶屏(续)

2022-05-02 21:35:37  阅读:179  来源: 互联网

标签:void cmd 液晶屏 unsigned char LCD 指令 LCD1602


从前面的分析中知道,在HD44780控制芯片忙的时候,是不能对其进行写入操作的,所以在写入指令或数据时都需要进行判忙的操作,其时序如下图所示(8位数据模式)。

从上图中可看到,当HD44780在执行内部操作时,其数据的最高位DB7为高电平,表示忙,只有内部操作结束时,DB7才为低电平,表示空闲,这时才能对HD44780进行写入操作。为了方便,一般通过C语言把以上“判忙”这一过程封装成一个函数,如下所示(C51,以下同)。

bit Busy(void)
{
    unsigned char value;
    LCD_EN = 1;                //使能端拉高电平
    _nop_();                   //调用汇编指令延时一个空指令周期
    value = DataPort;          //读取数据
    LCD_EN = 0;                //忙信号结束,拉低使能端电平
    _nop_();                  //调用汇编指令延时一个空指令周期
    return (bit)(value & 0x80);//返回1为忙,0为不忙
}

一般情况下,判忙是为了等待,即忙则等待,直到不忙为止,因此,还可以进一步封装成一个“忙等待”函数,如下所示。

void WaitForEnable(void)
{
    DataPort = 0xff;        //数据线电平拉高
    LCD_RS = 0;             //选择指令寄存器
    LCD_RW = 1;             //选择读方式
    while(Busy());          //忙等待
}

有了忙等待函数,就可以封装出其他函数了。接下来封装”写指令“函数,写指令可根据需要进行判忙或不判忙选择,依据前面给出的写操作时序,函数代码如下所示。

void LcdWriteCommand(unsigned char cmd, unsigned char busy)
{
    if(busy)            //若busy为1则判忙,否则不判忙
      WaitForEnable();          //忙等待
    LCD_RS = 0;                 //选择指令寄存器
    LCD_RW = 0;                 //选择写方式
    _nop_();                 //调用汇编指令延时一个空指令周期
    LCD_EN = 1;                 //使能端拉高电平
    _nop_();                 //调用汇编指令延时一个空指令周期
    DataPort = cmd;             //把命令数据送到数据线上
    _nop_();                 //调用汇编指令延时一个空指令周期       
    LCD_EN = 0;                 //拉低使能端电平,完成写入
    _nop_();                 //调用汇编指令延时一个空指令周期
}

以同样的方式,可以封装出一个”写数据“的函数,写数据都需要先判忙,函数代码如下所示。

void LcdWriteData(unsigned char data)
{
    WaitForEnable();         //忙等待
    LCD_RS = 1;               //选择数据寄存器    
    LCD_RW = 0;               //选择写方式
    _nop_();               //调用汇编指令延时一个空指令周期
    LCD_EN = 1;               //使能端拉高电平
    _nop_();               //调用汇编指令延时一个空指令周期
    DataPort = data;          //把显示数据送到数据线上
    _nop_();               //调用汇编指令延时一个空指令周期    
    LCD_EN = 0;               //拉低使能端电平,完成写入
    _nop_();               //调用汇编指令延时一个空指令周期
}

有了写指令的函数之后,”初始化“函数也可以写了,依据前面的初始化过程,函数代码如下所示(8位数据模式)。

void LcdInit(bit N, bit ID, bit S, bit D, bit C, bit B)
{
    unsigned char cmd = 0x30;
    LcdWriteCommand(cmd, 0);     //写第一次
    Delay_nms(5);                  //延时5ms
    LcdWriteCommand(cmd, 0);      //写第二次
    Delay_nms(1);                  //延时1ms
    LcdWriteCommand(cmd, 0);       //写第三次    
    if(N)
        cmd |= 0x08;               //双行
    else
        cmd &= ~0x08;              //单行            
    LcdWriteCommand(cmd, 1);       //确定显示行数及字形大小,检测忙信号
    LcdWriteCommand(0x08, 1);      //关闭显示,检测忙信号
    LcdWriteCommand(0x01, 1);      //清屏,检测忙信号
    cmd = 0x04;
    if(ID)
        cmd |= 0x02;               //AC递增
    else
        cmd &= ~0x02;              //AC递减
    if(S)
        cmd |= 0x01;               //画面移动
    else
        cmd &= ~0x01;              //光标移动
    LcdWriteCommand(cmd, 1);       //配置进入模式,检测忙信号
    cmd = 0x08;
    if(D)
        cmd |= 0x04;               //开启显示
    else
        cmd &= ~0x04;              //关闭显示
    if(C)
        cmd |= 0x02;               //显示光标
    else
        cmd &= ~0x02;              //不显示光标
    if(B)
        cmd |= 0x01;               //开启闪烁
    else
        cmd &= ~0x01;              //关闭闪烁
    LcdWriteCommand(cmd, 1);       //屏幕显示、光标显示、闪烁等配置,检测忙信号
    LcdWriteCommand(0x02, 1);      //光标复位
}

接下来需要封装一个”光标定位“函数,用于确定欲显示字符的起始位置,函数代码如下所示。

void LocateXY(char posX, char posY)
{
    unsigned char temp;
    temp = posX & 0x0f;       //屏蔽高4位,限定横坐标X的范围为0~15
    posY &= 0x01;             //屏蔽高7位,限定纵坐标Y的范围为0~1
    if(posY)        
      temp |= 0x40;           //第二行显示,地址码+0x40,因第二行起始地址为0x40
    temp |= 0x80;             //设定DDRAM地址的指令DB7恒为1(即0x80)
    LcdWriteCommand(temp, 1); //把命令temp写入LCD中,检测忙信号
}

接下来封装一个“显示单字符”函数,用于在确定位置显示一个字符,函数代码如下所示。 

void DisplayOneChar(unsigned char x, unsigned char y, unsigned char data)
{
    LocateXY(x, y);          //定位欲显示字符的位置
    LcdWriteData(data);     //将要显示的数据data写入LCD
}

最后可以封装一个“显示字符串”函数,用于一次性显示多个字符,函数代码如下所示。 

void DisplayString(unsigned char x, unsigned char y, unsigned char const *ptr)
{
    unsigned char i, j=0;
    while(ptr[j] > 31)
      j++;                  //ptr[j]>31时为ASCII码,j累加,计算出字符串长度
    for(i=0; i<j; i++)
    {
         DisplayOneChar(x++, y, ptr[i]);  //显示单个字符,同时x坐标递增
         if(x == 16)
         {
              x = 0;
              y ^= 1;         //当每行显示超过16个字符时换行继续显示
         }
    }
}

 一般应用拥有以上函数就够了,但也可以根据需要再封装一下其他函数,这里再封装一个获取当前光标所在地址(AC)的函数,即”读取光标位置“函数,依据前面给出的读操作时序,函数代码如下所示。

unsigned char LcdReadAC(void)
{
    unsigned char value;
    WaitForEnable();        //忙等待
    DataPort = 0xff;        //数据线电平拉高
    LCD_RS = 0;             //选择指令寄存器    
    LCD_RW = 1;             //选择读方式
    _nop_();                //调用汇编指令延时一个空指令周期
    LCD_EN = 1;             //使能端拉高电平
    _nop_();                //调用汇编指令延时一个空指令周期
    value = DataPort;       //读取数据
    LCD_EN = 0;             //拉低使能端电平
    _nop_();                //调用汇编指令延时一个空指令周期
    return (value & 0x7f);  //返回低7位的AC值
}

以同样的方式,可以封装出一个”读数据“的函数,函数代码如下所示,其参数为欲读取的内容所在地址。

unsigned char LcdReadData(unsigned char AC)
{
    unsigned char value;
    LcdWriteCommand((AC | 0x80), 1);  //定位欲读取的地址
    WaitForEnable();              //检测忙信号
    DataPort = 0xff;              //数据线电平拉高
    LCD_RS = 1;                    //选择数据寄存器    
    LCD_RW = 1;                    //选择读方式
    _nop_();                    //调用汇编指令延时一个空指令周期
    LCD_EN = 1;                    //使能端拉高电平
    _nop_();                    //调用汇编指令延时一个空指令周期
    value = DataPort;              //读取数据
    LCD_EN = 0;                    //拉低使能端电平
    _nop_();                    //调用汇编指令延时一个空指令周期
    return value;                //返回读取到的内容
}

接下来再封装一个“光标移动”函数,用于移动光标,其参数为0时光标左移,为1时光标右移,函数代码如下所示。

void CursorMove(bit dir)
{
    if(dir)
        LcdWriteCommand(0x14, 1);  //光标右移
    else
        LcdWriteCommand(0x10, 1);  //光标左移
}

以同样的方式,可以再封装出一个”画面移动“的函数,用于移动画面,其参数为0时画面左移,为1时画面右移,函数代码如下所示。

void ImageMove(bit dir)
{
    if(dir)
        LcdWriteCommand(0x1c, 1);    //画面右移
    else
        LcdWriteCommand(0x18, 1);    //画面左移
}

最后还需要封装一个上面初始化函数中用到的毫秒级延时函数,函数代码如下所示,该函数的延时时间与单片机相关,这里是51单片机12MHz晶振为例,若用其他单片机应酌情更改。

void Delay_nms(unsigned int ms)  
{
    unsigned int x,y;
    for(x=ms; x>0; x--)
        for(y=110; y>0; y--);
}

实际使用时,上面的所有函数可写在一个C语言文件,然后把相关的定义放在头文件中,主程序只需要把该文件添加进工程,即可调用所有的函数了。头文件一般作如下定义。

//========================引脚宏定义========================
sbit LCD_RS = P1^0;      //RS脚输出高电平
sbit LCD_RW = P1^1;      //RW脚输出高电平
sbit LCD_EN = P2^5;      //EN脚输出高电平  
//========================端口宏定义========================
#define DataPort P0       //P0为数据端口
//========================初始化参数的宏定义======================== 
#define SINGLE 0          //单行显示
#define DOUBLE 1          //双行显示
#define INC 1             //AC递增
#define DEC 0             //AC递减
#define SHIFT 1           //画面滚动
#define NOSHIFT 0         //画面不动
#define OPEN 1            //打开显示
#define CLOSE 0           //关闭显示
#define SHOW 1            //显示光标
#define NOSHOW 0          //不显示光标
#define BLINK 1           //光标闪烁
#define NOBLINK 0         //光标不闪烁
#define LEFT 0            //向左移动
#define RIGHT 1           //向右移动
//===========================函数声明============================
void Delay_nms(unsigned int ms); //延时n毫秒
bit Busy(void);                    //判忙
void WaitForEnable(void);         //忙等待
void LcdWriteData(unsigned char data);         //写数据
unsigned char LcdReadData(unsigned char AC);     //读某地址的数据
void LcdWriteCommand(unsigned char cmd, unsigned char busy); //写命令
unsigned char LcdReadAC(void);             //读当前地址
void LcdInit(bit N, bit ID, bit S, bit D, bit C, bit B);//初始化
void LocateXY(char posx, char posy);    //定位显示位置
void DisplayOneChar(unsigned char x, unsigned char y, unsigned char data); //显示单字符
void DisplayString(unsigned char x, unsigned char y, unsigned char const *ptr);//显示字符串
void CursorMove(bit dir);      //光标移动
void ImageMove(bit dir);      //画面滚动

在使用时,根据实际的连接情况,只需要更改上述头文件中的“引脚宏定义”和“端口宏定义”部分,其余均不需要改动。 

标签:void,cmd,液晶屏,unsigned,char,LCD,指令,LCD1602
来源: https://www.cnblogs.com/fxzq/p/16214841.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有