搜索
bottom↓
回复: 19

完整的LCD1602驱动,可直接使用

[复制链接]

出0入0汤圆

发表于 2012-2-7 15:12:33 | 显示全部楼层 |阅读模式
这是本人开发板的光盘附带代码,觉得写得不错,大家品评一下。

点击此处下载 ourdev_716736QW5XQD.rar(文件大小:31K) (原文件名:LCD1602.rar)
点击此处下载 ourdev_716554ZB6HAD.pdf(文件大小:793K) (原文件名:Spark51原理图.pdf)
点击此处下载 ourdev_716560C3448M.pdf(文件大小:324K) (原文件名:LCD1602液晶.pdf)
点击此处下载 ourdev_716561D39WEO.pdf(文件大小:759K) (原文件名:LCD1602接口封装.pdf)


以下是代码,只粘贴出来液晶驱动的C文件,大家喜欢的话,下载附件。
觉得作者考虑的挺严谨,比如检测忙状态,使用了次数统计,不会因为电路出现故障而死循环。

/*********************************************************************
文件名称:lcd1602.c
文件功能:lcd1602液晶驱动
创建时间:
作    者:Spark嵌入式工作室
*********************************************************************/
#include "lcd1602.h"

/*********************************************************************
功能:读取LCD状态
参数:无
返回:0 -- 空闲,可以进行读写操作
      1 -- 忙
*********************************************************************/
u8 lcd1602_read_state(void)
{
    u8 temp = 0;
   
    LCD1602_EN   = 0;
    LCD1602_RS   = 0;
    LCD1602_RW   = 1;
    LCD1602_EN   = 1;
    _nop_();
    _nop_();
   
    //读取状态
    LCD1602_DATA = 0xFF;
    temp = LCD1602_DATA;
   
    //只有最高位表示状态
    if(temp & 0x80)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/*********************************************************************
功能:向lcd写入指令
参数:command -- 指令
返回:无
*********************************************************************/
void lcd1602_write_command(u8 command)
{
    u8 i = 0;
   
    while(1)
    {
        //统计检测次数
        i++;
        
        //空闲退出循环
        if(lcd1602_read_state() == 0)
        {
            break;
        }
        //多次检测仍忙,放弃此次操作
        else if(i > 50)
        {
            return;
        }
    }
   
    //输出指令
    LCD1602_EN   = 0;
    LCD1602_RS   = 0;
    LCD1602_RW   = 0;
    LCD1602_DATA = command;
    _nop_();
    _nop_();
   
    //EN端产生上升沿,再恢复到低
    LCD1602_EN   = 1;
    _nop_();
    _nop_();
    LCD1602_EN   = 0;
}

/*********************************************************************
功能:向lcd写入数据
参数:dat -- 数据
返回:无
*********************************************************************/
void lcd1602_write_data(u8 dat)
{
    u8 i = 0;
   
    while(1)
    {
        //统计检测次数
        i++;
        
        //空闲退出循环
        if(lcd1602_read_state() == 0)
        {
            break;
        }
        //多次检测仍忙,放弃此次操作
        else if(i > 50)
        {
            return;
        }
    }
   
    //输出数据
    LCD1602_EN   = 0;
    LCD1602_RS   = 1;
    LCD1602_RW   = 0;
    LCD1602_DATA = dat;
    _nop_();
    _nop_();
   
    //EN端产生上升沿,再恢复到低
    LCD1602_EN   = 1;
    _nop_();
    _nop_();
    LCD1602_EN   = 0;
}

/*********************************************************************
功能:lcd初始化
参数:无
返回:无
*********************************************************************/
void lcd1602_init(void)
{
    lcd1602_write_command(0x38);   //设置16*2显示,5*7点阵,8位数据接口
    lcd1602_write_command(0x0C);   //开显示,没有光标
    lcd1602_write_command(0x06);   //写入数据,地址和光标增加,不移动
    lcd1602_write_command(0x01);   //显示清屏
}

/*********************************************************************
功能:设定lcd显示位置,即地址
参数:row -- 行坐标 1~2
      col -- 列坐标 1~16
返回:0  --  修改失败
      1  --  修改成功
*********************************************************************/
u8 lcd1602_set_cursor(u8 row, u8 col)
{
    u8 addr = 0;
   
    //判断参数合法性
    if((row < 1) || (row > 2) || (col < 1) || (col > 16))
    {
        //参数有误,直接返回
        return 0;
    }
   
    //把坐标转换成地址
    if(row == 1)
    {
        addr = col - 1;
    }
    else if(row == 2)
    {
        addr = 0x40 + (col - 1);
    }
   
    //写入地址
    lcd1602_write_command(0x80 | addr);
   
    return 1;
}

/*********************************************************************
功能:在特定的坐标写入字符
参数:row -- 行坐标 1~2, 如果是0则不进行坐标改动
      col -- 列坐标 1~16,如果是0则不进行坐标改动
      chr -- 字符ASCII码
返回:无
*********************************************************************/
void lcd1602_write_char(u8 row, u8 col, u8 chr)
{   
    //改动光标位置
    if((row != 0) && (col != 0))
    {
        if(!lcd1602_set_cursor(row, col))
        {
            //光标修改失败,直接返回
            return;
        }
    }
   
    //写入字符
    lcd1602_write_data(chr);

}

/*********************************************************************
功能:在特定的坐标写入字符串,没有换行功能
参数:row -- 行坐标 1~2, 如果是0则不进行坐标改动
      col -- 列坐标 1~16,如果是0则不进行坐标改动
      str -- 字符串地址
返回:无
*********************************************************************/
void lcd1602_write_str(u8 row, u8 col, u8 *str)
{
    u8 count = 0;
   
    //改动光标位置
    if((row != 0) && (col != 0))
    {
        if(!lcd1602_set_cursor(row, col))
        {
            //光标修改失败,直接返回
            return;
        }
    }
   
    //写入字符串
    while(*str != '\0')
    {
        lcd1602_write_data(*str);
        str++;
        
        //这里用count计数,防止死循环
        count++;
        if(count > 16)
        {
            break;
        }
    }
}

/*********************************************************************
功能:在特定的坐标写入数字,没有换行功能
参数:row  -- 行坐标 1~2, 如果是0则不进行坐标改动
      col  -- 列坐标 1~16,如果是0则不进行坐标改动
      num  -- 要显示的数字,范围0~9999
返回:无
*********************************************************************/
void lcd1602_write_num(u8 row, u8 col, u16 num)
{
    u8 str[5];        //保存num转换成的字符串
    u8 temp = 0, i = 0, count = 0;
    u8 *p;
   
   
    //取得千位,转换成ASCII码存入str
    temp   = num / 1000;
    str[0] = temp + '0';
   
    //取得百位
    num    = num - 1000 * temp;
    temp   = num / 100;
    str[1] = temp + '0';
   
    //取得十位
    num    = num - 100 * temp;
    temp   = num / 10;
    str[2] = temp + '0';
   
    //取得个位
    temp   = num % 10;
    str[3] = temp + '0';
   
    //添加字符串结束标志
    str[4] = '\0';
   
    //统计高位0的位数
    p = str;
    while(*p != '\0')
    {
        if(*p++ == '0')
        {
            count++;
        }
        else
        {
            break;
        }
    }
   
    if(count == 0)
    {
        //最高位不为0,不用处理
    }
    else if((count > 0) && (count < 4))
    {
        //根据0的个数,将字符串左移count个
        for(i = 0; i < (4 - count); i++)
        {
            str = str[i + count];
        }
        str = '\0';
    }
    else if(count == 4)
    {
        //num的值为0
        str[0] = '0';
        str[1] = '\0';
    }
   
    lcd1602_write_str(row, col, str);
}

/*********************************************************************
                         End  Of  File
*********************************************************************/

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2012-2-7 15:13:30 | 显示全部楼层
沙发

出0入0汤圆

发表于 2012-2-7 16:12:45 | 显示全部楼层
沙发

出0入0汤圆

发表于 2012-2-7 16:25:45 | 显示全部楼层
mark 多谢

出0入0汤圆

发表于 2012-2-7 18:48:53 | 显示全部楼层
使用这段代码要当心!
程序在读写1602指令和数据时,没有使用任何的延时。如果系统时钟速度快,或1602芯片的时序慢,甚至线路连接或PCB走线不好,都可能造成系统和1602时序不匹配,使指令、数据读写出错!
在读写子程序中,每当令1602使能后,最好根据自己的系统,插入适当数量的_nop_语句。偶试过,最少1条,多的时候需要6条,这还是51运行在12M时钟下的。时钟频率高的话,可能需要的更多

出0入0汤圆

 楼主| 发表于 2012-2-7 19:22:40 | 显示全部楼层
回复【4楼】dragonflag
-----------------------------------------------------------------------

有道理,我看着在板子上运行正常,就没加,为了保险起见,以后加上。
我得分析一下LCD1602的时序图,看看时间要求

出0入0汤圆

 楼主| 发表于 2012-2-7 19:53:30 | 显示全部楼层
回复【4楼】dragonflag
-----------------------------------------------------------------------

1、读操作,使能端发送上升沿后,需要100ns,数据才能稳定。
2、写操作,数据稳定40ns,才能使能端发送上升沿,切高电平至少保持150ns。
按这个时间要求,如果是12MHz,12T的单片机,倒不用太担心,如果用6T,或者1T的单片机就要考虑加延时了。

出0入0汤圆

发表于 2012-2-7 21:24:44 | 显示全部楼层
学习了……呵呵!

出0入0汤圆

发表于 2012-2-7 21:36:26 | 显示全部楼层
mark 多谢

出0入0汤圆

发表于 2012-2-8 09:12:35 | 显示全部楼层
回复【楼主位】chenxiaolang 小琅
-----------------------------------------------------------------------

楼主能否说说1602的电路,尤其是对比度那里,看了半天手册也不明白

出0入0汤圆

发表于 2012-2-8 12:01:03 | 显示全部楼层
提醒很有道理!!谢谢

出0入0汤圆

发表于 2012-2-9 09:57:39 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-2-11 10:44:46 | 显示全部楼层
不错哦...

出0入0汤圆

发表于 2012-2-11 22:17:35 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-3-31 21:47:59 | 显示全部楼层
确实很详细,也很保险,但想弱弱问一下,
if(count == 0)
    {
        //最高位不为0,不用处理
    }
在大括号中是否要加分号“;”  一时有点不太确定了。

出0入0汤圆

发表于 2012-4-1 10:57:59 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-4-1 13:56:12 | 显示全部楼层

谢谢分享!

出0入0汤圆

发表于 2012-5-6 22:18:25 | 显示全部楼层
顶一下~~~

出0入0汤圆

发表于 2012-6-3 22:23:23 | 显示全部楼层
谢谢分享!!!

出0入0汤圆

发表于 2012-6-7 20:20:38 | 显示全部楼层
好东西啊,谢谢分享
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 02:20

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表