51单片机 - lcd1602驱动

51单片机 - lcd1602驱动

本文用于阐述总结51单片机外接lcd1602的驱动程序,51单片机时钟电路采用12MHz晶振,P0端口外接上拉电阻与lcd1602数据口D0-D7相连,P2.7与lcd1602使能E端相连,P2.6与lcd1602读/写选择R/W端相连,P2.5与lcd1602数据/命令选择RS端相连。下面开始上代码:

#define LCD1602_DATA P0  // D0~D7为8位双向数据线
sbit LCD1602_E = P2^7;     // E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令
sbit LCD1602_RW = P2^6;    // R/W为读写信号线,高电平时进行读操作,低电平时进行写操作
sbit LCD1602_RS = P2^5;    // RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器

lcd1602内建命令

对lcd1602内建命令进行宏定义,使其在编写驱动能更有效控制lcd1602,另外也避免每次配置lcd1602都翻看用户手册。lcd1602控制命令只有数条较为简单,下列代码通过换行符划分各条指令可配置选项,各宏定义功能详见代码注释。

#define LCD1602_CLEAN_DISPLAY 0x01  // 清显示,指令码01H,光标复位到地址00H位置

#define LCD1602_CURSOR_RETURN 0x02  // 光标复位,光标返回到地址00H

#define LCD1602_CURSOR_RIGHT_MOVE 0x02  // 光标移动方向,高电平右移,低电平左移
#define LCD1602_CURSOR_LEFT_MOVE  0x00
#define LCD1602_WORD_MOVE_ENABLE  0x01  // 屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效
#define LCD1602_WORD_MOVE_DISABLE 0x00
#define LCD1602_INPUT_MODE (0x04 | LCD1602_CURSOR_RIGHT_MOVE | LCD1602_WORD_MOVE_DISABLE)

#define LCD1602_DISPLAY_ENABLE     0x04  // 控制整体显示的开与关,高电平表示开显示,低电平表示关显示
#define LCD1602_DISPLAY_DISABLE    0x00
#define LCD1602_CURSOR_ENABLE      0x02  // 控制光标的开与关,高电平表示有光标,低电平表示无光标
#define LCD1602_CURSOR_DISABLE     0x00
#define LCD1602_CURSOR_TWINKLE     0x01  // 控制光标是否闪烁,高电平闪烁,低电平不闪烁
#define LCD1602_CURSOR_NOT_TWINKLE 0x00
#define LCD1602_DISPLAY_CONTROL (0x08 | LCD1602_DISPLAY_ENABLE | LCD1602_CURSOR_DISABLE | LCD1602_CURSOR_NOT_TWINKLE)

#define LCD1602_WORDS_MOVE  0x08  // 高电平时移动文字,低电平时移动光标
#define LCD1602_CURSOR_MOVE 0x00
#define LCD1602_RIGHT_MOVE  0x04  // 高电平时右移,低电平时左移
#define LCD1602_LEFT_MOVE   0x00
#define LCD1602_MOVE_CONTROL (0x10 | LCD1602_CURSOR_MOVE | LCD1602_RIGHT_MOVE)

#define LCD1602_DATA_BUS_8BIT 0x10  // 高电平时为8位总线,低电平时为4位总线
#define LCD1602_DATA_BUS_4BIT 0x00
#define LCD1602_DISPLAY_2LINE 0x08  // 低电平时为单行显示,高电平时双行显示
#define LCD1602_DISPLAY_1LINE 0x00
#define LCD1602_DISPLAY_5x10  0x04  // 低电平时显示5x7的点阵字符,高电平时显示5x10的点阵字符
#define LCD1602_DISPLAY_5x7   0x00
#define LCD1602_FUNCTION_SET (0x20 | LCD1602_DATA_BUS_8BIT | LCD1602_DISPLAY_2LINE | LCD1602_DISPLAY_5x7)

lcd1602驱动对外接口呈现

lcd1602对外提供的函数接口如下,各个函数功能详见代码注释:

void lcd1602_check_busy(void);  // LCD1602忙检测
void lcd1602_write_cmd(u8 cmd);   // LCD1602写入8位命令
void lcd1602_write_data(u8 dat);  // LCD1602写入8位数据
void lcd1602_init(void);          // LCD1602初始化
void lcd1602_deinit(void);        // LCD1602去初始化
void lcd1602_delay_ms(u16 ms);    // 52单片机12MHZ时钟,延时1ms函数
void lcd1602_delay_10us(u16 n);  // 52单片机12MHZ时钟,延时10us函数
s8 lcd1602_set_pos(u8 x, u8 y);   // LCD1602光标定位
void lcd1602_write_str(string str);      // LCD1602写字符串
s8 lcd1602_write_num(u8 width, s32 num);  // LCD1602写数字
void lcd1602_write_space(u8 cnt);        // LCD1602写空格
void lcd1602_clean(void);  // LCD1602清屏
void test_lcd1602(void);   // LCD1602测试代码

