DS18B20单总线协议

单片机课设 

题目是DS18B20的通信,用CH452搭配数码管显示。DS18B20最主要的是单总线协议,这个我之前没有接触过。上来只好老老实实的看数据手册。

对于DS18B20来说,有下面的这些特点:

( 1 )采用单总线的接口方式 与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20 的双向通讯。单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量,使用方便等优点,使用户可轻松地组建传感器网络,为测量系统的构建引入全新概念。

( 2 )测量温度范围宽,测量精度高 DS18B20 的测量范围为 -55 ℃ ~+ 125 ℃ ; 在 -10~+ 85°C范围内,精度为 ± 0.5°C 。

( 3 )在使用中不需要任何外围元件。

( 4 )持多点组网功能 多个 DS18B20 可以并联在惟一的单线上,实现多点测温。

( 5 )供电方式灵活 DS18B20 可以通过内部寄生电路从数据线上获取电源。因此,当数据线上的时序满足一定的要求时,可以不接外部电源,从而使系统结构更趋简单,可靠性更高。

( 6 )测量参数可配置 DS18B20 的测量分辨率可通过程序设定 9~12 位。

( 7 ) 负压特性电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。

( 8 )掉电保护功能 DS18B20 内部含有 EEPROM ,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。

这里面需要注意的主要是内部的寄生电路,这个电路可以不接电源来实现工作。但是转换温度的时间会增加。在老师给我们的板子上接了电源。同时也给信号端口接了上拉电阻。所以也是在程序上简化了一些。

下面来一一介绍DS18B20的工作通信协议、DS18B20的配置以及CH452的显示部分。

DS18B20的工作通信协议:

DS18B20是单总线协议,结构上的简单一定会导致时序上的复杂。在时序上要求严格按照数据手册上面的要求来完成。对于每

一次的写命令来说都要按照下面的步骤来执行:

即 初始化命令——>Rom命令——>函数命令(主要是温度转换)之所以把这个写在前面,是因为这个非常的重要而且很容易被忽视。在自己写初步的程序的时候,一直没有正常的通讯。看了别人的程序才恍然大悟。主要是一开始这个步骤忽略了。介绍完了命令的步骤。下面开始说明初始化,写0 、写1和读0、读1这几步。

DS18B20单总线协议_第1张图片

(初始化时序)

DS18B20单总线协议_第2张图片

(读写1 0)

这里的话没有什么好说的,需要注意的主要是三点:(1)注意时序的要求,不同的单片机延时也不一样,我用的是STC15的和89的就不同。(2)注意释放总线   (3)可以利用初始化后面的应答信号确定是否响应,有时候会用到。

DS18B20的配置:

DS18B20内部有48位的唯一ID,这个ID在只有一个芯片的时候没有什么用。但是要是实现多个DS18B20挂载在一个总线的时候这个是不可缺少的。所以我们首先要读出来这个ID。ID可以通过CRC校验确定是否正确。


我读出的芯片ID是{0x28,0x79,0xA9,0xE6,0x05,0x00,0x00,0xA5}和{0x28,0xd8,0x60,0xc0,0x06,0x00,0x00,0x82}这样就通过不同的ID来确定不同新片了。

uchar CRC8() 
{ 
   uchar i,x; uchar crcbuff;
   
   crc=0;
   for(x = 0; x <8; x++)
   {
    crcbuff=fCode[x];
    for(i = 0; i < 8; i++) 
     { 
      if(((crc ^ crcbuff)&0x01)==0) 
      crc >>= 1; 
       else { 
              crc ^= 0x18;   //CRC=X8+X5+X4+1
              crc >>= 1; 
              crc |= 0x80; 
            }         
      crcbuff >>= 1;       
	 }
   }
     return crc;	
}
(CRC校验程序)

CH452的显示部分:

CH452是键盘扫描和显示芯片。我参照的是官方的例程,(ˇˍˇ) 向里面写数据就可自动的实现显示和键盘读写。下面贴出原理图DS18B20单总线协议_第3张图片

最后给出源程序,希望有借鉴的价值。

