这张图片说明LCD1602共有两行32个位置来显示字母,那如何定义在哪个位置来显示字母,显示的字母是什么呢?
这里我们定义一组输出口为D0—D7,共8位来控制字符的输出。
例如第二行第一个字符的地址是 40H,那么是否直接写入 40H 就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位 D7 恒定为高电平 1 所以实际写入的数据应该是 01000000B(0x40) +10000000B(0x80)=11000000B(0xC0)。

要写入数据时,我们让数据口输出为上面每个字符对应的8位码即可。
RS 为寄存器选择,高电平时选择数据寄存器、低电平时选择指令(地址)寄存器。
R/W 为读写信号线,高电平时进行读操作,低电平时进行写操作。当 RS 和 R/W 共同为低电平时可以写入指令或者显示地址,当 RS 为低电平 R/W 为高电平时可以读忙信号,当 RS 为高电平 R/W 为低电平时可以写入数据。

接线:
VSS – GND
VDD – 5V
对比度 VO – GND
控制线 RS – P1.0
RW – P1.1 E – P1.4
背光灯 A – 5V K – GDN
数据 D0到D7 – P0.到P0.7

LCD1602 初始化过程(8bit)
(1)延时 15ms
(2)写指令 38H(不检测忙信号) (3)延时 5ms
(4)以后每次写指令,读/写数据操作均需要检测忙信号
(5)写指令 38H:显示模式设置
(6)写指令 08H:显示关闭
(7)写指令 01H:显示清屏
(8)写指令 06H:显示光标移动设置
(9)写指令 0CH:显示开及光标设置

我们进行编程控制时,通常让RS固定位0或者1,因为它全程可为0可为1,我们习惯性定义为全程0或1,RW位在读数据时是0,写数据时是1,写数据是给屏幕一个8位码来显示想要的字符,读数据也叫测忙,也就是当LCD1602在读取数据时不能往里面写入数据。通常只根据EN端进行编程控制,Valid time是LCD进行读写数据的时间,编程时序控制仅需考虑此处即可(EN+Valid time)。BF(8位数据的最高位):为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。