lcd1602驱动 - 忙信号检测

lcd1602在收到51单片机送出的命令后,在处理命令过程中处理时间有长有短,如:显示某字符就比清屏所消耗的时间少得多,在处理命令期间不能对lcd1602进行写操作,通过设置读命令状态,进行忙检测,获取lcd1602数据引脚D7状态判断是否处于忙状态。

/*******************************************************************************
* 函 数 名         : lcd1602_check_busy
* 函数功能         : LCD1602忙检测
* 输    入         : void
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_check_busy(void)  // LCD1602忙检测
{
    u8 state = 0xff;

    LCD1602_E = 0;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do {
        LCD1602_E = 1;
        state = LCD1602_DATA;
        LCD1602_E = 0;     // 读完状态后撤销使能,防止LCD输出干扰总线
    } while (state & 0x80);      // 若LCD为忙碌状态就持续检测,一直到不忙为止
}

lcd1602驱动 - 写命令

通过lcd1602内建指令控制显示器,设置为写命令状态。

/*******************************************************************************
* 函 数 名         : lcd1602_write_cmd
* 函数功能         : LCD1602写入8位命令
* 输    入         : cmd:16进制命令值
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_write_cmd(u8 cmd)  // LCD1602写入8位命令
{
    lcd1602_check_busy();
    LCD1602_E = 0;         // 使能
    LCD1602_RS = 0;        // 选择发送命令
    LCD1602_RW = 0;        // 选择写入
    LCD1602_DATA = cmd;    // 放入命令
    lcd1602_delay_10us(1);  // 等待数据稳定
    LCD1602_E = 1;         // 写入时序
    lcd1602_delay_10us(1);  // 保持时间
    LCD1602_E = 0;
}

lcd1602驱动 - 写数据

控制lcd1602显示内容,默认lcd1602支持ascii码,不涉及自定义lcd1602字符相关内容。

/*******************************************************************************
* 函 数 名         : lcd1602_write_data
* 函数功能         : LCD1602写入8位数据
* 输    入         : dat:ascii码
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_write_data(u8 dat)  // LCD1602写入8位数据
{
    lcd1602_check_busy();
    LCD1602_E = 0;         // 使能清零
    LCD1602_RS = 1;        // 选择输入数据
    LCD1602_RW = 0;        // 选择写入
    LCD1602_DATA = dat;    // 写入数据
    lcd1602_delay_10us(1);
    LCD1602_E = 1;         // 写入时序
    lcd1602_delay_10us(1);  // 保持时间
    LCD1602_E = 0;
}

lcd1602驱动 - 设置光标位置

控制lcd1602显示某字符的具体位置。

/*******************************************************************************
* 函 数 名         : lcd1602_set_pos
* 函数功能         : LCD1602光标定位
* 输    入         : x/y:横/纵坐标
* 输    出         : s8:返回-1设置坐标值异常
* 说    名         : none
*******************************************************************************/
s8 lcd1602_set_pos(u8 x, u8 y)  // LCD1602光标定位
{
    if ((x >= LCD1602_WIDTH) || (y >= LCD1602_HIGH)) {
        return -1;
    }

    if (y == 0) {
        lcd1602_write_cmd(LCD1602_1LINE_ADDRESS + x);
    } else if (y == 1) {
        lcd1602_write_cmd(LCD1602_2LINE_ADDRESS + x);
    }

    return 0;
}

lcd1602驱动 - 显示字符串

根据字符串长度连续调用lcd1602_write_data()即可。

