51单片机笔记[5]-总线扩展模块

实验目的

掌握总线扩展的协议规范和应用方法

实验内容

1-WIRE总线芯片DS18B20应用

  1. 通过DS18B20测试温度,通过数码管显示
  2. 通过键盘设置上下限温度
  3. 当温度超过上限,通过蜂鸣器报警(单音)

DHT11、24C02芯片应用

  1. 每5分钟通过DHT11测试温湿度
  2. 将温湿度(保留一位小数)保存至外部存储器24C02
  3. 从存储器中取出保存的温湿度值,通过LCD1602显示

原理

LCD1602

1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。它由若干个5X7或者5X11等点阵字符位组成,每个点阵字符位都可以显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用,正因为如此所以它不能很好地显示图形(用自定义CGRAM,显示效果也不好)。

要显示汉字需要对汉字取字模,汉字只能单排显示。常用取字模软件:PCtoLCD2002(网上一搜就有)

取字模

主要有如下几种取字模的方式:

  • 行列式,顺向(高位在前),阳码
  • 列行式,顺向(高位在前),阳码
  • 逐行式,顺向(高位在前),阳码
  • 逐列式,顺向(高位在前),阳码

然后生成c数组

时序图的阅读


(1)时序图最左边一般是某一根引脚的标识,表示此行图线体现该引脚的变化,上图分别标明了RS、R/W、E、DB0~DB7四类引脚的时序变化。

(2)有线交叉状的部分,表示电平在变化,如上所标注。

(3)应该比较容易理解,如上图右上角所示,两条平行线分别对应高低电平,也正好吻合(2)中电平变化的说法。

(4)上图下,密封的菱形部分,注意要密封,表示数据有效,Valid Data这个词也显示了这点。

DS18B20和DHT11测试温湿度

  1. 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项目文件🐶

效果

视频床:https://streamja.com 如果无法加载以上帧框,请移步[https://streamja.com/pPgj2]查看效果😂
posted @ 2022-09-10 17:08  qsBye  阅读(497)  评论(0编辑  收藏  举报