您的位置:首页 > 编程语言 > C语言/C++

模拟实时测温,1602显示,一个I/O口上挂两个温感DS18B20,实现于仿真上PROTEUS

2017-01-08 09:27 465 查看
DS1302 与微处理器进行数据交换时,首先由微处理器向电路发送命令字节,命令字节最高位Write
Protect(D7)必须为逻辑1,如果D7=0,则禁止写DS1302,即写保护;D6=0,指定时钟数据,D6=1,指定RAM数据;D5~D1指定输入或输出的特定寄存器;最低位LSB(D0)为逻辑0,指定写操作(输入),
D0=1,指定读操作(输出)。

在DS1302的时钟日历或RAM进行数据传送时,DS1302必须首先发送命令字节。若进行单字节传送,8位命令字节传送结束之后,在下2个SCLK周期的上升沿输入数据字节,或在下8个SCLK周期的下降沿输出数据字节。

DS1302与RAM相关的寄存器分为两类:一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为C0H~FDH,其中奇数为读操作,偶数为写操作;再一类为突发方式下的RAM寄存器,在此方式下可一次性读、写所有的RAM的31个字节。

   DS18B20数字温度计是DALLAS公司生产的1-Wire,即单总线器件,具有线路简单,体积小的特点。因此用它来组成一个测温系统,具有线路简单,在一根通信线,可以挂很多这样的数字温度计,十分方便。

  1、DS18B20产品的特点

    (1)、只要求一个端口即可实现通信。

    (2)、在DS18B20中的每个器件上都有独一无二的序列号。

    (3)、实际应用中不需要外部任何元器件即可实现测温。

    (4)、测量温度范围在-55。C到+125。C之间。

    (5)、数字温度计的分辨率用户可以从9位到12位选择。

    (6)、内部有温度上、下限告警设置。

 2、DS18B20的使用方法
  由于DS18B20采用的是1-Wire总线协议方式,即在一根数据线实现数据的双向传输,而对AT89S51单片机来说,硬件上并不支持单总线协议,因此,我们必须采用软件的方法来模拟单总线的协议时序来完成对DS18B20芯片的访问。
  由于DS18B20是在一根I/O线上读写数据,因此,对读写的数据位有着严格的时序要求。DS18B20有严格的通信协议来保证各位数据传输的正确性和完整性。该协议定义了几种信号的时序:初始化时序、读时序、写时序。所有时序都是将主机作为主设备,单总线器件作为从设备。而每一次命令和数据的传输都是从主机主动启动写时序开始,如果要求单总线器件回送数据,在进行写命令后,主机需启动读时序完成数据接收。数据和命令的传输都是低位在先。
  1) DS18B20的读时序
  对于DS18B20的读时序分为读0时序和读1时序两个过程。
  对于DS18B20的读时隙是从主机把单总线拉低之后,在15秒之内就得释放单总线,以让DS18B20把数据传输到单总线上。DS18B20在完成一个读时序过程,至少需要60us才能完成。
  2)DS18B20的写时序
  对于DS18B20的写时序仍然分为写0时序和写1时序两个过程。
  对于DS18B20写0时序和写1时序的要求不同,当要写0时序时,单总线要被拉低至少60us,保证DS18B20能够在15us到45us之间能够正确地采样IO总线上的“0”电平,当要写1时序时,单总线被拉低之后,在15us之内就得释放单总线。

 

 

源代码:

主函数:

#include <REG51.H>

#include <intrins.h>

#include "bianliang.h"

#include "lcd1602.h"

#include "ds18b20.h"

#include "ds1302.h"

#define Lcd_Data 1

#define uchar unsigned char

#define uint unsigned int

 

void show_time(unsigned int x,unsigned int y)   //显示时间子函数

{

  DS1302_GetTime(&CurrentTime);  //获取时钟芯片的时间数据

  TimeToStr(&CurrentTime);
1a33c
      //时间数据转换液晶字符

  GotoXY(x,y);

  Print(CurrentTime.TimeString); //显示时间

  Delay1ms(50);                 //扫描延时

}

 

void show_date(unsigned int x,unsigned int y)
//显示日期子函数

{

  DS1302_GetTime(&CurrentTime);  //获取时钟芯片的时间数据

  DateToStr(&CurrentTime);       //日期数据转换液晶字符

  GotoXY(x,y);

  Print(CurrentTime.DateString); //显示日期

  Delay1ms(50);                 //扫描延时

}

 

