需求:使用LED 1602模块,显示 NO.1_iKUN / chang_tiao_rup ;
OK,我们先看下最终效果,再来详谈其实现,展示:
1.接线
VO:接个可调电阻到GND,方便调试对比度,提升显示效果,实在没有直接GND 在侧面找角度,也勉强能看 QAQ;
RS = P1^0;
RW = P1^1;
EN = P1^4;
2.see see时序图,实现读写 数据/指令 函数封装;(see代码注释)
写指令+写数据(地址):
void write_cmd_func(char cmd)
{
read_busy();
RS = 0; // RS低电平代表写 指令(确定位置);
RW = 0;
EN = 0; //根据时序图让E 开始跳变;
_nop_();
databuffer = cmd; //看时序图在等待E 变为高电压前,开始写入指令/数据;
EN = 1;
_nop_();
EN = 0;
_nop_();
}
void write_data_func (char userdata)
{
read_busy();
RS = 1; // R高电平代表写 数据(显示字符)
RW = 0;
EN = 0;
_nop_();
databuffer = userdata; //看时序图在等待E 变为高电压前,开始写入指令/数据;
EN = 1;
_nop_();
EN = 0;
_nop_();
}
读取忙标志 例:
void read_busy()
{
char tmp = 0x80;
databuffer = 0x80;
while(tmp & databuffer){ //只有当1602屏幕返回高位为0 时,即while(0); 推出循环;
RS = 0;
RW = 1;
EN = 0;
_nop_();
EN = 1;
_nop_();
tmp = databuffer; //看时序图在等待E 变为高电压后,开始读取指令/数据;
EN = 0;
_nop_();
}
}
3.最困难的日子过去了,再简单单写个void write_line_data(char hang,char qishilie,char *new),编译完成工程;
注意:
a.1602 其列的偏移是自动的,不需要写到循环里去控制,所以在每行开头声明一次即可;
b.再每次使用前,在主函数内需初始化1602(这条是必须的,手册要求)+ 清理屏幕(避免之前的数据(地址)残留);
源码展示:
#include "reg52.h"
#include <intrins.h> //解决延时空函数_nop_报错;
#define databuffer P0 //8位数据线,使用P0端口组 ^0-7
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
void Delay15ms() //@11.0592MHz
{
unsigned char i, j;
i = 27;
j = 226;
do
{
while (--j);
} while (--i);
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 9;
j = 244;
do
{
while (--j);
} while (--i);
}
void read_busy()
{
char tmp = 0x80;
databuffer = 0x80;
while(tmp & databuffer){ //只有当1602屏幕返回高位为0 时,即while(0); 推出循环;
RS = 0;
RW = 1;
EN = 0;
_nop_();
EN = 1;
_nop_();
tmp = databuffer; //看时序图在等待E 变为高电压后,开始读取指令/数据;
EN = 0;
_nop_();
}
}
void write_cmd_func(char cmd)
{
read_busy();
RS = 0; // RS低电平代表写 指令(确定位置);
RW = 0;
EN = 0;
_nop_();
databuffer = cmd; //看时序图在等待E 变为高电压前,开始写入指令/数据;
EN = 1;
_nop_();
EN = 0;
_nop_();
}
void write_data_func (char userdata)
{
read_busy();
RS = 1; // R高电平代表写 数据(显示字符)
RW = 0;
EN = 0;
_nop_();
databuffer = userdata; //看时序图在等待E 变为高电压前,开始写入指令/数据;
EN = 1;
_nop_();
EN = 0;
_nop_();
}
void init1602()
{
// LCD1602 初始化过程(8bit)
// (1)延时 15ms
Delay15ms();
// (2)写指令 38H(不检测忙信号)
write_cmd_func(0x38);
// (3)延时 5ms
Delay5ms();
// (4)以后每次写指令,读/写数据操作均需要检测忙信号
//---见read_busy函数;
// (5)写指令 38H:显示模式设置
write_cmd_func(0x38);
// (6)写指令 08H:显示关闭
write_cmd_func(0x08);
// (7)写指令 01H:显示清屏
write_cmd_func(0x01);
// (8)写指令 06H:显示光标移动设置
write_cmd_func(0x06);
// (9)写指令 0CH:显示开及光标设置
write_cmd_func(0x0C);
}
void write_line_data(char hang,char qishilie,char *new)
{
switch(hang){
case 1:
//写入显示地址(位置)时,每次都要让高位^7为高电平1 即+0x80;
//注意:1602 其列的偏移是自动的,不需要写到循环里去控制,所以在每行开头声明一次即可;
write_cmd_func(0x80+qishilie);
while(*new){
write_data_func(*new);
new++;
}
break;
case 2:
//第二行位置地址是0x40开头,所以位置在第二行就要+0x40;
write_cmd_func(0x80+0x40+qishilie);
while(*new){
write_data_func(*new);
new++;
}
break;
}
}
void main ()
{
init1602();
write_cmd_func(0x01); //清理之前的显示
write_line_data(1,3,"NO.01_iKUN");
write_line_data(2,1,"chang_tiao_rup");
}
分享作者在调试撸码过程中遇到的一个大坑:
//如果变量名直接写成data,会导致一直报错;
//因为data是C语言中的保留关键字之一,会产生冲突报错;
【代码重构了也不行,最后的最后排查到了它,真的这种小基础很容易让人遗忘;切记切记!!!】 ToT
最后预祝大家赏kun顺利!!QAQ