LCD1602显示屏三个操作的引脚其它的都是不需要操作的,在手册里面可以看到
分别是RS数据命令端,就是用来操作屏幕的命令的。R/W端就是用来读写操作的,E就是使能端,当需要进行那个状态的时候就是用于使能变换的。
屏幕的命令操作地址也就是指令集,标红的就是常用的。等下代码讲到
还有就是屏幕内置已经是集成可以显示什么字符,还有几个字符是可以自己自定义显示的,因为这个每一个操作位已经是做好选择的,还有就是留了几个空位用于自己自定义的。可以看到一些字符指令集是对应着地址位的。
还有可以看到屏幕是每个格子显示的,而且格子之间是有地址的,第一行的地址是00开始
而第二行的地址就是40,所以等下代码就是如果需要在第二行显示字符加上40就可以寻址到第二行去了,而且屏幕是可以显示到28个地址位这么长,但是呢碍于屏幕的大小,只能看到16个字符
可以看到c51的原理图上三个操作引脚是接到P25是读写端、P26是命令端、P27是使能端
剩下的很多引脚就是数据引脚,用来把数据写到这个引脚然后显示,可以看到整个P0的引脚都是数据引脚
接下来就是边写代码边解释为什么这样写。
首先写之前说明一个引脚的操作RS写1为写数据、写0为写指令。RW写1为读,写0为写。E使能1为有效,下降沿执行命令。P0的引脚为数据输入/输出
首先把引脚初始化,这些引脚就是刚才说到的引脚。
#include <regx52.h>
#include "LCD1602.h"
sbit LCD1602_WR=P2^5; //读写端 1读0写
sbit LCD1602_RS=P2^6; //数据/命令 1写数据0写命令
sbit LCD1602_EN=P2^7; //时序端
#define LCD1602_DAtaport P0 //数据位
初始化完了就要开始看时序图了
写指令代码,这里有一个延时函数,是用软件生成的,需要根据每个时钟晶振来确定延迟函数,这里需要延时是因为这个在时序图里面有符号对应着延迟的值,因为每次使能都需要一定的时间来确定虽然这里写着ns级别,按道理来说是可以执行的,但是实际上面的指令集的图却是ms级别,所以要一定的延时写入数据才行。
/**
* @brief LCD1602专属延迟函数1ms
* @param 无
* @retval 无
*/
void LCD_Delay1ms() //@11.0592MHz需要根据不同的晶振生成不同的延迟
{
unsigned char i, j;
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
void LCD1602_Wirecomd(unsigned char comd) //写命令
{
LCD1602_RS=0; //写命令
LCD1602_WR=0; //写操作
LCD1602_DAtaport=comd; 把命令传输到P0,然后识别到指令操作
LCD1602_EN=1; //由于没有延迟 反应太快,读不过来
LCD_Delay1ms();
LCD1602_EN=0;
LCD_Delay1ms();
}
写完命令,开始写数据了,只需要把Rs为该为1就是写数据了,使能端的操作还是一样的。由高到低才会有数据为产生,否是是无效的,也就是红色圈起来那个就是数据命令操作,然后RS、RW就要看写1还是0就是读写命令数据了。当这些做完E这个端是由高到低的,这样才完成一个操作
/**
* @brief LCD1602写数据函数
* @param Data:LCD1602要写入的数据
* @retval 无
*/
void LCD1602_Wiredata(unsigned char Data) //写数据
{
LCD1602_RS=1; //写数据
LCD1602_WR=0; //写操作
LCD1602_DAtaport=Data;
LCD1602_EN=1; //由于没有延迟 反应太快,读不过来
LCD_Delay1ms();
LCD1602_EN=0;
LCD_Delay1ms();
}
数据命令都写好了就到了屏幕的指令集了,就是屏的初始化,这里调用的就是写命令的函数
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*声明:里面的初始化函数写的命令集可更改,根据需要的效果更改指令集里面的指令
*/
void LCD1602_Init() //初始化指令
{ //LCD1602_Wirecomd(0x18); 流动字幕
LCD1602_Wirecomd(0x38); //模式 8位数据位接口 两行显示 5*7点阵
LCD1602_Wirecomd(0x0c); //显示开 光标关 闪烁关1
LCD1602_Wirecomd(0x06); //数据读写操作后,光标自动加一,画面不动
LCD1602_Wirecomd(0x01); //清屏
}
可以看到0x38红色的线8421-8421码来分就是0x38也就是0011-1000功能指令集这里也写得很清楚而且可以看到无论哪一行RS和RW都是00,除了后面那一行是有1,但是那个基本用不到,为什么光标位移那里不写呢,就是要静止显示。所以就没有写,也可以把注释去掉就可以看到屏幕流动了。
写完这些初始化下面开始选行列了,这里除了第一行以外,就是第二行了,所以参数Line除了1就是第二行了,所以写else要加上0x40,为什么写的列要或上0x80就是因为高位从屏幕的左边往右显示减1是因为屏幕的坐标是从0开始。
/**
* @brief LCD1602写控制行 列函数
* @param Line:1或2
* @param Colum:1~16
* @retval 无
*/
void LCD1602_SetCour(unsigned char Line,unsigned char Colum)
{
if(Line==1)
{
LCD1602_Wirecomd(0x80|(Colum-1)); //显示 发送指令0x80|AC //设置光标位置
}
else
{
LCD1602_Wirecomd(0x80|(Colum-1)+0x40); //第二行
}
}
既然初始化命令以及坐标都写好了,里面也集成好了字符,那么就可以直接写显示了。就是这么简单,直接写位置,然后写数据。就行了
/**
* @brief LCD1602写单个字符函数
* @param Line:1或2
* @param Colum:1~16
* @param Char:‘要写入的字符’ 或者ASiic标的十六进制数
* @retval 无
*/
void LCD1602_ShowChar(unsigned char Line,unsigned char Colum, char Char )
{
LCD1602_SetCour(Line,Colum);
LCD1602_Wiredata(Char);
}
那既然能显示一个字符了,是不是就可以按照这个显示一串字符啦,用指针操作,指向第一个地址,只要我没有遇到结束标志符\0那么就一直执行地址加的操作。
/**
* @brief LCD1602写字符串函数
* @param Line:1或2
* @param Colum:1~16
* @param *String:“要写入的字符串”
* @retval 无
*/ //指向字符串首地址
void LCD1602_ShowString(unsigned char Line,unsigned char Colum, char *String )
{
unsigned char i;
LCD1602_SetCour(Line,Colum); //设置位置
for(i=0;String[i]!='\0';i++) //识别字符结束标志
LCD1602_Wiredata(String[i]); //写入数据
}
那既然显示字符了,就要显示数字啦,这里数字就要看着asiic表操作了,因为这个集成内置字符就是按照这个表来显示的,其实也很简单。
首先是显示无符号数字,为了显示方便我们数字显示可没有像字符串那样的结束标志,所以要确定长度,而且要把字符数字转换位asiic码里面的字符数字,所以我们的数据要从‘0’的位置开始显示,所以要加上字符‘0’的位置。那么下面有一个操作为什么要这样呢,首先解释x的y次方。可以看到LCD1602_POW(10,i-1)传的参数是10和i-1,首先假装我们的i是3,所以第一次我们的数据就是10的3-1次方那么就是等于10的2次方等于100然后number这个数除100是不是就等于高位的数的第一个数了,然后对10取余也就是这一个数。,然后i等于2的时候就是10的1次方等于10,然后number除10等于后两个数,然后对10取余那么就是剩余个位数了。最后i等于0的时候,就是number除1还是本身,然后求余10也是剩余个位数,所以着3位数就一一显示出来了。
/**
* @brief x的y次方
* 声明:为了方便下面数字显示哪个位置的数字而建立的函数
*/
int LCD1602_POW(int x,int y) //x的y次方
{
unsigned char i;
int Result=1;
for(i=0;i<y;i++)
Result*=x;
return Result;
}
/**
* @brief LCD1602写无字符数字函数
* @param Line:1或2
* @param Colum:1~16
* @param Number:写入的数字 0~65536
* @param Length:数字长度
* @retval 无
*/
void LCD1602_ShowNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length )
{ //Number i
//789 /100%10 7 3 10^i-1=100
unsigned char i; //789 /10%10 8 2 10^i-1=10
LCD1602_SetCour(Line,Colum); //设置位置 //789 /1%10 9 1 10^i-1=1
for(i=Length;i>0;i--)
{
LCD1602_Wiredata('0'+Number/LCD1602_POW(10,i-1)%10); //从数字转Asiic表 0x30或'0'就是标的字符0开始这样加上多少就是显示多少数字
}
}
接下来显示有符号字符了,直接判断这个数大于0还是小于0,然后在数据面前写多一个字符就好了
/**
* @brief LCD1602写有符号数字函数
* @param Line:1或2
* @param Colum:1~16
* @param Number:写入的数字 -32768~32767
* @param Length:数字长度
* @retval 无
*/
void LCD1602_SingShowNum(unsigned char Line,unsigned char Colum, int Number,unsigned char Length )
{
unsigned char i;
unsigned int Number1=0; //65535 为了更好的发输出数字需要变成多位显示,所以多一个变量来存numbwer的值
LCD1602_SetCour(Line,Colum);
if(Number>=0)
{
LCD1602_Wiredata('+');
Number1=Number;
}
else
{
LCD1602_Wiredata('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD1602_Wiredata('0'+Number1/LCD1602_POW(10,i-1)%10); //从数字转Asiic表 0x30或'0'就是标的字符0开始这样加上多少就是显示多少数字
}
}
显示十六进制,同样设置长度,然后按照刚才的次方函数把10该为16就是16进制的啦,但是刚才的数字显示是在0开始,现在的十六进制就要在字符‘A’的位置开始了。因为大于10的时候我们要用a,b,c,d,e,f显示,所以大于等于10的时候就要在a的位置开始,不然还是从0开始
/**
* @brief LCD1602写十六进制函数
* @param Line:1或2
* @param Colum:1~16
* @param Number:写入的十六进制数 0x00~0xff
* @param Length:数字长度 2
* @retval 无
*/
void LCD1602_ShowHexNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length )
{
unsigned char i,SingleNumber;
SingleNumber=Number;
LCD1602_SetCour(Line,Colum); //设置位置
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD1602_POW(16,i-1)%16;
if(SingleNumber>=10)
{
LCD1602_Wiredata('A'+SingleNumber-10); //因为16进制大于10的话开始的字符偏移用重新设置 ,但是如果大于10我们要从a开始计所以要再减去10
}
else
{
LCD1602_Wiredata('0'+SingleNumber); //从数字转Ascii表 0x30或'0'就是标的字符0开始这样加上多少就是显示多少数字
}
}
}
二进制显示一个到了只有1和0,那么就是2的次方,数字只有0和1不能超过2,所以要对2求余。都是一个道理。
/**
* @brief LCD1602写二进制函数
* @param Line:1或2
* @param Colum:1~16
* @param Number:写入的十六进制数 0x00~0xff
* @param Length:数字长度 8
* @retval 无
*/
void LCD1602_ShowBinNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length )
{
unsigned char i;
LCD1602_SetCour(Line,Colum); //设置位置
for(i=Length;i>0;i--)
{
LCD1602_Wiredata('0'+Number/LCD1602_POW(2,i-1)%2); //显示二进制数 对2求余只显示0或1
} //因为输入的是十六进制数,所以对应的是Ascii表的 二进制数
}
代码解释到这里差不多了,剩下的也可以自己拓展类似于8进制,还有一些其他的都要可以慢慢试试。还有就是指令集也是每一个写一遍看看效果怎么样,这样自己印象更深刻。
接下来完整代码.c文件
#include <regx52.h>
#include "LCD1602zijida.h"
sbit LCD1602_WR=P2^5; //读写端 1读0写
sbit LCD1602_RS=P2^6; //数据/命令 1写数据0写命令
sbit LCD1602_EN=P2^7; //时序端
#define LCD1602_DAtaport P0 //数据位
/**
* @brief LCD1602专属延迟函数
* @param 无
* @retval 无
*/
void LCD_Delay1ms() //@11.0592MHz
{
unsigned char i, j;
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令函数
* @param comd:LCD162要写入的命令,就是指令集里面的命令
* @retval 无
*/
void LCD1602_Wirecomd(unsigned char comd) //写命令
{
LCD1602_RS=0; //写命令
LCD1602_WR=0; //写操作
LCD1602_DAtaport=comd;
LCD1602_EN=1; //由于没有延迟 反应太快,读不过来
LCD_Delay1ms();
LCD1602_EN=0;
LCD_Delay1ms();
}
/**
* @brief LCD1602写数据函数
* @param Data:LCD1602要写入的数据
* @retval 无
*/
void LCD1602_Wiredata(unsigned char Data) //写数据
{
LCD1602_RS=1; //写数据
LCD1602_WR=0; //写操作
LCD1602_DAtaport=Data;
LCD1602_EN=1; //由于没有延迟 反应太快,读不过来
LCD_Delay1ms();
LCD1602_EN=0;
LCD_Delay1ms();
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*声明:里面的初始化函数写的命令集可更改,根据需要的效果更改指令集里面的指令
*/
void LCD1602_Init() //初始化指令
{ //LCD1602_Wirecomd(0x18); 流动字幕
LCD1602_Wirecomd(0x38); //模式 8位数据位接口 两行显示 5*7点阵
LCD1602_Wirecomd(0x0c); //显示开 光标关 闪烁关1
LCD1602_Wirecomd(0x06); //数据读写操作后,光标自动加一,画面不动
LCD1602_Wirecomd(0x01); //清屏
}
/**
* @brief LCD1602写控制行 列函数
* @param Line:1或2
* @param Colum:1~16
* @retval 无
*/
void LCD1602_SetCour(unsigned char Line,unsigned char Colum)
{
if(Line==1)
{
LCD1602_Wirecomd(0x80|(Colum-1)); //显示 发送指令0x80|AC //设置光标位置
}
else
{
LCD1602_Wirecomd(0x80|(Colum-1)+0x40); //第二行
}
}
/**
* @brief LCD1602写单个字符函数
* @param Line:1或2
* @param Colum:1~16
* @param Char:‘要写入的字符’ 或者ASiic标的十六进制数
* @retval 无
*/
void LCD1602_ShowChar(unsigned char Line,unsigned char Colum, char Char )
{
LCD1602_SetCour(Line,Colum);
LCD1602_Wiredata(Char);
}
/**
* @brief LCD1602写字符串函数
* @param Line:1或2
* @param Colum:1~16
* @param *String:“要写入的字符串”
* @retval 无
*/ //指向字符串首地址
void LCD1602_ShowString(unsigned char Line,unsigned char Colum, char *String )
{
unsigned char i;
LCD1602_SetCour(Line,Colum); //设置位置
for(i=0;String[i]!='\0';i++) //识别字符结束标志
LCD1602_Wiredata(String[i]); //写入数据
}
/**
* @brief x的y次方
* 声明:为了方便下面数字显示哪个位置的数字而建立的函数
*/
int LCD1602_POW(int x,int y) //x的y次方
{
unsigned char i;
int Result=1;
for(i=0;i<y;i++)
Result*=x;
return Result;
}
/**
* @brief LCD1602写无字符数字函数
* @param Line:1或2
* @param Colum:1~16
* @param Number:写入的数字 0~65536
* @param Length:数字长度
* @retval 无
*/
void LCD1602_ShowNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length )
{ //Number i
//789 /100%10 7 3 10^i-1=100
unsigned char i; //789 /10%10 8 2 10^i-1=10
LCD1602_SetCour(Line,Colum); //设置位置 //789 /1%10 9 1 10^i-1=1
for(i=Length;i>0;i--)
{
LCD1602_Wiredata('0'+Number/LCD1602_POW(10,i-1)%10); //从数字转Asiic表 0x30或'0'就是标的字符0开始这样加上多少就是显示多少数字
}
}
/**
* @brief LCD1602写有符号数字函数
* @param Line:1或2
* @param Colum:1~16
* @param Number:写入的数字 0~65536
* @param Length:数字长度
* @retval 无
*/
void LCD1602_SingShowNum(unsigned char Line,unsigned char Colum, int Number,unsigned char Length )
{
unsigned char i;
unsigned int Number1=0; //65535 为了更好的发输出数字需要变成多位显示
LCD1602_SetCour(Line,Colum);
if(Number>=0)
{
LCD1602_Wiredata('+');
Number1=Number;
}
else
{
LCD1602_Wiredata('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD1602_Wiredata('0'+Number1/LCD1602_POW(10,i-1)%10); //从数字转Asiic表 0x30或'0'就是标的字符0开始这样加上多少就是显示多少数字
}
}
/**
* @brief LCD1602写十六进制函数
* @param Line:1或2
* @param Colum:1~16
* @param Number:写入的十六进制数 0x00~0xff
* @param Length:数字长度 2
* @retval 无
*/
void LCD1602_ShowHexNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length )
{
unsigned char i,SingleNumber;
SingleNumber=Number; //789 /10%10 8 2 10^i-1=10
LCD1602_SetCour(Line,Colum); //设置位置 //789 /1%10 9 1 10^i-1=1
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD1602_POW(16,i-1)%16;
if(SingleNumber>=10)
{
LCD1602_Wiredata('A'+SingleNumber-10); //因为16进制大于10的话开始的字符偏移用重新设置 ,但是如果大于10我们要从a开始计所以要再减去10
}
else
{
LCD1602_Wiredata('0'+SingleNumber); //从数字转Ascii表 0x30或'0'就是标的字符0开始这样加上多少就是显示多少数字
}
}
}
/**
* @brief LCD1602写二进制函数
* @param Line:1或2
* @param Colum:1~16
* @param Number:写入的十六进制数 0x00~0xff
* @param Length:数字长度 8
* @retval 无
*/
void LCD1602_ShowBinNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length )
{
unsigned char i;
LCD1602_SetCour(Line,Colum); //设置位置
for(i=Length;i>0;i--)
{
LCD1602_Wiredata('0'+Number/LCD1602_POW(2,i-1)%2); //显示二进制数 对2求余只显示0或1
} //因为输入的是十六进制数,所以对应的是Ascii表的 二进制数
}
.h文件
#ifndef _LCD1602ZIJIDA_H_
#define _LCD1602ZIJIDA_H_
void LCD1602_Init(); //初始化指令
void LCD_Delay1ms();
void LCD1602_Wirecomd(unsigned char comd); //写命令
void LCD1602_Wiredata(unsigned char Data); //写数据
void LCD1602_SetCour(unsigned char Line,unsigned char Colum);
void LCD1602_ShowChar(unsigned char Line,unsigned char Colum,char Char );
void LCD1602_ShowString(unsigned char Line,unsigned char Colum, char *String );
void LCD1602_ShowNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length );
void LCD1602_SingShowNum(unsigned char Line,unsigned char Colum, int Number,unsigned char Length );
void LCD1602_ShowHexNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length );
void LCD1602_ShowBinNum(unsigned char Line,unsigned char Colum, unsigned int Number,unsigned char Length );
#endif