#include <reg52.h>
#include <intrins.h>
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
#define DataBuffer P0//定义P0口为屏幕数据写入区,类似定义了一个寄存器
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 Check_Busy()
{char tmp = 0x80; DataBuffer = 0x80;//八位数据位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙while(tmp & 0x80)//高电平表示忙(&完为1读数据),此时模块不能接收命令或者数据,如果为低电平表示不忙(&完为0不读数据){RS = 0;RW = 1;//读数据时我们常把RW定义为1EN = 0;_nop_();EN = 1;_nop_();tmp = DataBuffer;//不断读总线上的数据,_nop_();//延时1微妙EN = 0;_nop_();//以使能端为核心写程序,伴随写数据}
}
void Write_Data_Func(char Data)//写操作即为给LCD屏幕写数据
{Check_Busy();RS = 1;//RS高电平写数据RW = 0;//写数据时我们常把RW定义为0EN = 0;_nop_();DataBuffer = Data;_nop_();//延时1微妙EN = 1;_nop_();_nop_();EN = 0;_nop_();
}
void Write_Adr_Func(char Adr)//写操作即为给LCD屏幕写地址
{Check_Busy();RS = 0;//RS低电平写地址RW = 0;//写数据时我们常把RW定义为0EN = 0;_nop_();DataBuffer = Adr;_nop_();EN = 1;_nop_();_nop_();EN = 0;_nop_();
}
void LCD1602_Write(char row,char col,char* String)//row:行,col:列
{switch(row){case 1:Write_Adr_Func(0x80+col);//在第一行第col列写入字符while(*String != '\0'){Write_Data_Func(*String);//每次写一个字符String++;//每次往LCD写完数据后,LCD自动往后移}break;case 2:Write_Adr_Func(0x80+0x40+col);//在第一行第col列写入字符while(*String != '\0'){Write_Data_Func(*String);//每次写一个字符String++;//每次往LCD写完数据后,LCD自动往后移}break;}
}void LCD1602_INIT()//官方指令初始化
{//(1)延时 15msDelay15ms();
//(2)写指令(地址) 38H(不检测忙信号) Write_Adr_Func(0x38);
//(3)延时 5msDelay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号
//(5)写指令 38H:显示模式设置Write_Adr_Func(0x38);
//(6)写指令 08H:显示关闭Write_Adr_Func(0x08);
//(7)写指令 01H:显示清屏Write_Adr_Func(0x01);
//(8)写指令 06H:显示光标移动设置Write_Adr_Func(0x06);
//(9)写指令 0CH:显示开及光标设置}Write_Adr_Func(0x0c);
}
void main()
{LCD1602_INIT();//官方指令初始化LCD1602_Write(1,5,"N0.1");LCD1602_Write(2,1,"PiPi is a Dog");}

这里的查忙程序,我们不断的让它查询(系统自动查一位移移位),当总线上数据读取为0时,代表不忙。
二、让LCD屏幕显示温湿度
(1)移位操作
C语言中,左移为逻辑左移,空间不变,数据左移,左移则右侧补0。char一字节等于8bit。
注意:定义局部变量与整体变量名字不要重复
采用DHT11测量温湿度并让温湿度显示在LCD屏幕上,当温度>26°时,开启风扇。

#include "reg52.h"
#include "intrins.h"
#define DataBuffer  P0 //定义8位数据线,Po端口组
sbit LED = P3^7;
sbit DHT = P3^3;//模块的data插在p3.3
sbit fengshan = P2^4;
char Datas[5];sfr AUXR = 0x8E;
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
char temp[11];
char huma[11];void Check_Busy()
{char tmp = 0x80; DataBuffer = 0x80;//八位数据位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙while(tmp & 0x80)//高电平表示忙(&完为1读数据),此时模块不能接收命令或者数据,如果为低电平表示不忙(&完为0不读数据){RS = 0;RW = 1;//读数据时我们常把RW定义为1EN = 0;_nop_();EN = 1;_nop_();tmp = DataBuffer;//不断读总线上的数据,_nop_();//延时1微妙EN = 0;_nop_();//以使能端为核心写程序,伴随写数据}
}
void Write_Data_Func(char Data)//写操作即为给LCD屏幕写数据
{Check_Busy();RS = 1;//RS高电平写数据RW = 0;//写数据时我们常把RW定义为0EN = 0;_nop_();DataBuffer = Data;_nop_();//延时1微妙EN = 1;_nop_();_nop_();EN = 0;_nop_();
}
void Write_Adr_Func(char Adr)//写操作即为给LCD屏幕写地址
{Check_Busy();RS = 0;//RS低电平写地址RW = 0;//写数据时我们常把RW定义为0EN = 0;_nop_();DataBuffer = Adr;_nop_();EN = 1;_nop_();_nop_();EN = 0;_nop_();
}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 UartInit(void)        //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x40; //配置串口工作方式1,REN不使能接收TMOD &= 0xF0;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//启动定时器
}
void SendBit(char msg)
{SBUF = msg;    //把数据存到数据发送缓冲区,因为需要移位寄存器的操作进行字符的移位,//所以需要缓冲时间,我们用发送中断位来缓冲这部分时间while(!TI);    //发送中断位,当不发送字符时,一直停留在此处TI = 0;        //TI:发送中断请求标志位。 在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,//响应中断后必须用软件复位,即TI=0。 在其他方式中,则在停止位开始发送时由内部硬件置位,必须用软件复位。//为什么要延时呢
}
void SendStr(char* str)
{while(*str != '\0'){SendBit(*str);str++;}
}void Delay30ms()       //@11.0592MHz
{unsigned char i, j;i = 54;j = 199;do{while (--j);} while (--i);
}void Delay60us()       //@11.0592MHz
{unsigned char i;i = 25;while (--i);
}void Delay1000ms()     //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 8;j = 1;k = 243;do{do{while (--k);} while (--j);} while (--i);
}void DHT_Start()//从0开始读数据
{DHT = 1;DHT = 0;Delay30ms();DHT = 1;while(DHT);//当检测到模块存在时DHT有模块置为低电平while(!DHT);while(DHT);//DHT为0表示开始传送数据
}
void Delay40us()        //@11.0592MHz
{unsigned char i;_nop_();i = 15;while (--i);
}void DHT_Read_Data()
{int i;int j;char flag;//每次读到的数据位char tmp;//定义一个8bit的变量,一轮读到的数存到这8bit里面DHT_Start();//每次读数据都要检测模块存在for(i=0;i<5;i++){for(j=0;j<8;j++){while(!DHT);//当1bit的读数据标志位过去后开始读取数据Delay40us();if(DHT == 1){flag = 1;while(DHT);//等待数据电平过去}else{flag = 0;//此时的低电平并不是数据位,而是代表判断}tmp = tmp << 1;//每次判断完数据,temp都左移一位tmp |= flag;}Datas[i] = tmp;//每次读完8bit数据后,全部存进字符数组里}
}void LCD1602_INIT()
{//(1)延时 15msDelay15ms();
//(2)写指令 38H(不检测忙信号) Write_Adr_Func(0x38);
//(3)延时 5msDelay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号
//(5)写指令 38H:显示模式设置Write_Adr_Func(0x38);
//(6)写指令 08H:显示关闭Write_Adr_Func(0x08);
//(7)写指令 01H:显示清屏Write_Adr_Func(0x01);
//(8)写指令 06H:显示光标移动设置Write_Adr_Func(0x06);
//(9)写指令 0CH:显示开及光标设置}Write_Adr_Func(0x0c);
}void LCD1602_Write(char row, char col, char *string)
{switch(row){case 1:Write_Adr_Func(0x80+col);while(*string){Write_Data_Func(*string);string++;}break;case 2:Write_Adr_Func(0x80+0x40+col);while(*string){Write_Data_Func(*string);string++;}break;}
}void Build_Datas()
{huma[0] = 'H';huma[1] = ':';huma[2] = ' ';huma[3] = Datas[0]/10 + 0x30;huma[4] = Datas[0]%10 + 0x30;huma[5] = '.';huma[6] = Datas[1]/10 + 0x30;huma[7] = Datas[1]%10 + 0x30;huma[8] = '%';huma[9] = '\0';temp[0] = 'T';temp[1] = ':';temp[2] = ' ';temp[3] = Datas[2]/10 + 0x30;temp[4] = Datas[2]%10 + 0x30;temp[5] = '.';temp[6] = Datas[3]/10 + 0x30;temp[7] = Datas[3]%10 + 0x30;temp[8] = 0xDF;temp[9] = 'C';temp[10] = '\0';}void main()
{Delay1000ms();UartInit();LCD1602_INIT();while(1){Delay1000ms();DHT_Read_Data();if(Datas[2]>26){fengshan = 0;Delay1000ms();fengshan = 1;}Build_Datas();SendStr(huma);SendStr("\r\n");SendStr(temp);SendStr("\r\n");LCD1602_Write(1,2,huma);LCD1602_Write(2,2,temp);}
}

