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驱动()
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include <>
#include ""
#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驱动()
#include ""
/*******************************************************************************
* 函 数 名 : 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();
}
}
附录 - 通用()
#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