void show_temp(unsigned int x,unsigned int y)  //显示温度子函数

{

  ReadTemp();                    //开启温度采集程序

  temp_to_str();                 //温度数据转换成液晶字符

  GotoXY(x,y);                  //液晶字符显示位置

  Print(TempBuffer);             //显示温度

  Delay1ms(50);                 //扫描延时

}

void show_week(unsigned int x,unsigned int y)

{

  DS1302_GetTime(&CurrentTime);  //获取时钟芯片的时间数据

  DateToStr(&CurrentTime);       //日期数据转换液晶字符

  GotoXY(x,y);

  Print(week_value);             //显示星期

  GotoXY(x-4,y);

  Print("Week");//在液晶上显示 字母 week

  Delay1ms(50);                 //扫描延时

}

 

 

void main()

{

//unsigned char i;

   flag=1;           //时钟停止标志

LCD_Initial();    //液晶初始化

init_ds18();

init_ds18(); //时钟芯片初始化

up_flag=0;

down_flag=0;

while(1)

{   

if(done==1)

{

LCD_clear();

Delay1ms(50);

}

else

{

show_date(0,0);                //显示日期

show_week(15,0);                   //显示星期

show_time(0,1);                //显示时间

show_temp(9,1);                //显示温度

flag=0;

}

}

//GotoXY(0,0);

//    read_rom();

//for(i = 0;i<8;i++)

//{

// write_data(0x30+ds18b20_num1[i]/16);

//delay_50us1(1);

//write_data(0x30+ds18b20_num1[i]%16);

//delay_50us1(1);

//}

//write_data(0xc0);

//write_data('c');

 

 

}

BIANLIANG.H

#define uint unsigned int

#define uchar unsigned char

sbit  DS1302_CLK = P1^2;              //实时时钟时钟线引脚

sbit  DS1302_IO  = P1^1;              //实时时钟数据线引脚

sbit  DS1302_RST = P1^3;              //实时时钟复位线引脚

sbit  DQ         = P1^0;              //温度传送数据IO口

sbit  ACC0 = ACC^0;

sbit  ACC7 = ACC^7;

char hide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year;  //秒,分,时到日,月,年位闪的计数

char done,count,temp,flag,up_flag,down_flag;

uchar temp_value;      //温度值

uchar temp_value1;      //温度值

uchar TempBuffer[17],week_value[2];

uchar ds18b20_num1[8]={0xb9,0x00,0x00,0x00,0xb8,0xc5,0x31,0x28};

uchar ds18b20_num2[8]={0x8e,0x00,0x00,0x00,0xb8,0xc5,0x30,0x28};

sbit LcdRs= P3^7;

sbit LcdRw= P3^6;

sbit LcdEn  = P3^5;

sfr  DBPort
= 0xA0;//P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口

void time_set(void);

void set_date(void);

LCD1602.H

#ifndef LCD_CHAR_1602_2005_4_9

#define LCD_CHAR_1602_2005_4_9

 

#include <intrins.h>

 

 

unsigned char LCD_Wait(void)

{

LcdRs=0;

LcdRw=1;_nop_();

LcdEn=1;_nop_();

//while(DBPort&0x80);//在用Proteus仿真时,注意用屏蔽此语句,在调用GotoXY()时,会进入死循环,

 //可能在写该控制字时,该模块没有返回写入完备命令,即DBPort&0x80==0x80

 //实际硬件时打开此语句

LcdEn=0;

return DBPort;

}

 

#define LCD_COMMAND0      // Command

#define LCD_DATA1      // Data

#define LCD_CLEAR_SCREEN0x01      // 清屏

#define LCD_HOMING  0x02      // 光标返回原点

void LCD_Write(bit style, unsigned char input)

{

LcdEn=0;

LcdRs=style;

LcdRw=0;_nop_();

DBPort=input;_nop_();//注意顺序

LcdEn=1;_nop_();//注意顺序

LcdEn=0;_nop_();

LCD_Wait();

}

 

#define LCD_SHOW0x04    //显示开

#define LCD_HIDE0x00    //显示关  

 

#define LCD_CURSOR0x02
//显示光标

#define LCD_NO_CURSOR0x00    //无光标     

 

#define LCD_FLASH0x01    //光标闪动

#define LCD_NO_FLASH0x00    //光标不闪动

 