下面我们进行分文件优化代码,分文件优化代码时,先把写好的函数按功能分别分开做成几个C文件,再定义几个头文件,头文件里面函数只需要声明(说明该头文件包含哪些函数),加分号表示声明。头文件只是用来封装函数,当主函数里面遇到重复变量时,仍然需要定义,不被主函数内部调用时,封装头文件便不用封装。

通过LCD1602来了解时序相关推荐

  1. matlab信号时序图,怎么看时序图,电路原理图(转)

    片选:动词,单片机学科词汇,可以理解成选片.很多芯片挂在同一总线上的时候,有一个信号来区别总线上的数据和地址由哪个芯片来处理,这个信号就叫做片选信号CS(chip select).片选这个词即由此而来 ...

  2. DS1302时钟与LCD1602液晶显示

    DS1302时钟 DS1302是一个实时时钟芯片,它在实时显示时间中应用.它可以对年.月.日.周.时.分.秒进行计时. DS1302的引脚及原理图如下: 它与CPU的连接需要SCLK(7).I/O(6 ...

  3. LCD1602模块-带IIC转接板

    LCD1602模块-带IIC转接板 LCD1602简介 写时序 读时序 指令表 字符表 IIC转接板-PCF8574.PCF8574T IIC地址 IIC写时序 IIC读时序 PCF8574手册 示例 ...

  4. 基于STM8的数字温度计设计

    基于STM8的数字温度计设计 这是在2021年12月初完成的一次课程设计,全程自己设计组装完成,现作为记录存档发布,大家也可以借鉴本文来完成自己的课程设计.(建议使用电脑阅读) 课程设计目录 基于ST ...

  5. lj245a引脚功能图_如何快速读懂时序图?

    我们都知道在学校是通过铃声来控制所有班级的上下课时间,那个单片机是通过什么样的办法进行取指令,执行指令和其它操作的呢?在这里引入了一个时序的概念. 一.时钟电路 单片机时钟电路有三种方式:① 单片机内 ...

  6. sip 时序图_时序图怎么看_教你如何看懂时序图 - 什么是时序图_时序图怎么看_教你如何看懂时序图...

    时序图怎么看_教你如何看懂时序图 操作时序永远使用是任何一片IC芯片的最主要的内容.一个芯片的所有使用细节都会在它的官方器件手册上包含.所以使用一个器件事情,要充分做好的第一件事就是要把它的器件手册上 ...

  7. LCD1602液晶显示模块的单片机驱动深入详解之硬件篇

    (本文以HD44780主控芯片的LCD1602为蓝本进行描述,其中的截图也来自HD44780数据手册,用户可自行搜索其datasheet,有部分整理网上的,但绝对要比你看到的要深入得多) 一.接口 L ...

  8. lcd和服务器同时显示温度,为什么LCD1602不能同时显示温度和时间?

    程序目标:1602第一行显示温度,第二行显示时间.温度用DS18b20,时间用DS1302. 目前存在问题:用1602单独显示温度和时间都没得问题.合在一起显示就出现温度值为0,时间可以正常显示. 初 ...

  9. lj245a引脚功能图_技术文章—教你快速读懂单片机时序图

    我们都知道在学校是通过铃声来控制所有班级的上下课时间,那个单片机是通过什么样的办法进行取指令,执行指令和其它操作的呢?在这里引入了一个时序的概念: 一.时钟电路 单片机时钟电路有三种方式: 1.单片机 ...

最新文章

  1. 思科路由器动态NAT配置
  2. java与java ee_Java EE拦截器
  3. PB代码动态解析执行器
  4. python操作注册表能干啥_转 python操作注册表模块_winreg
  5. CGAL中Point的引用
  6. [uva11235]Frequent values(RMQ,ST,离散化)
  7. linux自建git仓库
  8. web安全之信息收集
  9. STM32一体化步进电机驱动器控制步进电机
  10. 总结-模型评价指标的定义(基于CatBoost文档)
  11. 文王八卦圖와 易經組織
  12. 给你一个全自动的屏幕适配方案(基于SW方案)!—— 解放你和UI的双手
  13. 《Rework》语句摘录
  14. 计算机重装系统后无法连接网络连接,重装WIN7系统后怎样连接到网络
  15. zotero+谷歌翻译不能用,最后的解决方案!
  16. 9.PS-强大的画笔工具
  17. FPGA的NIOS-||的开发入门
  18. C++静态库和动态库的导入导出
  19. 【深度学习】TensorFlow基础知识点总结
  20. 解决IDEA报错:Could not find resource mybatis-config.xml

热门文章

  1. L-Edit 11.1-附资源包
  2. 奔流(RollingStream)IP数字电视(IPTV)整体解决方案
  3. MediaCodec对接到OMX的简单分析
  4. 【Arduino学习】一、IO口操作
  5. 硬盘无法格式化该怎么办?
  6. 2022年天猫七夕价格便宜吗?天猫七夕节有什么优惠?
  7. android 数据库 事务,Android数据库中事务操作方法之银行转账示例
  8. 市级政务云平台建设与运营解决方案
  9. 安卓快手跳一跳辅助v1.0.0
  10. 树莓派+USB摄像头自制网络摄像头