/*******************************************************************************
* 函 数 名         : lcd1602_write_str
* 函数功能         : LCD1602写字符串
* 输    入         : str:字符串
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_write_str(string str)  // LCD1602写字符串
{
    for (; (*str) != '\0'; str++) {
        lcd1602_write_data(*str);
    }
}

lcd1602驱动 - 数字显示

在lcd1602上输出数字,支持正/负整数显示,可控制显示位宽,当待显示数字位数不足位宽时会在前端补0,待显示数字位数超过位宽时只显示部分低位。注:C51编程,keil编译器疑似不支持malloc()。

/*******************************************************************************
* 函 数 名         : lcd1602_write_num
* 函数功能         : LCD1602写数字
* 输    入         : width:显示位数,num:数字
* 输    出         : s8:返回-1输入显示位数异常
* 说    名         : none
*******************************************************************************/
s8 lcd1602_write_num(u8 width, s32 num)  // LCD1602写数字
{
    u8 i;
    u8 dat[10] = { 0 };  // 不支持malloc

    if (width > sizeof(dat)) {
        return -1;
    }

    if (num < 0) {
        lcd1602_write_data('-');
        num = -num;
    }
    for (i = 0; num; i++) {
        if (i >= width) {
            break;
        }
        dat[i] = (num % 10 + '0');
        num /= 10;
    }

    for (; i < width; i++) {
        dat[i] = '0';
    }

    for (; i; i--) {
        lcd1602_write_data(dat[i - 1]);  // 写入数据
    }

    return 0;
}

附录 - lcd1602驱动(lcd1602.h)

#ifndef __LCD1602_H__
#define __LCD1602_H__

#include <reg52.h>
#include "include.h"

#define LCD1602_DATA P0  // D0~D7为8位双向数据线
sbit LCD1602_E = P2^7;     // E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令
#ifdef ELECTRONIC_MARS
sbit LCD1602_RW = P2^6;    // R/W为读写信号线,高电平时进行读操作,低电平时进行写操作
sbit LCD1602_RS = P2^5;    // RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器
#endif
#ifdef ELECTRONIC_MOON
sbit LCD1602_RW = P2^5;
sbit LCD1602_RS = P2^6;
#endif

#define LCD1602_WIDTH 16  // 显示器宽度
#define LCD1602_HIGH   2  // 显示器高度

#define LCD1602_1LINE_ADDRESS 0xc0  // 显示行首地址
#define LCD1602_2LINE_ADDRESS 0x80

#define LCD1602_CLEAN_DISPLAY 0x01  // 清显示,指令码01H,光标复位到地址00H位置

#define LCD1602_CURSOR_RETURN 0x02  // 光标复位,光标返回到地址00H

#define LCD1602_CURSOR_RIGHT_MOVE 0x02  // 光标移动方向,高电平右移,低电平左移
#define LCD1602_CURSOR_LEFT_MOVE  0x00
#define LCD1602_WORD_MOVE_ENABLE  0x01  // 屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效
#define LCD1602_WORD_MOVE_DISABLE 0x00
#define LCD1602_INPUT_MODE (0x04 | LCD1602_CURSOR_RIGHT_MOVE | LCD1602_WORD_MOVE_DISABLE)

#define LCD1602_DISPLAY_ENABLE     0x04  // 控制整体显示的开与关,高电平表示开显示,低电平表示关显示
#define LCD1602_DISPLAY_DISABLE    0x00
#define LCD1602_CURSOR_ENABLE      0x02  // 控制光标的开与关,高电平表示有光标,低电平表示无光标
#define LCD1602_CURSOR_DISABLE     0x00
#define LCD1602_CURSOR_TWINKLE     0x01  // 控制光标是否闪烁,高电平闪烁,低电平不闪烁
#define LCD1602_CURSOR_NOT_TWINKLE 0x00
#define LCD1602_DISPLAY_CONTROL (0x08 | LCD1602_DISPLAY_ENABLE | LCD1602_CURSOR_DISABLE | LCD1602_CURSOR_NOT_TWINKLE)

#define LCD1602_WORDS_MOVE  0x08  // 高电平时移动文字,低电平时移动光标
#define LCD1602_CURSOR_MOVE 0x00
#define LCD1602_RIGHT_MOVE  0x04  // 高电平时右移,低电平时左移
#define LCD1602_LEFT_MOVE   0x00
#define LCD1602_MOVE_CONTROL (0x10 | LCD1602_CURSOR_MOVE | LCD1602_RIGHT_MOVE)

#define LCD1602_DATA_BUS_8BIT 0x10  // 高电平时为8位总线,低电平时为4位总线
#define LCD1602_DATA_BUS_4BIT 0x00
#define LCD1602_DISPLAY_2LINE 0x08  // 低电平时为单行显示,高电平时双行显示
#define LCD1602_DISPLAY_1LINE 0x00
#define LCD1602_DISPLAY_5x10  0x04  // 低电平时显示5x7的点阵字符,高电平时显示5x10的点阵字符
#define LCD1602_DISPLAY_5x7   0x00
#define LCD1602_FUNCTION_SET (0x20 | LCD1602_DATA_BUS_8BIT | LCD1602_DISPLAY_2LINE | LCD1602_DISPLAY_5x7)