void LCD_SetDisplay(unsigned char DisplayMode)

{

LCD_Write(LCD_COMMAND, 0x08|DisplayMode);

}

 

#define LCD_AC_UP0x02

#define LCD_AC_DOWN0x00      // default

 

#define LCD_MOVE0x01      // 画面可平移

#define LCD_NO_MOVE0x00      //default

 

void LCD_SetInput(unsigned char InputMode)

{

LCD_Write(LCD_COMMAND, 0x04|InputMode);

}

 

void LCD_Initial()

{

LcdEn=0;

LCD_Write(LCD_COMMAND,0x38);           //8位数据端口,2行显示,5*7点阵

LCD_Write(LCD_COMMAND,0x38);

LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR);    //开启显示, 无光标

LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN);   //清屏

LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE);       //AC递增, 画面不动

}

 

void LCD_clear()

{

LcdEn=0;

LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN);   //清屏

}

void GotoXY(unsigned char x, unsigned char y)

{

if(y==0)

LCD_Write(LCD_COMMAND,0x80|x);

if(y==1)

LCD_Write(LCD_COMMAND,0x80|(x-0x40));

}

void Print(unsigned char *str)

{

while(*str!='\0')

{

LCD_Write(LCD_DATA,*str);

str++;

}

}

 

#endif

DS1302.H

#define DS1302_WEEK0x8A

#define DS1302_DAY0x86

#define DS1302_MONTH0x88

#define DS1302_YEAR0x8C

 

void DS1302InputByte(unsigned char d)
//实时时钟写入一字节(内部函数)

{

    unsigned char i;

    ACC = d;

    for(i=8; i>0; i--)

    {

        DS1302_IO = ACC0;           //相当于汇编中的 RRC

        DS1302_CLK = 1;

        DS1302_CLK = 0;

        ACC = ACC >> 1;

    }

}

 

unsigned char DS1302OutputByte(void)
//实时时钟读取一字节(内部函数)

{

    unsigned char i;

    for(i=8; i>0; i--)

    {

        ACC = ACC >>1;         //相当于汇编中的 RRC

        ACC7 = DS1302_IO;

        DS1302_CLK = 1;

        DS1302_CLK = 0;

    }

    return(ACC);

}

 

void Write1302(unsigned char ucAddr, unsigned char ucDa)//ucAddr: DS1302地址, ucData: 要写的数据

{

    DS1302_RST = 0;

    DS1302_CLK = 0;

    DS1302_RST = 1;

    DS1302InputByte(ucAddr);       // 地址,命令

    DS1302InputByte(ucDa);       // 写1Byte数据

    DS1302_CLK = 1;

    DS1302_RST = 0;

}

 

unsigned char Read1302(unsigned char ucAddr)//读取DS1302某地址的数据

{

    unsigned char ucData;

    DS1302_RST = 0;

    DS1302_CLK = 0;

    DS1302_RST = 1;

    DS1302InputByte(ucAddr|0x01);        // 地址,命令

    ucData = DS1302OutputByte();         // 读1Byte数据

    DS1302_CLK = 1;

    DS1302_RST = 0;

    return(ucData);

}

 

 

 

void DS1302_GetTime(SYSTEMTIME *Time)  //获取时钟芯片的时钟数据到自定义的结构型数组

{

unsigned char ReadValue;

ReadValue = Read1302(DS1302_SECOND);

Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_MINUTE);

Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_HOUR);

Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_DAY);

Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_WEEK);

Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_MONTH);

Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_YEAR);

Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

}

 

void DateToStr(SYSTEMTIME *Time)    //将时间年,月,日,星期数据转换成液晶显示字符串,放到数组里DateString[]

