51单片机笔记[5]-总线扩展模块
实验目的
掌握总线扩展的协议规范和应用方法
实验内容
1-WIRE总线芯片DS18B20应用
- 通过DS18B20测试温度,通过数码管显示
- 通过键盘设置上下限温度
- 当温度超过上限,通过蜂鸣器报警(单音)
DHT11、24C02芯片应用
- 每5分钟通过DHT11测试温湿度
- 将温湿度(保留一位小数)保存至外部存储器24C02
- 从存储器中取出保存的温湿度值,通过LCD1602显示
原理
LCD1602
1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。它由若干个5X7或者5X11等点阵字符位组成,每个点阵字符位都可以显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用,正因为如此所以它不能很好地显示图形(用自定义CGRAM,显示效果也不好)。
要显示汉字需要对汉字取字模,汉字只能单排显示。常用取字模软件:PCtoLCD2002(网上一搜就有)
取字模
主要有如下几种取字模的方式:
- 行列式,顺向(高位在前),阳码
- 列行式,顺向(高位在前),阳码
- 逐行式,顺向(高位在前),阳码
- 逐列式,顺向(高位在前),阳码
然后生成c数组
时序图的阅读
(1)时序图最左边一般是某一根引脚的标识,表示此行图线体现该引脚的变化,上图分别标明了RS、R/W、E、DB0~DB7四类引脚的时序变化。
(2)有线交叉状的部分,表示电平在变化,如上所标注。
(3)应该比较容易理解,如上图右上角所示,两条平行线分别对应高低电平,也正好吻合(2)中电平变化的说法。
(4)上图下,密封的菱形部分,注意要密封,表示数据有效,Valid Data这个词也显示了这点。
DS18B20和DHT11测试温湿度
- DS18B20介绍
DS18B20是美国DALLAS半导体公司推出的第一片支持“一线总线”接口的温度传感器,它具有微型化,低功耗,高性能,抗干扰能力强,易配微处理器等优点,可直接将温度转化成数字信号处理器处理。测量的温度范围是—55125℃,测温误差0.5℃。可编程分辨率912位,对应的可分辨温度分别为0.5℃,0.25℃,0.125℃和0.0625℃。相较热电偶传感器而言可实现高精度测温。
DS18B20控制方法(DS18B20有六条控制命令):
- 温度转换 44H 启动DS18B20进行温度转换
- 读暂存器 BEH 读暂存器9位二进制数字
- 写暂存器 4EH 将数据写入暂存器的TH、TL字节
- 复制暂存器 48H 把暂存器的TH、TL字节写到E2RAM中
- 重新调E2RAM B8H 把E2RAM中的TH、TL字节写到暂存器TH、TL字节
- 读电源供电方式 B4H 启动DS18B20发送电源供电方式的信号给主CPU
[https://zhuanlan.zhihu.com/p/396356659]
2. DHT11介绍
DHT11数字温湿度传感器是一款能够检测温湿度的复合传感器,其内置一个测温元件、一个电阻式感湿元件和一个单片机。通信也是使用单线(1-WIRE)方式。通信过程:配对(握手)->51单片机接收数据
当配对成功后,DHT11就会默认发送40个比特位,即一共5个字节,其中包含两个字节的当前温度值、两个字节是当前湿度值和一个校验值。
[https://zhuanlan.zhihu.com/p/483408243]仅供参考,详细说明还请翻阅官方手册。
24C02芯片作为存储器
串行E2PROM是基于I2C-BUS 的存储器件,遵循二线制协议,由于其具有接口方便,体积小,数据掉电不丢失等特点,在仪器仪表及工业自动化控制中得到大量的应用。24C02与单片机的接口非常简单,如下图所示。E0,E1,E2为器件地址线,WP为写保护引脚,SCL,SDA为二线串行接口,符合I2C总线协议。
IIC协议(IIC又作I2C)
IIC为两线式串行总线,它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。
在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbs以上。
时钟线SCL:在通信过程起到控制作用。
数据线SDA:用来一位一位的传送数据。
实验过程
原理图
DS18B20应用 | 24C02应用 |
---|---|
![]() |
👈 |
流程图
总体流程 |
---|
代码
敬告读者:程序有bug,温度还没有转换...
目录结构
├── 12.c
├── _51_24C02
│ ├── README.md
│ ├── _24c02.c
│ └── _24c02.h
├── _51_DHT11
│ ├── README.md
│ ├── dht11.c
│ └── dht11.h
├── _51_DS18B20
│ ├── README.md
│ ├── ds18b20.c
│ └── ds18b20.h
└── _51_LCD1602
├── README.md
├── lcd1602.c
└── lcd1602.h
文件:12.c
#include <stc15f2k60s2.h>
#include "_51_LCD1602\lcd1602.h"
#include "_51_DS18B20\ds18b20.h"
#include "_51_DHT11\dht11.h"
#include "_51_24C02\_24c02.h"
#include "stdio.h"
#ifndef u8
#define u8 unsigned char
#endif
/*实验五*/
//LCD1602 : P4 and P5^0 P5^1 P5^2
char str_temp[16];
float t = 3.14;
sbit SPEAKER=P7^0;
int MAX_TEMP = 40;
int MIN_TEMP = 0;
sbit KEY1=P1^0;
sbit KEY2=P1^3;
sbit KEY3=P1^6;
uchar temp_from_ds18b20[16];
void display_temp_settings();
//延时n毫秒
void delay_ms(unsigned int n)
{
unsigned int i=0,j=0;
for(i=0;i<n;i++)
for(j=0;j<123;j++);
}
//按键监听
void key_scan(){
if(KEY1==0){
delay_ms(10);
if(KEY1==0){
MAX_TEMP++;
display_temp_settings();
}
}
if(KEY2==0){
delay_ms(10);
if(KEY2==0){
MIN_TEMP--;
display_temp_settings();
}
}
}
//显示温度设置
void display_temp_settings(){
/*默认已经初始化LCD1602了*/
uchar _temp[16];
sprintf(_temp,"MAX:%d",MAX_TEMP);
LCD1602_displayString(1,1,_temp);
sprintf(_temp,"MIN:%d",MIN_TEMP);
LCD1602_displayString(2,1,_temp);
delay_ms(20000);
}
void display_ds_temp(){
/*默认已经初始化LCD1602*/
int i;
u8 dht11_hum[3],dht11_temp[3];
ds18b20_read(temp_from_ds18b20);
key_scan();
if((int)temp_from_ds18b20>MAX_TEMP | (int)temp_from_ds18b20<MIN_TEMP){
SPEAKER=1;
delay_ms(1000);
SPEAKER=~SPEAKER;
}
LCD1602_clear();
LCD1602_displayString(1,1,"----DS18B20----");
for (i=0;i<10;i++)
{
LCD1602_displayString(2,1,"the value:");
sprintf(str_temp,"%d",temp_from_ds18b20);
LCD1602_displayString(2,11,str_temp);
delay_ms(500);
}
delay_ms(20000);
}
void display_dht11_temp(){
/*默认已经初始化LCD1602*/
int i;
u8 dht11_hum[3],dht11_temp[3];
DHT11ReadData(dht11_hum,dht11_temp);
LCD1602_clear();
LCD1602_displayString(1,1,"----DHT11----");
for (i=0;i<10;i++)
{
sprintf(str_temp,"Hum:%d,Temp:%d",dht11_hum,dht11_temp);
LCD1602_displayString(2,1,str_temp);
delay_ms(500);
}
delay_ms(20000);
}
void read_write_display(){
/*默认已经初始化LCD1602*/
unsigned char dofly[4]={1,2,3,4}; // 显示码值 1234
unsigned char i;
u8 dht11_hum[3],dht11_temp[3];
DHT11ReadData(dht11_hum,dht11_temp);
ISendStr(0xae,4,dht11_hum,4); //写入24c02
DelayMs(200);
dofly[0]=0; //清除当前数据
dofly[1]=0;
dofly[2]=0;
dofly[3]=0;
IRcvStr(0xae,4,dofly,4); //调用存储数据
LCD1602_clear();
LCD1602_displayString(1,1,"24c02");
sprintf(str_temp,"Hum:%d,Temp:%d",dofly[2],dofly[2]);
LCD1602_displayString(2,1,str_temp);
delay_ms(20000);
}
void read_write_display_default(){
unsigned char dofly[4]={1,2,3,4}; // 显示码值 1234
unsigned char i;
ISendStr(0xae,4,dofly,4); //写入24c02
DelayMs(200);
dofly[0]=0; //清除当前数据
dofly[1]=0;
dofly[2]=0;
dofly[3]=0;
IRcvStr(0xae,4,dofly,4); //调用存储数据
LCD1602_clear();
LCD1602_displayString(1,1,"24c02");
sprintf(str_temp,"Hum:%d,Temp:%d",dofly[2],dofly[2]);
LCD1602_displayString(2,1,str_temp);
delay_ms(10000);
}
void main()
{
LCD1602_init();
SPEAKER=0;
display_temp_settings();
while(1)
{
key_scan();
display_ds_temp();
display_dht11_temp();
//read_write_display_default();
read_write_display();
}
}
目录:_51_DHT11
文件:dht11.h
#ifndef DHT11_H
#define DHT11_H
#include <stc15f2k60s2.h>
/*
记得修改引脚
*/
#define u8 unsigned char
sbit DHT11 = P0^1;
extern u8 DHT11ReadData(u8 *Humi, u8 *Temp);
void Delay_ms(int ms);
void Delay_us(int us);
#endif
文件:dht11.c
#include "_51_DHT11\dht11.h"
/*
void Delay_ms(int ms){
int i,j;
for(i=0;i<ms;i++){
for(j=0;j<200;j++);
}
}
*/
void Delay_us(int us){
int i;
for(i=0;i<us;i++){
for(i=0;i<us;i++);
}
}
//1-检测到响应信号 0-未检测到
u8 DHT11RstAndCheck(void)
{
u8 timer = 0;
EA = 0;
DHT11 = 0;
Delay_ms(20);
DHT11 = 1;
Delay_us(30);
while(!DHT11)
{
timer++;
Delay_us(1);
}
if(timer>100 || timer<20)
{
EA = 1;
return 0;
}
timer = 0;
while(DHT11)
{
timer++;
Delay_us(1);
}
EA = 1;
if(timer>100 || timer<20)
{
return 0;
}
return 1;
}
//读一字节数据
u8 DHT11ReadByte(void)
{
u8 i;
u8 byt = 0;
EA = 0;
for(i=0; i<8; i++)
{
while(DHT11);
while(!DHT11);
Delay_us(40);
byt <<= 1;
if(DHT11)
{
byt |= 0x01;
}
}
EA = 1;
return byt;
}
//读取一次数据(整数) 0-读取失败 1-读取成功
u8 DHT11ReadData(u8 *Humi, u8 *Temp)
{
u8 status = 0;
u8 i;
u8 buf[5];
if(DHT11RstAndCheck())
{
for(i=0; i<5; i++)
{
buf[i] = DHT11ReadByte();
}
if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4])
{
*Humi = buf[0];
*Temp = buf[2];
}
status = 1;
}
else
{
*Humi = 0xFF;
*Temp = 0xFF;
status = 0;
}
return status;
}
目录:_51_24C02
文件:_24c02.h
#ifndef _24C02_H
#define _24C02_H
/*
记得修改引脚
*/
#include <stc15f2k60s2.h>
#include <intrins.h>
sbit _24C02_SCK = P3^0;
sbit _24C02_SDA = P3^1;
sbit _24C02_WP = P3^2;
void SendByte(unsigned char c);
unsigned char RcvByte();
extern bit IRcvStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no);
extern bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no);
void DelayUs2x(unsigned char t);//函数声明
void DelayMs(unsigned char t);
#endif
文件:_24c02.c
/*-----------------------------------------------
名称:IIC协议 EEPROM24c02
内容:此程序用于检测EEPROM性能,测试方法如下:写入24c02一些数据,然后在内存中清除这些数据,
掉电后主内存将失去这些信息,然后从24c02中调入这些数据。看是否与写入的相同。
函数是采用软件延时的方法产生SCL脉冲,固对高晶振频率要作 一定的修改....(本例是1us机器
周期,即晶振频率要小于12MHZ)
------------------------------------------------*/
#include "_51_24C02\_24c02.h"
#define _Nop() _nop_() //定义空指令
// 常,变量定义区
unsigned char code dofly_DuanMa[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,
0x77,0x7c,0x39,0x5e,0x79,0x71};// 显示段码值0~F
unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮,即位码
bit ack; //应答标志位
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
启动总线
------------------------------------------------*/
void Start_I2c()
{
_24C02_SDA=1; //发送起始条件的数据信号
_Nop();
_24C02_SCK=1;
_Nop(); //起始条件建立时间大于4.7us,延时
_Nop();
_Nop();
_Nop();
_Nop();
_24C02_SDA=0; //发送起始信号
_Nop(); //起始条件锁定时间大于4μ
_Nop();
_Nop();
_Nop();
_Nop();
_24C02_SCK=0; //钳住I2C总线,准备发送或接收数据
_Nop();
_Nop();
}
/*------------------------------------------------
结束总线
------------------------------------------------*/
void Stop_I2c()
{
_24C02_SDA=0; //发送结束条件的数据信号
_Nop(); //发送结束条件的时钟信号
_24C02_SCK=1; //结束条件建立时间大于4μ
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
_24C02_SDA=1; //发送I2C总线结束信号
_Nop();
_Nop();
_Nop();
_Nop();
}
/*----------------------------------------------------------------
字节数据传送函数
函数原型: void SendByte(unsigned char c);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0 假)
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
------------------------------------------------------------------*/
void SendByte(unsigned char c)
{
unsigned char BitCnt;
for(BitCnt=0;BitCnt<8;BitCnt++) //要传送的数据长度为8位
{
if((c<<BitCnt)&0x80)_24C02_SDA=1; //判断发送位
else _24C02_SDA=0;
_Nop();
_24C02_SCK=1; //置时钟线为高,通知被控器开始接收数据位
_Nop();
_Nop(); //保证时钟高电平周期大于4μ
_Nop();
_Nop();
_Nop();
_24C02_SCK=0;
}
_Nop();
_Nop();
_24C02_SDA=1; //8位发送完后释放数据线,准备接收应答位
_Nop();
_Nop();
_24C02_SCK=1;
_Nop();
_Nop();
_Nop();
if(_24C02_SDA==1)ack=0;
else ack=1; //判断是否接收到应答信号
_24C02_SCK=0;
_Nop();
_Nop();
}
/*----------------------------------------------------------------
字节数据传送函数
函数原型: unsigned char RcvByte();
功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
发完后请用应答函数。
------------------------------------------------------------------*/
unsigned char RcvByte()
{
unsigned char retc;
unsigned char BitCnt;
retc=0;
_24C02_SDA=1; //置数据线为输入方式
for(BitCnt=0;BitCnt<8;BitCnt++)
{
_Nop();
_24C02_SCK=0; //置时钟线为低,准备接收数据位
_Nop();
_Nop(); //时钟低电平周期大于4.7us
_Nop();
_Nop();
_Nop();
_24C02_SCK=1; //置时钟线为高使数据线上数据有效
_Nop();
_Nop();
retc=retc<<1;
if(_24C02_SDA==1)retc=retc+1; //读数据位,接收的数据位放入retc中
_Nop();
_Nop();
}
_24C02_SCK=0;
_Nop();
_Nop();
return(retc);
}
/*----------------------------------------------------------------
应答子函数
原型: void Ack_I2c(void);
----------------------------------------------------------------*/
void Ack_I2c(void)
{
_24C02_SDA=0;
_Nop();
_Nop();
_Nop();
_24C02_SCK=1;
_Nop();
_Nop(); //时钟低电平周期大于4μ
_Nop();
_Nop();
_Nop();
_24C02_SCK=0; //清时钟线,钳住I2C总线以便继续接收
_Nop();
_Nop();
}
/*----------------------------------------------------------------
非应答子函数
原型: void NoAck_I2c(void);
----------------------------------------------------------------*/
void NoAck_I2c(void)
{
_24C02_SDA=1;
_Nop();
_Nop();
_Nop();
_24C02_SCK=1;
_Nop();
_Nop(); //时钟低电平周期大于4μ
_Nop();
_Nop();
_Nop();
_24C02_SCK=0; //清时钟线,钳住I2C总线以便继续接收
_Nop();
_Nop();
}
/*----------------------------------------------------------------
向无子地址器件发送字节数据函数
函数原型: bit ISendByte(unsigned char sla,ucahr c);
功能: 从启动总线到发送地址,数据,结束总线的全过程,从器件地址sla.
如果返回1表示操作成功,否则操作有误。
注意: 使用前必须已结束总线。
----------------------------------------------------------------*/
/*bit ISendByte(unsigned char sla,unsigned char c)
{
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址
if(ack==0)return(0);
SendByte(c); //发送数据
if(ack==0)return(0);
Stop_I2c(); //结束总线
return(1);
}
*/
/*----------------------------------------------------------------
向有子地址器件发送多字节数据函数
函数原型: bit ISendStr(unsigned char sla,unsigned char suba,ucahr *s,unsigned char no);
功能: 从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
如果返回1表示操作成功,否则操作有误。
注意: 使用前必须已结束总线。
----------------------------------------------------------------*/
bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no)
{
unsigned char i;
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址
if(ack==0)return(0);
SendByte(suba); //发送器件子地址
if(ack==0)return(0);
for(i=0;i<no;i++)
{
SendByte(*s); //发送数据
DelayMs(1);
if(ack==0)return(0);
s++;
}
Stop_I2c(); //结束总线
return(1);
}
/*----------------------------------------------------------------
向无子地址器件读字节数据函数
函数原型: bit IRcvByte(unsigned char sla,ucahr *c);
功能: 从启动总线到发送地址,读数据,结束总线的全过程,从器件地
址sla,返回值在c.
如果返回1表示操作成功,否则操作有误。
注意: 使用前必须已结束总线。
----------------------------------------------------------------*/
/*bit IRcvByte(unsigned char sla,unsigned char *c)
{
Start_I2c(); //启动总线
SendByte(sla+1); //发送器件地址
if(ack==0)return(0);
*c=RcvByte(); //读取数据
NoAck_I2c(); //发送非就答位
Stop_I2c(); //结束总线
return(1);
}
*/
/*----------------------------------------------------------------
向有子地址器件读取多字节数据函数
函数原型: bit ISendStr(unsigned char sla,unsigned char suba,ucahr *s,unsigned char no);
功能: 从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
如果返回1表示操作成功,否则操作有误。
注意: 使用前必须已结束总线。
----------------------------------------------------------------*/
bit IRcvStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no)
{
unsigned char i;
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址
if(ack==0)return(0);
SendByte(suba); //发送器件子地址
if(ack==0)return(0);
Start_I2c();
SendByte(sla+1);
if(ack==0)return(0);
for(i=0;i<no-1;i++)
{
*s=RcvByte(); //发送数据
Ack_I2c(); //发送就答位
s++;
}
*s=RcvByte();
NoAck_I2c(); //发送非应位
Stop_I2c(); //结束总线
return(1);
}
目录:_51_DS18B20
文件:ds18b20.h
#ifndef DS18B20_H
#define DS18B20_H
/*
记得修改引脚
*/
sbit DS18B20=P6^0; //定义DS18B20的输出端
#define uchar unsigned char
#define uint unsigned int
extern uchar ds18b20_read(uchar _ds_temp);
extern void ds18b20_write(uchar * _ds_temp);
#endif
文件:ds18b20.c
#include <stc15f2k60s2.h>
#include "_51_DS18B20\ds18b20.h"
uint temp; // 温度的变量
uchar flag1; // 标志位
void delay(uint count) //延时
{
uint i;
while(count)
{
i=200;
while(i>0)
i--;
count--;
}
}
void dsreset(void) //DS18B20复位、初始化函数
{
uint i;
DS18B20=0;
i=103; //将总线拉低480us~960us
while(i>0)i--;
DS18B20=1; //然后拉高总线,若DS18B20做出反应会将在15us~60us后将总线拉低
i=4; //15us~60us等待
while(i>0)i--;
//while(DS);
}
bit tmpreadbit(void) //读一字节,看是0还是1
{
uint i;
bit dat;
DS18B20=0;i++; //i++ for delay
DS18B20=1;i++;i++;
dat=DS18B20;
i=8;while(i>0)i--;
return (dat);
}
uchar ds18b20_read(uchar _ds_temp) //读一字节数据函数
{
uchar i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{
j=tmpreadbit();
dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面,这样刚好一个字节在DAT里
}
_ds_temp=dat;
return(dat);
}
uchar tmpread(void) //读一字节数据函数
{
uchar i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{
j=tmpreadbit();
dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面,这样刚好一个字节在DAT里
}
return(dat);
}
void ds18b20_write(uchar *_ds_temp) //向DS18B20写1字节数据函数
{
uint i;
uchar j,dat;
bit testb;
for(j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) //写入1
{
DS18B20=0;
i++;i++;
DS18B20=1;
i=8;while(i>0)i--;
}
else
{
DS18B20=0; //写入0
i=8;while(i>0)i--;
DS18B20=1;
i++;i++;
}
}
_ds_temp = &dat;
}
void tmpwritebyte(uchar dat) //向DS18B20写1字节数据函数
{
uint i;
uchar j;
bit testb;
for(j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) //写入1
{
DS18B20=0;
i++;i++;
DS18B20=1;
i=8;while(i>0)i--;
}
else
{
DS18B20=0; //写入0
i=8;while(i>0)i--;
DS18B20=1;
i++;i++;
}
}
}
void tmpchange(void) //DS18B20 开始获取温度并且转换
{
dsreset();
delay(1);
tmpwritebyte(0xcc); // 写跳过读ROM指令
tmpwritebyte(0x44); // 写温度转换指令
}
uint tmp() //读取寄存器中储存的温度数据
{
float tt;
uchar a,b;
dsreset();
delay(1);
tmpwritebyte(0xcc); //写跳过读ROM指令
tmpwritebyte(0xbe); //写读取温度指令
a=tmpread(); //读低8位
b=tmpread(); //读高8位
temp=b;
temp<<=8; //2字节组合为1个字
temp=temp|a;
tt=temp*0.0625; //温度在寄存器中为12位,分辨率为0.065
temp=tt*10+0.5; //乘以10表示小数点后面只取一位,加0.5是四舍五入
return temp;
}
目录:_51_LCD1602
文件:lcd1602.h
#ifndef __LCD1602_H__
#define __lcd1602_H__
#define LCD1602_Order_OFF 0x08
#define LCD1602_Order_CursorClose 0x0C
#define LCD1602_Port P4 //LCD输出的8位串行数据口
sbit LCD1602_RS = P5^2; //0命令 1数据
sbit LCD1602_RW = P5^1; //0写 1读
sbit LCD1602_EN = P5^0; //读写使能,0允许
extern void LCD1602_init(); //初始化函数
extern void LCD1602_Write(unsigned char cmd,unsigned char dat); //写开启函数
extern void LCD1602_displayString(unsigned char hang, unsigned char lie, unsigned char *pstr); //显示字符串函数
extern void LCD1602_CursorSet(unsigned char hang, unsigned char lie); //设置光标函数
extern void LCd1602_clear();//清屏
#define LCD1602_CursorClose() LCD1602_Write(0,LCD1602_Order_CursorClose) //关闭光标
#define LCD1602_Off() LCD1602_Write(0,LCD1602_Order_OFF) //关显示
/************LCD1602指令集及配置*****************运行时间(在250KHz下)*/
#define LCD1602_Order_Clear 0x01 //清屏 1.64us
#define LCD1602_Order_Home 0x02 //光标归位 1.64us
#define LCD1602_Order_Mode_ID 0x01 //1=数据读写操作后,AC自动增1; 0=自动减1
#define LCD1602_Order_Mode_S 0x00 //1=数据读写操作 画面平移; 0=画面不动
#define LCD1602_Order_Mode (0x04|\
(LCD1602_Order_Mode_ID<<1)|\
LCD1602_Order_Mode_S) //输入方式设置 40us
#define LCD1602_Order_OnOFF_D 0x01 //1=开显示,0=关显示
#define LCD1602_Order_OnOFF_C 0x00 //1=显示光标,0=不显示光标
#define LCD1602_Order_OnOFF_B 0x00 //1=光标闪烁,0=不闪烁
#define LCD1602_Order_OnOFF (0x08|\
(LCD1602_Order_OnOFF_D<<2)|\
(LCD1602_Order_OnOFF_C<<1)|\
LCD1602_Order_OnOFF_B) //显示开关控制 40us
#define LCD1602_Order_Shift_SC 0x00 //0=光标平移一个字符位,1=画面平移
#define LCD1602_Order_Shift_RL 0x01 //1=右移,0=左移
#define LCD1602_Order_Shift (0x10|\
(LCD1602_Order_Shift_SC<<3)|\
(LCD1602_Order_Shift_RL<<2)) //光标、画面移动 40us
#define LCD1602_Order_Function_DL 0x00 //1=8位数据接口,0=4位
#define LCD1602_Order_Function_N 0x01 //1=两行显示,0=1行
#define LCD1602_Order_Function_F 0x01 //1=5*10点阵,0=5*7
#define LCD1602_Order_Function (0x20|\
(LCD1602_Order_Function_DL<<4)|\
(LCD1602_Order_Function_N<<3)|\
(LCD1602_Order_Function_F<<2)) //工作方式设置 40us
#define LCD1602_Order_CGRAM_Address 0 //CGRAM(Character Generator 字符产生器)地址(共6位)
#define LCD1602_Order_CGRAM (0x40|LCD1602_Order_CGRAM_Address) //CGRAM地址设置 40us
#define LCD1602_Order_DDRAM_Address 0 //一行显示:0~0x4F;两行显示:首行:0~0x27,次行:0x40~0x67
#define LCD1602_Order_DDRAM (0x80|LCD1602_Order_DDRAM_Address) //设置DDRAM地址 40us
#endif
文件:lcd1602.c
#include <stc15f2k60s2.h>
#include "_51_LCD1602\lcd1602.h"
//自带延时函数
void LCD1602_Delay(unsigned int dely)
{
for(;dely>0;dely--);
}
//读忙
unsigned char LCD1602_ReadBusy()
{
unsigned char lcd_status;
LCD1602_RS=0;
LCD1602_RW=1;
LCD1602_EN=1;
#if(LCD1602_Order_Function_DL==1)
LCD1602_Port=0xFF;
#endif
#if(LCD1602_Order_Function_DL==0)
LCD1602_Port|=0xF0;
#endif
LCD1602_Delay(10);
lcd_status=LCD1602_Port;
LCD1602_EN=0;
#if(LCD1602_Order_Function_DL==0)
LCD1602_EN=1;
LCD1602_Delay(10);
lcd_status=(lcd_status&0xF0)|((0xF0&LCD1602_Port)>>4);
LCD1602_EN=0;
#endif
return lcd_status;
}
/*向液晶显示器写命令/数据函数
参数说明:cmd=0为写命令,cmd=1为写数据
*/
void LCD1602_Write(unsigned char cmd,unsigned char dat)
{
while((LCD1602_ReadBusy()&0x80) == 0x80);
LCD1602_RS=cmd;
LCD1602_RW=0;
LCD1602_EN=1;
#if(LCD1602_Order_Function_DL==1)
LCD1602_Port=dat;
#endif
#if(LCD1602_Order_Function_DL==0)
LCD1602_Port=(LCD1602_Port&0x0F)|(dat&0xF0);
LCD1602_Delay(50);
LCD1602_EN=0;
LCD1602_EN=1;
LCD1602_Port=(LCD1602_Port&0x0F)|(dat<<4);
#endif
LCD1602_Delay(50);
LCD1602_EN=0;
}
/*显示字符串
参数说明:起始行,列,字符串
*/
void LCD1602_displayString(unsigned char hang, unsigned char lie, unsigned char *pstr)
{
LCD1602_Write(0,0x80+(0x40 * (hang - 1)) + (lie - 1));
while(*pstr)
{
LCD1602_Write(1,*pstr++);
}
}
/*让指定位置闪烁光标
参数说明:行,列
*/
void LCD1602_CursorSet(unsigned char hang, unsigned char lie)
{
LCD1602_Write(0,0x80+(0x40 * (hang - 1)) + (lie - 1));
LCD1602_Write(0,0x0F);
}
/*初始化*/
void LCD1602_init()
{
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_EN=1;
#if(LCD1602_Order_Function_DL==1)
LCD1602_Port=LCD1602_Order_Function;
#endif
#if(LCD1602_Order_Function_DL==0)
LCD1602_Port=(LCD1602_Port&0x0f)|(LCD1602_Order_Function&0xF0);
#endif
LCD1602_Delay(10);
LCD1602_EN=0;
LCD1602_Delay(10000);
LCD1602_Write(0,LCD1602_Order_Function); //工作方式设置
LCD1602_Write(0,LCD1602_Order_OFF); //关显示
LCD1602_Write(0,LCD1602_Order_Clear); //清屏
LCD1602_Write(0,LCD1602_Order_Mode); //输入方式设置
LCD1602_Write(0,LCD1602_Order_OnOFF); //开显示
}
/*清屏*/
void LCd1602_clear(){
LCD1602_Write(0,LCD1602_Order_Clear); //清屏
}
文件分享
[https://www.qsbye.cn/files/8d9e7ca5201103d96d9a54995ccd94f9.zip]
Proteus版本:Proteus 8.10
归档内含源码、hex文件、Proteus项目文件🐶
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2021-09-10 QS27及SZ-J2辉光管资料
2021-09-10 下载安卓源码手记(mac)