void lcd1602_check_busy(void);  // LCD1602忙检测
void lcd1602_write_cmd(u8 cmd);   // LCD1602写入8位命令
void lcd1602_write_data(u8 dat);  // LCD1602写入8位数据
void lcd1602_init(void);          // LCD1602初始化
void lcd1602_deinit(void);        // LCD1602去初始化
void lcd1602_delay_ms(u16 ms);    // 52单片机12MHZ时钟,延时1ms函数
void lcd1602_delay_10us(u16 n);  // 52单片机12MHZ时钟,延时10us函数
s8 lcd1602_set_pos(u8 x, u8 y);   // LCD1602光标定位
void lcd1602_write_str(string str);      // LCD1602写字符串
s8 lcd1602_write_num(u8 width, s32 num);  // LCD1602写数字
void lcd1602_write_space(u8 cnt);        // LCD1602写空格
void lcd1602_clean(void);  // LCD1602清屏
void test_lcd1602(void);   // LCD1602测试代码

#endif

附录 - lcd1602驱动(lcd1602.c)

#include "lcd1602.h"

/*******************************************************************************
* 函 数 名         : lcd1602_check_busy
* 函数功能         : LCD1602忙检测
* 输    入         : void
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_check_busy(void)  // LCD1602忙检测
{
    u8 state = 0xff;

    LCD1602_E = 0;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do {
        LCD1602_E = 1;
        state = LCD1602_DATA;
        LCD1602_E = 0;     // 读完状态后撤销使能,防止LCD输出干扰总线
    } while (state & 0x80);      // 若LCD为忙碌状态就持续检测,一直到不忙为止
}

/*******************************************************************************
* 函 数 名         : lcd1602_write_cmd
* 函数功能         : LCD1602写入8位命令
* 输    入         : cmd:16进制命令值
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_write_cmd(u8 cmd)  // LCD1602写入8位命令
{
    lcd1602_check_busy();
    LCD1602_E = 0;         // 使能
    LCD1602_RS = 0;        // 选择发送命令
    LCD1602_RW = 0;        // 选择写入
    LCD1602_DATA = cmd;    // 放入命令
    lcd1602_delay_10us(1);  // 等待数据稳定
    LCD1602_E = 1;         // 写入时序
    lcd1602_delay_10us(1);  // 保持时间
    LCD1602_E = 0;
}

/*******************************************************************************
* 函 数 名         : lcd1602_write_data
* 函数功能         : LCD1602写入8位数据
* 输    入         : dat:ascii码
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_write_data(u8 dat)  // LCD1602写入8位数据
{
    lcd1602_check_busy();
    LCD1602_E = 0;         // 使能清零
    LCD1602_RS = 1;        // 选择输入数据
    LCD1602_RW = 0;        // 选择写入
    LCD1602_DATA = dat;    // 写入数据
    lcd1602_delay_10us(1);
    LCD1602_E = 1;         // 写入时序
    lcd1602_delay_10us(1);  // 保持时间
    LCD1602_E = 0;
}

/*******************************************************************************
* 函 数 名         : lcd1602_init
* 函数功能         : LCD1602初始化
* 输    入         : void
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_init(void)  // LCD1602初始化
{
    lcd1602_write_cmd(LCD1602_FUNCTION_SET);  // 开显示
    lcd1602_write_cmd(LCD1602_DISPLAY_CONTROL);  // 开显示不显示光标
    lcd1602_write_cmd(LCD1602_INPUT_MODE);  // 写一个指针加1
    lcd1602_clean();  // 清屏
    lcd1602_set_pos(1, 0);  // 设置数据指针起点
}

/*******************************************************************************
* 函 数 名         : lcd1602_deinit
* 函数功能         : LCD1602去初始化
* 输    入         : void
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_deinit(void)        // LCD1602去初始化
{
    lcd1602_clean();  // 清屏
    lcd1602_write_cmd(0x08 |
                      LCD1602_DISPLAY_DISABLE |
                      LCD1602_CURSOR_DISABLE |
                      LCD1602_CURSOR_NOT_TWINKLE);
}

/*******************************************************************************
* 函 数 名         : lcd1602_delay_ms
* 函数功能         : 52单片机12MHZ时钟,延时1ms函数
* 输    入         : ms:延时ms数
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_delay_ms(u16 ms)  // 52单片机12MHZ时钟,延时1ms函数
{
    u16 i;

    for (; ms; ms--) {
        for (i = 0; i < 123; i++);
    }
}

/*******************************************************************************
* 函 数 名         : lcd1602_delay_10us
* 函数功能         : 52单片机12MHZ时钟,延时10us函数
* 输    入         : void
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_delay_10us(u16 n)  // 52单片机12MHZ时钟,延时10us函数
{
    while (n--);
}

/*******************************************************************************
* 函 数 名         : lcd1602_set_pos
* 函数功能         : LCD1602光标定位
* 输    入         : x/y:横/纵坐标
* 输    出         : s8:返回-1设置坐标值异常
* 说    名         : none
*******************************************************************************/
s8 lcd1602_set_pos(u8 x, u8 y)  // LCD1602光标定位
{
    if ((x >= LCD1602_WIDTH) || (y >= LCD1602_HIGH)) {
        return -1;
    }

    if (y == 0) {
        lcd1602_write_cmd(LCD1602_1LINE_ADDRESS + x);
    } else if (y == 1) {
        lcd1602_write_cmd(LCD1602_2LINE_ADDRESS + x);
    }

    return 0;
}