{   if(hide_year<2)                 //这里的if,else语句都是判断位闪烁,<2显示数据,>2就不显示,输出字符串为 2007/07/22

    {                               

  Time->DateString[0] = '2';

  Time->DateString[1] = '0'; 

  Time->DateString[2] = Time->Year/10 + '0';

  Time->DateString[3] = Time->Year%10 + '0';

}

  else

    {

      Time->DateString[0] = ' ';

      Time->DateString[1] = ' '; 

      Time->DateString[2] = ' ';

      Time->DateString[3] = ' ';

}

Time->DateString[4] = '/';

if(hide_month<2)

{

  Time->DateString[5] = Time->Month/10 + '0';

  Time->DateString[6] = Time->Month%10 + '0';

}

  else

  {

    Time->DateString[5] = ' ';

    Time->DateString[6] = ' ';

  }

Time->DateString[7] = '/';

if(hide_day<2)

{

  Time->DateString[8] = Time->Day/10 + '0';

  Time->DateString[9] = Time->Day%10 + '0';

}

  else

  {

    Time->DateString[8] = ' ';

    Time->DateString[9] = ' ';    

  }

if(hide_week<2)

{

  week_value[0] = Time->Week%10 + '0';  //星期的数据另外放到 week_value[]数组里,跟年,月,日的分开存放,因为等一下要在最后显示

}

  else

  {

    week_value[0] = ' ';

  }

  week_value[1] = '\0';

 

Time->DateString[10] = '\0'; //字符串末尾加 '\0' ,判断结束字符

}

 

void TimeToStr(SYSTEMTIME *Time)  //将时,分,秒数据转换成液晶显示字符放到数组 TimeString[];

{   if(hide_hour<2)

    {

  Time->TimeString[0] = Time->Hour/10 + '0';

  Time->TimeString[1] = Time->Hour%10 + '0';

}

  else

    {

      Time->TimeString[0] = ' ';

      Time->TimeString[1] = ' ';

}

Time->TimeString[2] = ':';

    if(hide_min<2)

{

  Time->TimeString[3] = Time->Minute/10 + '0';

  Time->TimeString[4] = Time->Minute%10 + '0';

}

  else

    {

      Time->TimeString[3] = ' ';

      Time->TimeString[4] = ' ';

       }

Time->TimeString[5] = ':';

    if(hide_sec<2)

    {

  Time->TimeString[6] = Time->Second/10 + '0';

  Time->TimeString[7] = Time->Second%10 + '0';

    }

      else

       {

         Time->TimeString[6] = ' ';

     Time->TimeString[7] = ' ';

       }

Time->DateString[8] = '\0';

}

 

 

void Initial_DS1302(void)   //时钟芯片初始化

{   

unsigned char Second=Read1302(DS1302_SECOND);

if(Second&0x80)      //判断时钟芯片是否关闭  

    {

Write1302(0x8e,0x00); //写入允许

Write1302(0x8c,0x10); //以下写入初始化时间 日期:07/07/25.星期: 3. 时间: 23:59:55

Write1302(0x88,0x10); //月

Write1302(0x86,0x20); //年

Write1302(0x8a,0x03); //周

Write1302(0x84,0x23); //时

Write1302(0x82,0x59); //分

Write1302(0x80,0x55); //秒

Write1302(0x8e,0x80); //禁止写入

}

 

}

DS18B20.H

void WriteOneChar(uchar dat);

unsigned char ReadOneChar(void);

void delay_18B20(unsigned int i)

{

while(i--);

}

void delay_50us1(uint t)    //延时50us

{

  uchar j;

for(;t>0;t--)

  for(j = 15;j>0;j--);

}

 

void delay_5us1(uchar x)    //延时5us子函数

{

  while(--x);

}

 void delay_1ms(uchar time)   //时基1ms延时调用子函数

{

     uchar a,b;

   for(;time>0;time--)

{

    for(a=195;a>0;a--)

    for(b = 1;b>0;b--);

}

}

 

void init_ds18()    //ds18b20初始化

{

    DQ = 0;       //拉低总线480--960us,

delay_5us1(250);

DQ = 1;      //释放总线60--120us,

delay_5us1(30);

if(DQ == 0)    //总线响应低电平,初始化成功

{

  delay_5us1(200);

DQ = 1;     //延时480us内,释放总线

WriteOneChar(0xcc);    //初始化

DQ = 0;     //拉低总线480--960us

delay_5us1(255);

DQ = 1;     //释放总线,且延时480us以内

delay_5us1(240);

DQ = 1;      //释放总线

}

}

 

void write_com(uchar com)    //ds1302写入命令

{

  LcdRs = 0;

LcdRw = 0;

LcdEn = 0;

P2 = com;

delay_50us1(10);

LcdEn = 1;

delay_50us1(10);

LcdEn = 0;

}

 

//void write_data(uchar dat)     //ds1302写数据命令

//{

//    LcdRs = 1;

//LcdRw = 0;

//LcdEn = 0;

//P2 = dat;

//delay_50us1(10);

//LcdEn = 1;