#include
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define FOSC 11059200L          //系统频率
#define BAUD 115200             //串口波特率
//命令定义
#define CH452_NOP		0x0000					// 空操作
#define CH452_RESET     0x0201					// 复位
#define CH452_LEVEL		0x0100					// 加载光柱值,需另加7位数据
#define CH452_CLR_BIT	0x0180					// 段位清0,需另加6位数据
#define CH452_SET_BIT	0x01C0					// 段位置1,需另加6位数据
#define CH452_SLEEP	   	0x0202					// 进入睡眠状态
#define CH452_LEFTMOV   0x0300		      // 设置移动方式-左移
#define CH452_LEFTCYC   0x0301		      // 设置移动方式-左循环
#define CH452_RIGHTMOV  0x0302		      // 设置移动方式-右移
#define CH452_RIGHTCYC  0x0303		      // 设置移动方式-右循环	
#define CH452_SELF_BCD	0x0380					// 自定义BCD码,需另加7位数据
#define CH452_SYSOFF    0x0400					// 关闭显示、关闭键盘
#define CH452_SYSON1    0x0401					// 开启显示
#define CH452_SYSON2    0x0403					// 开启显示、键盘
#define CH452_SYSON2W   0x0423					// 开启显示、键盘, 真正2线接口
#define CH452_NO_BCD    0x0500					// 设置默认显示方式,可另加3位扫描极限
#define CH452_BCD       0x0580					// 设置BCD译码方式,可另加3位扫描极限
#define CH452_TWINKLE   0x0600		      // 设置闪烁控制,需另加8位数据
#define CH452_GET_KEY	0x0700					// 获取按键,返回按键代码
#define CH452_DIG0      0x0800					// 数码管位0显示,需另加8位数据
#define CH452_DIG1      0x0900		      // 数码管位1显示,需另加8位数据
#define CH452_DIG2      0x0a00		      // 数码管位2显示,需另加8位数据
#define CH452_DIG3      0x0b00		      // 数码管位3显示,需另加8位数据
#define CH452_DIG4      0x0c00		      // 数码管位4显示,需另加8位数据
#define CH452_DIG5      0x0d00					// 数码管位5显示,需另加8位数据
#define CH452_DIG6      0x0e00					// 数码管位6显示,需另加8位数据
#define CH452_DIG7      0x0f00		      // 数码管位7显示,需另加8位数据
#define RADIX           0x02            //小数点
// 显示部分引脚定义
sbit   din = P2^6;		                // 串行数据输出,接CH451的数据输入
sbit   load=P3^3;                        //串行命令加载,上升延激活
sbit   dout=P2^7;                        //INT1,键盘中断和键值数据输入,接CH451的数据输出
sbit   dclk = P2^5;      
sbit   DS=P1^3;           //define interface
sbit   beep = P1^7;
sfr    AUXR  = 0x8e;               //辅助寄存器                    //串行数据时钟上升延激活
unsigned char code number[]={0xbd,0x18,0xd5,0xd9,0x78,0xe9,0xed,0x98,0xfd,0xf9};
uchar i,j,cmd1,keycode;
uint temp;            	 // variable of temperature 
uint temped;		 	// 源码
uchar flag1;            // sign of the result positive or negative
uchar flag_key;
uchar ch451_key;
uchar flag_wendu;
uint cmd;
bit busy;
bit presence;
unsigned char  crc;
uchar fCode[8];							//序列号数组
uchar DSrom1[8]={0x28,0x79,0xA9,0xE6,0x05,0x00,0x00,0xA5}; //板子上的				//2个器件每个64位序列号
uchar DSrom2[8]={0x28,0xd8,0x60,0xc0,0x06,0x00,0x00,0x82}; //ROM1					   //2个器件每个64位序列号
uint   f[2]; //温度数组
uint   e[2]; //原温度数组
void CH452_Write(unsigned short cmd);	// ch452写程序
void CH452_Read(void);					// ch452读程序
void Delay500us();						//延时
void Delay30us();						//延时
void Delay240us();					   	//延时
void Delay5us();						//延时
void Delay60us();						//延时
void Delay2us();						//延时
void Delay1000us();					    //延时
void Delay100ms();					    //延时
void Delay80us();
void Delay20us();
void Delay1ms();   
void delay_b20(uint n);					//单片机定时1us	  不准!!!!!!
void uartinit(void);					//串口初始化
void SendData(uint dat);				//发送数据
void SendString(char *s);				//发送字符串
void dsreset(void);						// Ds1820初始化
bit tmpreadbit(void);       			//read a bit
uchar tmpread(void);					//read a byte date
void tmpwritebyte(uchar dat);  			//write a byte to ds18b20
void tmpchange(void);  					//DS18B20 begin change
void display(uint temp);	        		//显示程序
void exti1init ();
void DispCode();						// 读序列号
void read_dealtemp0();					// 读多个温度
void read_dealtemp1();					//读取温度
uchar CRC8() ;	
void delay_b20(uint n)//STC12C5A单片机定时1us
{ 
	while(n--)
	{
	_nop_();
	}
}
void exti1init (){
	 INT0 = 1;
    IT0 = 1;                    //设置INT0的中断类型 (1:仅下降沿 0:上升沿和下降沿)
    EX0 = 1;                    //使能INT0中断
    EA = 1;
}
void display(uint temp)			//显示程序
{
    uchar A1,A2,A3,A4;
    A1 =  temp/1000;
    A2 = temp % 1000 / 100;
    A3 = temp % 100 / 10;
	A4 = temp % 10;

	 CH452_Write(CH452_DIG4 | number[ A1 ]);
	 CH452_Write(CH452_DIG5 | number[ A2]|RADIX);
	 CH452_Write(CH452_DIG6 | number[A3]);
	 CH452_Write(CH452_DIG7 | number[A4]);
}
void tmpchange(void)  //DS18B20 begin change
{
  dsreset();
  Delay240us();
  tmpwritebyte(0xcc);  // 跳过ROM 
  tmpwritebyte(0x44);  //  initiates a single temperature conversion
}