/*******************************************************************************
* 函 数 名         : lcd1602_write_str
* 函数功能         : LCD1602写字符串
* 输    入         : str:字符串
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_write_str(string str)  // LCD1602写字符串
{
    for (; (*str) != '\0'; str++) {
        lcd1602_write_data(*str);
    }
}

/*******************************************************************************
* 函 数 名         : lcd1602_write_num
* 函数功能         : LCD1602写数字
* 输    入         : width:显示位数,num:数字
* 输    出         : s8:返回-1输入显示位数异常
* 说    名         : none
*******************************************************************************/
s8 lcd1602_write_num(u8 width, s32 num)  // LCD1602写数字
{
    u8 i;
    u8 dat[10] = { 0 };  // 不支持malloc

    if (width > sizeof(dat)) {
        return -1;
    }

    if (num < 0) {
        lcd1602_write_data('-');
        num = -num;
    }
    for (i = 0; num; i++) {
        if (i >= width) {
            break;
        }
        dat[i] = (num % 10 + '0');
        num /= 10;
    }

    for (; i < width; i++) {
        dat[i] = '0';
    }

    for (; i; i--) {
        lcd1602_write_data(dat[i - 1]);  // 写入数据
    }

    return 0;
}

/*******************************************************************************
* 函 数 名         : lcd1602_write_space
* 函数功能         : LCD1602写空格
* 输    入         : cnt:空格数量
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_write_space(u8 cnt)  // LCD1602写空格
{
    for(; cnt; cnt--) {
        lcd1602_write_data(' ');
    }
}

/*******************************************************************************
* 函 数 名         : lcd1602_clean
* 函数功能         : LCD1602清屏
* 输    入         : void
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void lcd1602_clean(void)  // LCD1602清屏
{
    lcd1602_write_cmd(LCD1602_CLEAN_DISPLAY);
    lcd1602_delay_ms(5);  // 清屏后即刻写指令失败,延时5ms
}

/*******************************************************************************
* 函 数 名         : test_lcd1602
* 函数功能         : LCD1602测试代码
* 输    入         : void
* 输    出         : void
* 说    名         : none
*******************************************************************************/
void test_lcd1602(void)  // LCD1602测试代码
{
    u8 i;

    lcd1602_init();

    for (i = 1; i < 200; i++) {
        lcd1602_set_pos(0, 1);
        lcd1602_write_str("test_lcd1602 ###");
        lcd1602_set_pos(14, 1);
        lcd1602_write_space(2);

        lcd1602_set_pos(0, 0);
        lcd1602_write_num(2, i);
        lcd1602_set_pos(8, 0);
        lcd1602_write_num(5, -i);

        lcd1602_delay_ms(500);
        lcd1602_clean();
    }
}

附录 - 通用(include.h)

#ifndef __INCLUDE_H__
#define __INCLUDE_H__

/*
################################################################

                                运行平台选择

################################################################
*/
#define ELECTRONIC_MARS    // 自制万年历
//#define ELECTRONIC_MOON    // 51开发板

typedef char s8;
typedef unsigned char u8;
typedef short s16;
typedef unsigned short u16;
typedef long s32;
typedef unsigned long u32;
typedef char* string;



#endif
发布了15 篇原创文章 · 获赞 17 · 访问量 1659

猜你喜欢

转载自blog.csdn.net/weixin_44413515/article/details/104439315
今日推荐