//delay_50us1(10);

//LcdEn = 0;

//}

void read_rom()     //读ds18b20序列号

{

uchar i;

  init_ds18();

delay_50us1(2);

WriteOneChar(0x33);

for(i = 0;i<8;i++)

{

  ds18b20_num1[i] = ReadOneChar();

}

}

 

//void Init_DS18B20(void)

//{

// unsigned char x=0;

// delay_18B20(60);

// DQ = 1;          //DQ复位

// delay_18B20(8);  //稍做延时

// DQ = 0;          //单片机将DQ拉低

// delay_18B20(80); //精确延时 大于 480us

// DQ = 1;          //拉高总线

// delay_18B20(14);

// x=DQ;            //稍做延时后 如果x=0则初始化成功 x=1则初始化失败

// delay_18B20(20);

//}

 

unsigned char ReadOneChar(void)

{

uchar i=0;

uchar dat = 0;

for (i=8;i>0;i--)

 {  

   dat>>=1;

  DQ = 0; // 给脉冲信号

  _nop_();

  _nop_();

  DQ = 1; // 给脉冲信号

  _nop_();

  _nop_();

  if(DQ)

  dat|=0x80;

  delay_18B20(15);

  DQ=1;

  _nop_();

 }

 return(dat);

}

void WriteOneChar(uchar dat)

{

 unsigned char i=0;

 for (i=8; i>0; i--)

 {

  DQ = 0;

_nop_();

_nop_();

 DQ = dat&0x01;

    delay_18B20(15);

 DQ = 1;

_nop_();

    dat>>=1;

 }

}

 

bit match_rom(uchar *rom)

{

uchar i;

init_ds18();

WriteOneChar(0x55);

for(i=8;i>0;i--)

{

WriteOneChar(*(rom+i-1));;

}

return 1;

}

void get1_ds18b20(void)

{

unsigned char a=0;

unsigned char b=0,i;

match_rom(ds18b20_num1);

WriteOneChar(0x44);

   DQ = 1;

 

delay_1ms(255);

DQ = 0;

delay_5us1(255);

DQ = 1;

delay_5us1(30);

    if(DQ == 0)

{

delay_5us1(200);

DQ = 1;

match_rom(ds18b20_num1);

    WriteOneChar(0xbe);  //读

delay_18B20(100);

a=ReadOneChar();    //读取温度值低位

b=ReadOneChar();   //读取温度值高位

temp_value1=b<<4;

temp_value1+=(a&0xf0)>>4;

DQ = 0;

delay_5us1(255);

DQ = 1;

delay_5us1(240);

DQ = 1;

    }          

}

 

void get2_ds18b20(void)

{

unsigned char a=0;

unsigned char b=0,i;

match_rom(ds18b20_num2);

WriteOneChar(0x44);

   DQ = 1;

 

delay_1ms(255);

DQ = 0;

delay_5us1(255);

DQ = 1;

delay_5us1(30);

    if(DQ == 0)

{

delay_5us1(200);

DQ = 1;

match_rom(ds18b20_num2);

    WriteOneChar(0xbe);  //读

a=ReadOneChar();    //读取温度值低位

b=ReadOneChar();   //读取温度值高位

temp_value=b<<4;

temp_value+=(a&0xf0)>>4;

DQ = 0;

delay_5us1(255);

DQ = 1;

delay_5us1(240);

DQ = 1;

    }          

}

 

 

void ReadTemp(void)

{

 

get1_ds18b20();

get2_ds18b20();

}

 

void temp_to_str()   //温度数据转换成液晶字符显示

{

  TempBuffer[0]=temp_value/10+'0';  //十位

  TempBuffer[1]=temp_value%10+'0';  //个位;

  TempBuffer[2]=',';

  TempBuffer[3]=temp_value1/10+'0';  //十位

  TempBuffer[4]=temp_value1%10+'0';  //个位

  TempBuffer[5]=0xdf;   //温度符号

  TempBuffer[6]='C';

  TempBuffer[7]='\0';

}

void Delay1ms(unsigned int count)

{

unsigned int i,j;

for(i=0;i<count;i++)

for(j=0;j<120;j++);

}

 

void mdelay(uint delay)

{uint i;

 for(;delay>0;delay--)

   {for(i=0;i<62;i++) //1ms延时.

       {;}

   }

}

电路连线图:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 微处理器 io
相关文章推荐