/*
响应多个温度 首先是复位 然后ROM 接着RAm最后计算
*/
void read_dealtemp0(){	  //读多个温度
	uchar i,j; 
	uchar a,b;
	float tt; 
	  j = 0;
	  dsreset();
	  Delay240us()	;
	  tmpwritebyte(0x55);	  //输入序列号
	  for(i=0;i<8;i++) { 
	  tmpwritebyte(DSrom1[i]);//发送64位序列号 
	  } 
	  tmpwritebyte(0xbe);
	  a=tmpread();
 	  b=tmpread();
	  temp=b;
	  temp<<=8;             //two byte  compose a int variable
	  temp=temp|a;
	  temped=temp;	   // 源码
	  tt=temp*0.0625;	   
  	  temp=tt*100+0.5;
	  f[j]=temp;
	  e[j]=temped;
	  Delay100ms();
  }
void read_dealtemp1(){	  //读多个温度
	uchar i,j; 
	uchar a,b;
	float tt; 
	  j = 1;
	  dsreset();
	  Delay240us()	;
	  tmpwritebyte(0x55);	  //输入序列号
	  for(i=0;i<8;i++) { 
	  tmpwritebyte(DSrom2[i]);//发送64位序列号 
	  } 
	  tmpwritebyte(0xbe);
	  a=tmpread();
 	  b=tmpread();
	  temp=b;
	  temp<<=8;             //two byte  compose a int variable
	  temp=temp|a;
	  temped=temp;	   // 源码
	  tt=temp*0.0625;	   
  	  temp=tt*100+0.5;
	  f[j]=temp;
	  e[j]=temped;
	  Delay100ms();
  }
uint tmp()               //get the temperature
{
  float tt;
  uchar a,b;
  dsreset();
	Delay240us()	;
  tmpwritebyte(0xcc);
  tmpwritebyte(0xbe);
  a=tmpread();
  b=tmpread();
  temp=b;
  temp<<=8;             //two byte  compose a int variable
  temp=temp|a;
	temped=temp;	   // 源码
  tt=temp*0.0625;	   
  temp=tt*100+0.5;
  return temp;		 // 转换的温度
}
void tmpwritebyte(uchar dat)   //write a byte to ds18b20
{
  uchar j;
  bit testb;
  for(j=1;j<=8;j++)
  {
    testb=dat&0x01;
    dat=dat>>1;
    if(testb)     //write 1
    {
      DS=0;
      Delay5us();
      DS=1;
      Delay60us();
    }
    else
    {
      DS=0;       //write 0
      Delay60us();
      DS=1;
      Delay5us();
    }

  }
}
void DispCode()//读取序列号 
{ 
		uchar i; 
		dsreset(); 
		tmpwritebyte(0x33); 
			for (i=0;i<8;i++) 
			{
			fCode[i]=tmpread(); 
			} 
}
uchar tmpread(void)   //read a byte date
{
  uchar i,j,dat;
  dat=0;
  for(i=1;i<=8;i++)
  {
    j=tmpreadbit();
    dat=(j<<7)|(dat>>1);   //读出的数据最低位在最前面,这样刚好一个字节在DAT里
  }
  return(dat);
}
bit tmpreadbit(void)       //read a bit
{
   bit dat;
   DS=0;//i++ for delay
   Delay2us();
	 DS=1;
	 Delay2us();
   dat=DS;
   Delay60us();
   return (dat);
}											 
void dsreset(void)       //send reset and initialization command
{
		DS = 1; //DQ复位 
	    Delay2us();//延时 
		DS = 0; //DQ拉低 
		Delay500us(); //精确延时大于480us 
		DS = 1; //拉高 
		Delay30us(); 
		
		presence = DS;
		Delay60us();

		DS = 1;	
}

void SendString(char *s)
{
    while (*s)                  //检测字符串结束标志
    {
        SendData(*s++);         //发送当前字符
    }
}
void SendData(uint dat)
{
    while (busy);               //等待前面的数据发送完成
    busy = 1;
    SBUF = dat;                 //写数据到UART数据寄存器
}
void uartinit(void){
	SCON = 0x50;                //8位可变波特率
    AUXR = 0x40;                //定时器1为1T模式
    TMOD = 0x20;                //定时器1为模式2(8位自动重载)
    TL1 = (256 - (FOSC/32/BAUD));   //设置波特率重装值
    TH1 = (256 - (FOSC/32/BAUD));
    TR1 = 1;                    //定时器1开始工作
    ES = 1;                     //使能串口中断
    EA = 1;
}
void Delay1ms()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	_nop_();
	i = 11;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
}

