代码拉取完成,页面将自动刷新
/**
* lcd1602.h - LCD1602 驱动库 - 头文件
* by Nixsawe <ziming_cool@126.com>
* helped by @slipperstree
*
* 为了简化操作逻辑,特封装此库
*/
#ifndef __LCD1602__
#define __LCD1602__
#include<atmel/at89x52.h>
#include<intrins.h>
/*---------- 全局配置 ----------*/
/*---------- 数据总线长度 ----------*/
// 数据总线长度只能为 4 或 8,且在编译时确定,运行时无法修改
// 默认的数据总线长度为 8
#ifdef LCD1602_DATA_LENGTH
# if (LCD1602_DATA_LENGTH==8)
# elif (LCD1602_DATA_LENGTH==4)
# else
// 无效的总线长度,总线长度只能为 4 或 8
# undef LCD1602_DATA_LENGTH
# pragma warning("Invalid macro LCD1602_DATA_LENGTH. Only 4 or 8 is acceptable.")
# endif
#endif
#ifndef LCD1602_DATA_LENGTH
# define LCD1602_DATA_LENGTH 8
#endif
#undef LCD1602_DATALINE_4_BITS
#undef LCD1602_DATALINE_8_BITS
#if (LCD1602_DATA_LENGTH==8)
// 8 位数据总线
# define LCD1602_DATALINE_8_BITS
# undef LCD1602_DATALINE_USE_PORT
#else
// 4 位数据总线,写 1 个字节要在 D7~D4 上输出两次 4bit 数据
# define LCD1602_DATALINE_4_BITS
#endif
/*---------- 输入输出引脚 ----------*/
// LCD 使能位,这条线上产生上升沿将导致命令被执行
// 也称为时钟线 (响应时间 <=25ns,脉冲宽度 >=150ns,信号周期 >=400ns)
// 几乎不需要在时序上考虑等待的问题
#ifndef LCD1602_EN
# define LCD1602_EN P2_7
#endif
// LCD 数据/命令选择位 (H/L)
// 高电平时读写数据,低电平时读状态或写命令
#ifndef LCD1602_RS
# define LCD1602_RS P2_6
#endif
// LCD 读/写选择位 (H/L)
// 高电平读数据,低电平写数据
#ifndef LCD1602_RW
# define LCD1602_RW P2_5
#endif
// LCD Data IO 端口
// 数据输入输出
#ifndef LCD1602_IO_PORT
# define LCD1602_IO_PORT P0
#endif
// LCD Data IO 端口 (高 4 位)
#ifndef LCD1602_IO_D7
# define LCD1602_IO_D7 P0_7
#endif
#ifndef LCD1602_IO_D6
# define LCD1602_IO_D6 P0_6
#endif
#ifndef LCD1602_IO_D5
# define LCD1602_IO_D5 P0_5
#endif
#ifndef LCD1602_IO_D4
# define LCD1602_IO_D4 P0_4
#endif
/*---------- 基本类型定义 ----------*/
typedef unsigned char LCD1602_CMD;
typedef unsigned char LCD1602_DATA;
typedef unsigned char LCD1602_STATE;
// LCD1602 初始化结构体类型
typedef struct {
// 显示行数、字体大小
LCD1602_CMD FNS;
// 屏幕显示与光标
LCD1602_CMD DSP;
// 输入模式
LCD1602_CMD ENT;
} LCD1602_INIT_DATA;
/*---------- 基础功能封装 ----------*/
// 等待一个机器周期 (约 1μs)
#define lcd1602_wait_nop() \
do \
{ \
_nop_(); \
} while(0)
// 使能线上升沿
#define lcd1602_rising_edge() \
do \
{ \
LCD1602_EN = 1; \
lcd1602_wait_nop(); \
} while(0)
// 使能线下降沿
#define lcd1602_falling_edge() \
do \
{ \
LCD1602_EN = 0; \
lcd1602_wait_nop(); \
} while(0)
#if defined(LCD1602_DATALINE_8_BITS)
// 8 位总线写
#define lcd1602_write(dat) \
do \
{ \
LCD1602_IO_PORT = (dat); \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
lcd1602_falling_edge(); \
} while(0)
// 8 位总线读
#define lcd1602_read(dat) \
do \
{ \
LCD1602_IO_PORT = 0xff; \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
(dat) = LCD1602_IO_PORT; \
lcd1602_falling_edge(); \
} while(0)
#elif defined(LCD1602_DATALINE_USE_PORT)
// 4 位总线,使用整个输出端口的高 4 位进行输出
// 如果低 4 位作为其他功能使用,可能造成干扰
// 4 位总线写
#define lcd1602_write(dat) \
do \
{ \
/* 写高 4 位 */ \
LCD1602_IO_PORT = ((dat) & 0xf0); \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
lcd1602_falling_edge(); \
/* 写低 4 位 */ \
LCD1602_IO_PORT = ((dat) << 4); \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
lcd1602_falling_edge(); \
} while(0)
// 4 位总线读
#define lcd1602_read(dat) \
do \
{ \
/* 读高 4 位 */ \
LCD1602_IO_PORT = 0xf0; \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
(dat) = (LCD1602_IO_PORT & 0xf0); \
lcd1602_falling_edge(); \
/* 读低 4 位 */ \
LCD1602_IO_PORT = 0xf0; \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
(dat) |= (LCD1602_IO_PORT >> 4); \
lcd1602_falling_edge(); \
} while(0)
#else
// 4 位总线,输出时按位寻址独立输出 4 位数据
// 不会造成干扰
// 4 位总线写
#define lcd1602_write(dat) \
do \
{ \
/* 写高 4 位 */ \
LCD1602_IO_D7 = ((dat) >> 7) & 1; \
LCD1602_IO_D6 = ((dat) >> 6) & 1; \
LCD1602_IO_D5 = ((dat) >> 5) & 1; \
LCD1602_IO_D4 = ((dat) >> 4) & 1; \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
lcd1602_wait_nop(); \
lcd1602_falling_edge(); \
/* 写低 4 位 */ \
LCD1602_IO_D7 = ((dat) >> 3) & 1; \
LCD1602_IO_D6 = ((dat) >> 2) & 1; \
LCD1602_IO_D5 = ((dat) >> 1) & 1; \
LCD1602_IO_D4 = (dat) & 1; \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
lcd1602_wait_nop(); \
lcd1602_falling_edge(); \
} while(0)
// 4 位总线读
#define lcd1602_read(dat) \
do \
{ \
/* 读高 4 位 */ \
LCD1602_IO_D7 = 1; \
LCD1602_IO_D6 = 1; \
LCD1602_IO_D5 = 1; \
LCD1602_IO_D4 = 1; \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
lcd1602_wait_nop(); \
(dat) = (((unsigned char)(LCD1602_IO_D7) << 7) | ((unsigned char)(LCD1602_IO_D6) << 6) | ((unsigned char)(LCD1602_IO_D5) << 5) | ((unsigned char)(LCD1602_IO_D4) << 4)); \
lcd1602_falling_edge(); \
/* 读低 4 位 */ \
LCD1602_IO_D7 = 1; \
LCD1602_IO_D6 = 1; \
LCD1602_IO_D5 = 1; \
LCD1602_IO_D4 = 1; \
lcd1602_wait_nop(); \
lcd1602_rising_edge(); \
lcd1602_wait_nop(); \
(dat) |= (((unsigned char)(LCD1602_IO_D7) << 3) | ((unsigned char)(LCD1602_IO_D6) << 2) | ((unsigned char)(LCD1602_IO_D5) << 1) | (unsigned char)(LCD1602_IO_D4)); \
lcd1602_falling_edge(); \
} while(0)
#endif
#if (0==1)
// 什么都不做,仅是为了让我的编辑器能够正常处理之后的缩进
void ();
#endif
/*---------- LCD1602 指令集 ----------*/
// 1. 清屏 (Clear Display)
// 清空液晶显示屏的显示内容,即置 DDRAM 所有字节为 20H (空格字符 ' ')
// 光标将撤回显示屏左上方,地址计数器 (AC) 将设为 0,重置 I/D = 0
#define LCD1602_CMD_CLD() 0x01
#define LCD1602_XCMD_CLD LCD1602_CMD_CLD
// 2. 光标归位 (Return Home)
// 光标将撤回显示屏左上方,地址计数器 (AC) 将设为 0,DDRAM 内容保持不变
#define LCD1602_CMD_RET() 0x02
#define LCD1602_XCMD_RET LCD1602_CMD_RET
// 3. 输入模式设置 (Entry Mode Set)
// 设定每次处理 1 个字节后 (DDRAM 或是 CGRAM) 光标、屏幕以及指针的移动方向
// I/D: 光标移动方向 向右 / 向左 (H/L)
// DDRAM 地址 自增 / 自减 (H/L)
// I/D 在读写操作中均生效
// SH : 屏幕是否移动 移动 / 固定 (H/L)
// SH 仅当写 DDRAM 时生效,读 DDRAM 或是对 CGRAM 操作时屏幕均不会移动
#define LCD1602_CMD_ENT(ID,SH) (0x04 | ((ID) ? 2 : 0) | !!(SH))
#define LCD1602_XCMD_ENT(mask) (0x04 | (mask))
#define LCD1602_ENTRY_ADDR_INCREASE 2
#define LCD1602_ENTRY_ADDR_DECREASE 0
#define LCD1602_ENTRY_SCREEN_MOVE 1
#define LCD1602_ENTRY_SCREEN_HOLD 0
// LCD1602_CMD_ENT(1, 0)
// 等价于 LCD1602_XCMD_ENT(LCD1602_ENTRY_ADDR_INCREASE)
// 或 LCD1602_XCMD_ENT(LCD1602_ENTRY_ADDR_INCREASE | LCD1602_ENTRY_SCREEN_HOLD)
// 4. 显示开关控制 (Display ON/OFF Control)
// 控制整个屏幕的显示是否打开,是否显示光标,光标是否闪烁
// D: 显示 开 / 关 (H/L)
// C: 光标 开 / 关 (H/L)
// B: 光标闪烁 开 / 关 (H/L)
// NOTE: C 控制是否在光标位置显示下划线,B 独立控制光标位置 (整个字符) 是否闪烁,二者互不干扰
#define LCD1602_CMD_DSP(D,C,B) (0x08 | ((D) ? 4 : 0) | ((C) ? 2 : 0) | !!(B))
#define LCD1602_XCMD_DSP(mask) (0x08 | (mask))
#define LCD1602_DISPLAY_ON 4
#define LCD1602_CURSOR_ON 2
#define LCD1602_CURSOR_SHOW 2
#define LCD1602_CURSOR_BLINK 1
// LCD1602_CMD_DSP(1, 0, 1)
// 等价于 LCD1602_XCMD_DSP(LCD1602_DISPLAY_ON | LCD1602_CURSOR_BLINK)
// 5. 移动光标或屏幕 (Cursor or Display Shift)
// 移动整个屏幕,或是移动光标,而不对 DDRAM 内的数据进行修改
// S/C: 移动屏幕 / 移动光标 (H/L)
// R/L: 右移 / 左移 (H/L)
#define LCD1602_CMD_SFT(SC,RL) (0x10 | ((SC) ? 8 : 0) | ((RL) ? 4 : 0))
#define LCD1602_XCMD_SFT(mask) (0x10 | (mask))
#define LCD1602_SHIFT_SCREEN 8
#define LCD1602_SHIFT_CURSOR 0
#define LCD1602_SHIFT_RIGHT 4
#define LCD1602_SHIFT_LEFT 0
// LCD1602_CMD_SFT(1, 0)
// 等价于 LCD1602_XCMD_SFT(LCD1602_SHIFT_SCREEN | LCD1602_SHIFT_LEFT)
// 6. 功能设定 (Function Set)
// 设置芯片的数据总线,屏幕上显示的行数,以及点阵字体高度
// DL: 数据总线 8 位 / 4 位 (H/L)
// N : 显示行数 2 行 / 1 行 (H/L)
// F : 点阵字体 5*11 / 5*8 (H/L)
// #define LCD1602_CMD_FNS(DL,N,F) (0x10 | ((DL) << 4) | ((N) << 3) | ((F) << 2))
#ifdef LCD1602_DATALINE_8_BITS
#define LCD1602_CMD_FNS(N,F) (0x30 | ((N) ? 8 : 0) | ((F) ? 4 : 0))
#define LCD1602_XCMD_FNS(mask) (0x30 | (mask))
#else
#define LCD1602_CMD_FNS(N,F) (0x20 | ((N) ? 8 : 0) | ((F) ? 4 : 0))
#define LCD1602_XCMD_FNS(mask) (0x20 | (mask))
#endif
#define LCD1602_TWO_LINES 8
#define LCD1602_SINGLE_LINE 0
#define LCD1602_LARGE_FONT 4
#define LCD1602_SMALL_FONT 0
#define LCD1602_NORMAL_FONT 0
// LCD1602_CMD_FNS(1, 1)
// 等价于 LCD1602_XCMD_FNS(LCD1602_TWO_LINES | LCD1602_LARGE_FONT)
// 7. 设置 CGRAM 地址 (Set CGRAM Address)
// 设置目标 CGRAM 地址
// AC: 目标 CGRAM 地址
#define LCD1602_CMD_CGR(AC) (0x40 | ((AC) & 0x3f))
#define LCD1602_XCMD_CGR LCD1602_CMD_CGR
// 8. 设置 DDRAM 地址 (Set DDRAM Address)
// 设置地址计数器 AC 的值
// AC: 目标 DDRAM 地址
// 当显示行数为 1 行 (N=0) 时 DDRAM 的地址范围是 00H~4FH
// 当显示行数为 2 行 (N=1) 时 DDRAM 的地址范围是 00H~27H, 40H~67H
#define LCD1602_CMD_DDR(AC) (0x80 | (AC))
#define LCD1602_XCMD_DDR LCD1602_CMD_DDR
// 9. 是否繁忙 / AC 地址值
// #define LCD1602_BUSY_FLAG 0x80
#define LCD1602_AC_ADDRESS_MASK 0x7f
#define lcd1602_ac_address() (lcd1602_read_state() & LCD1602_AC_ADDRESS_MASK)
// 10. 向 DDRAM/CGRAM 写数据 (通过函数实现)
// 11. 从 DDRAM/CGRAM 读数据 (通过函数实现)
/*---------- 内部变量导出 ----------*/
// 初始化 LCD 的默认配置信息
// 当调用 lcd1602_init 方法时,用户自定义的配置内容会覆盖对应的成员
// 外部初始化 LCD 时,可自己定义对应的结构体,也可通过修改该结构体对应成员来设置初始参数
// XXX: 在未来,这个可用于全局配置,实现在现有配置上累加新配置
// XXX: 如切换光标下划线的显示状态,但不对光标的闪烁状态进行更改
extern LCD1602_INIT_DATA lcd1602_config;
/*---------- 函数原型 ----------*/
// 写命令
void lcd1602_write_cmd(LCD1602_CMD cmd);
// 写数据 (到 DDRAM 或 CGRAM)
void lcd1602_write_data(LCD1602_DATA dat);
// 读状态 (BF 以及 AC 地址值)
LCD1602_STATE lcd1602_read_state();
// 读数据 (从 DDRAM 或 CGRAM)
LCD1602_DATA lcd1602_read_data();
// 判断 LCD1602 繁忙状态
LCD1602_STATE lcd1602_busy();
// 同步等待直到 LCD1602 不再繁忙
void lcd1602_waitBusy();
// 执行初始化
void lcd1602_init(LCD1602_INIT_DATA * p_init_data);
// 清屏
void lcd1602_cld();
// 光标回到初始位置
void lcd1602_ret();
// 设置光标位置
void lcd1602_setcursor(LCD1602_DATA x, LCD1602_DATA y);
// 光标左移
void lcd1602_cursor_left();
// 光标右移
void lcd1602_cursor_right();
// 屏幕左移
void lcd1602_screen_left();
// 屏幕右移
void lcd1602_screen_right();
// 打印字符串 (使用 NUL 截断)
void lcd1602_print(char * s);
// 打印字符串 (使用固定长度,不会被 NUL 截断)
void lcd1602_nprint(char * s, int n);
// 在字库中构造生成对应的字符
void lcd1602_define(LCD1602_DATA char_code, LCD1602_DATA bytes[8]);
#endif /* __LCD1602__ */
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。