void Delay100ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	_nop_();
	i = 5;
	j = 52;
	k = 195;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay20us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	_nop_();
	i = 52;
	while (--i);
}
void Delay80us()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	i = 1;
	j = 217;
	do
	{
		while (--j);
	} while (--i);
}
void Delay1000us()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	_nop_();
	i = 11;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
}
void Delay2us()		//@11.0592MHz
{
	unsigned char i;

	i = 3;
	while (--i);
}
void Delay60us()		//@11.0592MHz
{
	unsigned char i, j;

	i = 1;
	j = 162;
	do
	{
		while (--j);
	} while (--i);
}
void Delay5us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 11;
	while (--i);
}
void Delay240us()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	i = 3;
	j = 145;
	do
	{
		while (--j);
	} while (--i);
}
void Delay500us()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	i = 6;
	j = 93;
	do
	{
		while (--j);
	} while (--i);
}
void Delay30us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 80;
	while (--i);
}
uchar CRC8() 
{ 
   uchar i,x; uchar crcbuff;
   
   crc=0;
   for(x = 0; x <8; x++)
   {
    crcbuff=fCode[x];
    for(i = 0; i < 8; i++) 
     { 
      if(((crc ^ crcbuff)&0x01)==0) 
      crc >>= 1; 
       else { 
              crc ^= 0x18;   //CRC=X8+X5+X4+1
              crc >>= 1; 
              crc |= 0x80; 
            }         
      crcbuff >>= 1;       
	 }
   }
     return crc;	
}
void CH452_Write(unsigned short cmd){
	load=0; //命令开始,LOAD=0
	for(i=0;i!=12;i++) //送入12位数据,低位在前
		{
	dclk=0;
	din=cmd&1;
	dclk=1; //上升沿有效
	cmd=cmd>>1;
	}
	load=1; //加载数据,LOAD上升沿
	}
void CH452_Read(void)
		{
		cmd1=0x07; //读按键的命令字
		load=0;
		for(i=0;i!=4;i++) // 只需要发出高4位,多发也可以,但应该确保最后留下的4位是该命令码
		{
		din=cmd1&1;
		dclk=0;
		cmd1>>=1; //往右移一位
		dclk=1; //产生时钟上升沿锁通知CH451输入位数据
		}
		load=1; //产生加载上升沿通知CH451处理命令数据
		for(j=0;j<100;j++){ }
		keycode=0; //清除keycode
		for(i=0;i!=7;i++)
		{
		keycode<<=1; //数据移入keycode,高位在前,低位在后
		if (dout)
		{ keycode++;} //从高到低读入451的数据
		// keycode|=CH452_DOUT;
		dclk=0; //产生时钟下升沿通知CH451输出下一位
		dclk=1;
		}
		}
//中断服务程序
void exint0() interrupt 0       //INT0中断入口
		{
				
		}
/*----------------------------
UART 中断服务程序
-----------------------------*/
void Uart() interrupt 4 using 1
		{
		    if (RI)
		    {
		        RI = 0;                 //清除RI位
		    }
		    if (TI)
		    {
		        TI = 0;                 //清除TI位
		        busy = 0;               //清忙标志
		    }
		}
void main(void){
		uchar path1;
		path1 = 0; 
		CH452_Write(CH452_RESET);
		CH452_Write(CH452_SYSON2);	//CH452初始化	
		flag_wendu=1;  				 // 显示温度标记
		//	presence =0;			// 响应信号	 0是OK
	    //	DispCode();				//读序列号 读的时候只能是单线的操作
		//	CRC8();				   //校验crc, 0 是ok
while (1){
			CH452_Read();
			if (keycode==0x44){
				path1 =0 ;
				beep=0;
				Delay100ms();
				beep=1;
			}
			if (keycode==0x45){
				path1 =1; 
				beep=0;
				Delay100ms();
				beep=1;
				}
			if (keycode==0x46){
				path1 =2; 
				beep=0;
				Delay100ms();
				beep=1;
				}
			
			if (keycode==0x47){
				path1 =3; 
				beep=0;
				Delay100ms();
				beep=1;
				}
				keycode=0;
		switch(path1)	
		{
			case 0: display(f[0]); break;
			case 1: display(e[0]); break;
			case 2: display(f[1]); break;
			case 3: display(e[1]); break;
		}

			tmpchange();//初始化 
			read_dealtemp0();//读取温度
			tmpchange();//初始化 
			read_dealtemp1();//读取温度
}
}

















你可能感兴趣的:(单片机)