精华内容
下载资源
问答
  • 基于51单片机DS18B20温度传感器设计的温控系统,温度显示在六位数码管上,资源包含C语言源代码,可烧写的Hex文件,Proteus仿真图。
  • (1)DS18B20采用单线接口,它与微处理器连接时仅仅需要一条口线即可实现微处理器与DS18B20的双向通信。 (2)引脚 引脚 定义 GND 接地 VDD 电源正极 DQ 信号的输入输出 从此我们可以看出DS18B20和...

    一. 简单描述

    (1)DS18B20采用单线接口,它与微处理器连接时仅仅需要一条口线即可实现微处理器与DS18B20的双向通信。
    (2)引脚

    引脚 定义
    GND 接地
    VDD 电源正极
    DQ 信号的输入输出

    从此我们可以看出DS18B20和单片机的连接十分的简单,单片机用一个IO口即可以控制DS18B20
    (3)这个小测温程序中需要用到的指令

    CCH-跳过ROM。忽略64位ROM(每一个DS18B20都有一个独特的64位光刻ROM,此程序中我们仅仅使用一个DS18B20),直接向18B20**发送温度转换命令**
    44H-启动温度转换。
    0xBE-读取温度数据
    

    二. 测温程序编写

    我们在编写程序的时候一定要有模块化的思想,把复杂问题简单化,而且这种方法编写出的代码“可移植行比较强”,遇到类似项目时我们只需重新组装,稍加修改即可。
    (1.)初始化

    	uchar i;
    	DSPORT = 0;			 //对应DQ管脚,我这个单片机对应P3^7.
    	i = 70;	
    	while(i--);//延时642us
    	DSPORT = 1;			//然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低,s说明初始化成功
    	i = 0;
    	while(DSPORT)	//等待DS18B20拉低总线
    	{
    		Delay1ms(1);
    		i++;
    		if(i>5)//等待>5MS
    		{
    			return 0;//初始化失败
    		}
    	
    	}
    	return 1;//初始化成功
    

    (2)写入数据

    
    {
    	uint i, j;
    
    	for(j=0; j<8; j++)
    	{
    		DSPORT = 0;	     	  //每写入一位数据之前先把总线拉低1us
    		i++;//给一点时间缓冲
    		DSPORT = dat & 0x01;  //然后写入一个数据,从最低位开始
    		i=6;
    		while(i--); //延时68us,持续时间最少60us
    		DSPORT = 1;	//然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
    		dat >>= 1;
    	}
    }
    

    (3.)读入一个字节数据

    	uchar byte, bi;
    	uint i, j;	
    	for(j=8; j>0; j--)
    	{
    		DSPORT = 0;//先将总线拉低1us
    		i++;
    		DSPORT = 1;//然后释放总线
    		i++;
    		i++;//延时6us等待数据稳定
    		bi = DSPORT;	 //读取数据,从最低位开始读取
    		/*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
    		byte = (byte >> 1) | (bi << 7);						  
    		i = 4;		//读取完之后等待48us再接着读取下一个数
    		while(i--);
    	}				
    	return byte;
    

    (4.)启动温度转换

    初始化函数
    写入函数(0xcc//跳过ROM指令
    写入(0x44//启动温度转换命令
    

    (5)读取温度数据

    初始化函数
    写入(0xcc//跳过ROm指令
    写入(0xbe//读取温度数据指令
    

    (6.)得到16位温度数据

    	int temp = 0;
    	uchar tmh, tml;
    	Ds18b20ChangTemp();			 	//先写入转换命令
    	Ds18b20ReadTempCom();			//然后等待转换完后发送读取温度命令
    	tml = Ds18b20ReadByte();		//读取温度值共16位,先读低字节
    	tmh = Ds18b20ReadByte();		//再读高字节
    	temp = tmh;
    	temp <<= 8;
    	temp |= tml;
    	return temp;
    

    好了到此我们已经使用使用DS18B20得到了温度的数据,此时温度以16位的形式存放在RAM中,要利用这组数据得到正确的温度我们需要:
    如果这个数据是负数,这个数据需要取反加一
    0.0625
    若为整数,我们仅仅需要0.0625即可得到所要的温度
    之后我们仅仅需要利用数码管显示数据即可

    8位动态数码管显示函数

    void DigDisplay()
    {
    	u8 i;
    	for(i=0;i<6;i++)
    	{
    		switch(i)	 //位选,选择点亮的数码管,
    		{
    			case(0):
    				LSA=1;LSB=1;LSC=1; break;//显示第0位
    			case(1):
    				LSA=0;LSB=1;LSC=1; break;//显示第1位
    			case(2):
    				LSA=1;LSB=0;LSC=1; break;//显示第2位
    			case(3):
    				LSA=0;LSB=0;LSC=1; break;//显示第3位
    			case(4):
    				LSA=1;LSB=1;LSC=0; break;//显示第4位
    			case(5):
    				LSA=0;LSB=1;LSC=0; break;//显示第5位
    		}
    		P0=DisplayData[i];//发送数据
    		delay(100); //间隔一段时间扫描	
    		P0=0x00;//消隐
    	}		
    }
    

    ok 到此简单的测温程序已经写出了。

    展开全文
  • 项目实现功能: 使用51开发板上的DS18B20温度传感器设计温控系统,要求如下: 用串口将采集到的温度数据实时发送至上位机,在上位机软件上显示当前温度值(关于上位机软件的编写请参考下篇VB 内容)。 关于温度...

    项目实现功能: 使用51开发板上的DS18B20温度传感器设计温控系统,要求如下:


           用串口将采集到的温度数据实时发送至上位机,在上位机软件上显示当前温度值(关于上位机软件的编写请参考下篇VB 内容)。
           关于温度变化的实现,大家可参考以下方法: 室温通常在 28°C左右,用手捏住温度传感器可使其温度上升,用温度低的物体接触温度传感器可使其温度降低,或在温度传感器上淋点水,然后对着温度传感器吹气可以使温度迅速下降,大家也可想其他办法使温度传感器周围温度在 25°C~32°C 变化。
     

    一、温度传感器概述:

           温度传感器是各种传感器中最常用的一种,早期使用的是模拟温度传感器,如热敏电阻,随着环境温度的变化,它的阻值也发生线性变化,用处理器采集电阻两端的电压,然后根据某个公式就可计算出当前环境温度。随着科技的进步,现代的温度传感器已经走向数字化, 外形小,接口简单,广泛应用在生产实践的各个领域,为我们的生活提供便利。随着现代仪器的发展,微型化、集成化、数字化正成为传感器发展的一个重要方向。美国DALLAS半导体公司推出的数字化温度传感器DS18B20采用单总线协议,即与单片机接口仅需占用一个I/O端口,无须任何外部元件,直接将环境温度转化成数字信号,以数字码方式串行输出,从而 大大简化了传感器与微处理器的接口。

    二、DS18B20 温度传感器介绍:

          DS18B20是美国DALLAS半导体公司推出的第一片支持“一线总线”接口的温度传感器,它具有微型化、低功耗、高性能、抗干扰能力强、易配微处理器等优点,可直接将温度转化成串行数字信号供处理器处理。

    1、D818B20温度传感器特性:
    ① 适应电压范围宽,电压范围在3.0~5.5V,在寄生电源方式下可由数据线供电。
    ② 独特的单线接口方式,它与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通信。
    ③ 支持多点组网功能,多个DS18B20可以并联在唯一的三线上,实现组网多点测温。
    ④ 在使用中不需要任何外围元件,全部传感元件及转换电路集成在形如一只三极管的集成电路内。
    ⑤ 测温范围-55°C~+I25°C,在-10°C~+85°C时精度为的0.5°C。
    ⑥ 可编程分辨率为9~12位,对应的可分辨温度分别为0.5°C,0.25°C,0.125°C和0.0625°C,    可实现高精度测温。
    ⑦ 在9位分辨率时,最多在93.75ms内把温度转换为数字;12位分辨率时,最多在750ms内把温度值转换为数字,显然速度更快。
    ⑧ 测量结果直接输出数字温度信号,以“一线总线”串行传送给CPU,同时可传送CRC校验码,具有极强的抗干扰纠错能力。
    ⑨ 负压特性。电源极性接反时,芯片不会因发热而烧毁,但不能正常工作。
    2、应用范围:
    ① 冷冻库、粮仓、储罐、电信机房、电力机房、电缆线槽等测温和控制领域。
    ② 轴瓦、缸体、纺机、空调等狭小空间工业设备测温和控制。
    ③ 汽车空调、冰箱、冷柜以及中低温干燥箱等。
    ④ 供热、制冷管道热量计量、中央空调分户热能计量等。
    3、引脚介绍:

    DS18B20有两种封装:三脚T0-92直插式(用的最多、最普遍的封装)和八脚SOIC贴片式。

    4、硬件连接:
           我们首先来了解“单总线”的概念。目前常用的单片机与外设之间进行数据传输的串行总线主要有I2C,SPI和SCI总线。其中l2C总线以同步串行二线方式进行通信(一条时钟线,—条数据线),SPI总线则以同步串行三线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线),而SCI总线是以异步方式进行通信(一条数据输入线,一条数据输出线)。这些总线至少需要两条或两条以上的信号线,而DS18B20使用的单总线技术与上述总线不同,它采用单条信号线,既可传输时钟,又可传输数据,而且数据传输是双向的,因而这种单总线技术具有线路简单,硬件开销少,成本低廉,便于总线扩展和维护等优点。单总线适用于单主机系统,能够控制一个或多个从机设备。
           主机可以是微控制器,从机可以是单总线器件,它们之间的数据交换只通过一条信号线。当只有一个从机设备时,系统可按单节点系统操作;当有多个从机设备时,系统则按多节点系统操作。设备(主机或从机)通过一个漏极开路或三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其他设备使用总线。单总线通常要求外接一个约为5kQ的上拉电阻。芯片手册上的典型连接如下图所示。

           DS18B20和单片机的连接非常简单,单片机只需要一个I/O口就可以控制DS18B20。这个图的接法是单片机与一个DS18B20通信,如果要控制多个DS18B20进行温度采集,只要将所有DS18B20的I/O口全部连接到一起就可以了。在具体操作时,通过读取每个DS18B20内部芯片的序列号来识别。本章我们仅操作一个DS18B20进行温度采集。掌握了本章的知识,就能很容易扩展,设计出多点温度采集系统。
    5、工作原理:
          硬件电路连接好以后,单片机需要怎样工作才能将DS18B20中的温度数据读取出来呢?下面将给出详细分析。
    首先我们来看控制DS18B20的指令:
    ① 33H—读ROM。读DS18B20温度传感器ROM中的编码(即64位地址)。
    ② 55H—匹配ROM。发出此命令之后,接着发出64位ROM编码,访问单总线上与该编码相对应的DS18B20并使之做出响应,为下一步对该DS18B20的读/写做准备。
    ③ FOH—搜索ROM。用于确定挂接在同一总线上DS18B20的个数,识别64位ROM地址,为操作各器件做好准备。
    ④ CCH—跳过ROM。忽略64位ROM地址,直接向18B20发温度变换命令,适用于一个从机工作。
    ⑤ ECH—告警搜索命令。执行后只有温度超过设定值上限或下限的芯片才做出响应。以上这些指令涉及的存储器是64位光刻ROM,    下表列出了它的各位定义。

           64位光刻ROM中的序列号是出厂前被光刻好的,它可以看做该DS18B20的地址序列码。其各位排列顺序是:开始8位为产品类型标号,接下来48位是该DS18B20自身的序列号,最后8位是前面56位的CRC循环冗余校验码(CRC=X8+X5+X4+1)。光刻ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一条总线上挂接多个DS18B20的目的。
           下面介绍以上儿条指令的用法。当主机需要对众多在线DS18B20中的某一个进行操作时,首先应将主机逐个与DS18B20挂接,读出其序列号;然后再将所有的DS18B20挂接到总线上,单片机发出匹配ROM命令(55H),紧接着主机提供的64位序列(包括该DS18B20的48位序列号)之后的操作就是针对该DS18B20的。
           如果主机只对一个DS18B20进行操作,就不需要读取ROM编码以及匹配ROM编码了,只要用跳过ROM (CCH)命令,就可进行如下温度转换和读取操作。
    ① 44H—温度转换。启动DS18B20进行温度转换,12位转换时最长为750ms(9位为93.75ms)。结果存入内部9字节的RAM中。
    ② BEH—读暂存器。读内部RAM中9字节的温度数据。
    ③ 4EH—写暂存器。发出向内部RAM的第2,    3字节写上、下限温度数据命令,紧跟该命令之后,是传送两字节的数据。
    ④ 48H—复制暂存器。将RAM中第2,    3字节的内容复制到E2PROM中。
    ⑤88H—重调E2PROM。将E2PROM中内容恢复到RAM中的第3、4字节。
    ⑥84H—读供电方式。读DS18B20的供电模式。寄生供电时,DS18B20发送0;外接电源供电时,DS18B20发送1。
    以上这些指令涉及的存储器为高速暂存器RAM和可电擦除E2PROM,    见下表。

            高速暂存器RAM由9个字节的存储器组成。第0~1个字节是温度的显示位;第2和第3个字节是复制的TH和TL,同时第2和第3个字节的数字可以更新;第4个字节是配置寄存器,同时第4个字节的数字可以更新;第5,6,7三个字节是保留的。可电擦除E2PROM又包括温度触发器TH和TL,以及一个配置寄存器。这些大家了解就可以了。
           下表列出了温度数据在高速暂存器RAM的第0和第1个字节中的存储格式。

           DS18B20 在出厂时默认配置为12位,其中最高位为符号位,即温度值共11位,单片机在读取数据时,一次会读2字节共16位,读完后将低11位的二进制数转化为十进制数后再乘以0.0625便为所测的实际温度值。另外,还需要判断温度的正负。前5个数字为符号位,这5位同时变化,我们只需要判断11位就可以了。前5位为1时,读取的温度为负值,且测到的数值需要取反加1再乘以0.0625才可得到实际温度值。前5位为0时,读取的温度为正值,且温度为正值时,只要将测得的数值乘以0.0625即可得到实际温度值。
    6、工作时序图:
    下图为时序图中各总线状态。

    (1)初始化时序图见下图)

    ① 先将数据线置高电平1。
    ② 延时(该时间要求不是很严格,但是要尽可能短一点)。
    ③ 数据线拉到低电平0。
    ④ 延时750µs(该时间范围可以在480~960µs)。
    ⑤ 数据线拉到高电平1。
    ⑥ 延时等待。如果初始化成功则在15~60ms内产生一个由DS18B20返回的低电平0,据该状态可以确定它的存在。但是应注意,不能无限地等待,不然会使程序进入死循环,所以要进行超时判断。
    ⑦ 若CPU读到数据线上的低电平0后,还要进行延时,其延时的时间从发出高电平算起(第@步的时间算起)最少要480µs。
    雹)将数据线再次拉到高电平1后结束。


    (2) DS18B20写数据(时序图见下图)

    ① 数据线先置低电平 0。
    ② 延时确定的时间为 15µs。
    ③ 按从低位到高位的顺序发送数据(一次只发送一位)。
    ④ 延时时间为 45µs。
    ⑤ 将数据线拉到高电平 1 。
    ⑥ 重复①~⑤步骤,直到发送完整个字节。
    ⑦ 最后将数据线拉高到 1。

    (3) DS18B20 读数据(时序图见下图)

    ① 将数据线拉高到1。
    ② 延时2µs。
    ③ 将数据线拉低到0。
    ④ 延时6µs。
    ⑤ 将数据线拉高到1。
    ⑥ 延时4µs。
    ⑦ 读数据线的状态得到一个状态位,并进行数据处理。
    ⑧ 延时30µs。
    ⑨ 重复①~⑦步骤,直到读取完一个字节。

     

    #include <reg52.h>		//编译器自带的库用 < >	 编译器包含C52的定义
    #include "DS18B20_Sum.h"
    #include "delay_Sum.h"
    
    
    #define uchar unsigned char
    #define uint  unsigned int
    
    sbit DS18B20=P2^2;   //温度传感器信号线
    
    uint temp;
    float f_temp;
    long int temperature=0;
    DS18B20初始化
    /*
    ① 先将数据线置高电平 1。
    ② 延时(该时间要求不是很严格,但是要尽可能短一点)。
    ③ 数据线拉到低电平 0。
    ④ 延时750μs(该时间范围可以在480~960μs) 。
    ⑤ 数据线拉到高电平 l 。
    ⑥ 延时等待。如果初始化成功则在15~60ms内产生一个由DS18B20返回的低电平0, 据该状态可以确定它的存在。
       但是应注意,不能无限地等待,不然会使程序进入死循环,所以要进行超时判断。
    ⑦ 若 CPU 读到数据线上的低电平 0 后,还要进行延时, 其延时的时间从发出高电平算起(第⑤步的时间算起)最少要 480μs。
    ⑧ 将数据线再次拉到高电平 l 后结束。
    */
    uchar reset_ds18b20()
    {
       uchar presence;
       DS18B20=0;
       delay_ds18b20(29);     //延时480 - 960  us
       DS18B20=1;						  
       delay_ds18b20(3);      //延时 15 - 60   us
       presence=DS18B20;
       delay_ds18b20(25);	  //延时 60 - 240  us
       return(presence);
    }
    DS18B20读取一字节数据
    /*
    ① 将数据线拉高到 1。
    ② 延时2μs。
    ③ 将数据线拉低到 0。
    ④ 延时6μs。
    ⑤ 将数据线拉高到 1。
    ⑥ 延时4μs。
    ⑦ 读数据线的状态得到一个状态位,并进行数据处理。
    ⑧ 延时30μs。 
    ⑨ 重复①~⑦步骤,直到读取完一个字节。
    */
     uchar read_bit_ds18b20()
    {
    	uchar i;
    	DS18B20=1;
    	delay_ds18b20(1);
    	DS18B20=0;
    	//delay(1);    //延时 15us  也可以不延时
    	DS18B20=1;
    	//delay(3);	 //延时 1 - 45  us
    	for(i=0;i<3;i++);
    	return(DS18B20);
    }
    DS18B20写一字节数据
    /*
    ① 数据线先置低电平 0。
    ② 延时确定的时间为 15μs。
    ③ 按从低位到高位的顺序发送数据(一次只发送一位)。
    ④ 延时时间为 45μs。
    ⑤ 将数据线拉到高电平 l 。
    ⑥ 重复①~⑤步骤,直到发送完整个字节。
    ⑦ 最后将数据线拉高到 1。
    */ 
    void write_bit_ds18b20(uchar dat)
    {
    	DS18B20=0;        //置0 无需延时
    	if(dat==1)
    	  DS18B20=1;
    	delay_ds18b20(1);	 //延时 60 - 120  us
    	DS18B20=1;
    	delay_ds18b20(1);	 //延时 1 - ∞  us
    }
    
    DS18B20写数据
    void write_byte_ds18b20(uchar dat)
    {
         uchar i,j;
    	 for(i=0;i<8;i++)
    	 {
    	     j=((dat>>i)&0x01);
    		 write_bit_ds18b20(j);
    		 //delay(1);      //延时 1 - ∞  us
    	 }
    }  
    
    DS18B20读数据
    uchar read_byte_ds18b20()
    {
    	uchar dat=0;
    	uchar i;
    	for(i=0;i<8;i++)
    	 {
    	   if(read_bit_ds18b20())
    	     dat|=0x01<<i;
    	   //delay(1);	   //延时 1 - ∞  us
    	 
    	 }
    	 return(dat); 	  
    }
    
    
     
    int read_temp_ds18b20()
    {
       uchar templ=0,temph=0;
       int temp=0;
       reset_ds18b20();//复位
       write_byte_ds18b20(0xcc); //跳过ROM
       write_byte_ds18b20(0x44); //跳过温度采集
       delay_ds18b20(10);     //750ms
       reset_ds18b20();
       write_byte_ds18b20(0xcc); //跳过ROM
       write_byte_ds18b20(0xbe); //准备好数据(开始温度转换采集)
       templ=read_byte_ds18b20();//读低字节温度值
       temph=read_byte_ds18b20();//读低字节温度值
       temp=(templ+(temph*256));
       return(temp);
    }
    
    float read_temp_ten_ds18b20()
    {
       float tt;
       uchar templ=0,temph=0;
       int temp=0;
       reset_ds18b20();//复位
       write_byte_ds18b20(0xcc); //跳过ROM
       write_byte_ds18b20(0x44); //跳过温度采集
       delay_ds18b20(10);     //750ms
       reset_ds18b20();
       write_byte_ds18b20(0xcc); //跳过ROM
       write_byte_ds18b20(0xbe); //准备好数据(开始温度转换采集)
       templ=read_byte_ds18b20();//读低字节温度值
       temph=read_byte_ds18b20();//读低字节温度值
       temp=temph;
       temp<<=8;             //two byte  compose a int variable
       temp=temp|templ;
       tt=temp*0.0625;
       temp=tt*10+0.5;
    
       return(temp);
    }
    
    uchar DS18B20_get(void)
    {
      temperature = read_temp_ds18b20(); //读ds18b20 温度值,赋给 temperature
      if((temperature&0xf800)>0)		 //判断温度数据 是否是负温度
    	 ;//Disp_1602(7,1,"-",1);  		  //在1602上显示一个 ” - “ 负温度标志
      else  ;//Disp_1602(7,1,"+",1);  	 //否则在1602上显示一个 ” + “ 负温度标志  
      temperature=read_temp_ds18b20();	   //读ds18b20 温度值,赋给 temperature
      delay(1);
      //write_oneline_DS18b20_1602(8,(temperature&0x07ff)*0.625); //把读出的温度 转换成实际温度 ,并显示在1602上
      return((temperature&0x07ff)*0.625); //十六进制
      //十进制   temperature=temperature*(0.0625*10);
      }
    #include <reg52.h>		//编译器自带的库用 < >	 编译器包含C52的定义
    #include <stdio.h>
    #include "DS18B20_Sum.h"
    #include "delay_Sum.h"
    #include "printf_sum.h"
    
    #define uchar unsigned char
    #define uint  unsigned int
     float temperature2=0;
    
    void InitUART(uint choose)//使用定时器1作为串口波特率发生器
    {
        TH1 = 0xFD;	     //晶振11.0592mhz 波特率设为9600
        TL1 = TH1;
        TMOD |= 0x20;	 //定时器1方式2
        SCON = 0x50;	 //串口接收使能
        ES = 1;			 //串口中断使能
        TR1 = 1;		 //定时器1使能
        TI = 1;			 //发送中断标记位,必须设置
    
        EA = choose;	 //开总中断
    
    }	
    void main()
    {   
    	InitUART(0);
    
    	while(1)																	 
    	{
    
    	temperature2= read_temp_ten_ds18b20();	 
    	printf("%.1f",temperature2/10);
    	delay(200);
    	
    	}
    }

     

     

    展开全文
  • 基于FPGA的ds18b20温度传感器设计程序

    千次阅读 2014-11-20 19:23:14
    DS18B20是一个1-wire总线,12bit的数字温度传感器,其详细的参数这里不做具体的介绍,只讨论其基于Verilog的控制程序的设计。 实际上,对DS18B20的控制,主要是实现1-wire总线的初始化,读,写等操作,然后再...

    基于FPGA的DS18B20控制程序设计及其Verilog实现 ()

     

    (2012-05-20 22:20:41)

    转载

    标签: 

    杂谈

    一,总体介绍

    DS18B20是一个1-wire总线,12bit的数字温度传感器,其详细的参数这里不做具体的介绍,只讨论其基于Verilog的控制程序的设计。

    实际上,对DS18B20的控制,主要是实现1-wire总线的初始化,读,写等操作,然后再根据DS18B20的控制要求,实现对其控制的verilog逻辑。

    在1-Wire总线上,有一个master,可以有1个或者多个slave。而对于FPGA+DS18B20的温度测试设计来讲,需要在FPGA上实现一个1-Wire总线的master。DS18B20作为1-wire总线的slave设备存在,可以有一个或者多个,不过为了简化程序,例程里假定只存在一个DS18B2020。

    1-Wire总线的操作形式上相对简单,但操作本身相对却又比较复杂。用Verilog做控制程序设计时,可以采用多层次嵌套的状态机来实现。

    二,FPGA +DS18B20的硬件设计

    硬件的设计非常简单,只需要将DS18B20的DQ与FPGA的一个IO连接,并加4.7K左右的上拉电阻就可以了。VDD和VPU可以为3.0~5.0V。这里我们参照FPGA本身的IO电压,选择3.3V。

    另外要注意的一点是,由于DQ的数据是双向的,所以FPGA的该IO要设定为inout类型。


    三,1-Wire总线的基本操作及Verilog实现。

    根据1-Wire总线的特点,可以把1-Wire总线的操作归结为初始化,单bit读操作,单bit写操作等最基础的几种。下面分别是几种基本操作的介绍和verilog实现。由于DS18B20的时序操作的最小单位基本上是1us,所以在该设计中,全部采用1MHz的时钟。

    1. 初始化

    初始化实际上就是1-wire总线上的Reset操作。由master发出一定长度的初始化信号。Slave看到该初始化信号后,在一定时间内发出规定长度的响应信号,然后初始化操作就结束了。

    我们用一个简单的状态机来实现对DS18B20初始化的操作。根据初始化的时序要求,设计一个有3个状态的简单的状态机,这三个状态分别是RST_IDLE,RST_MINIT和RST_SINIT。系统初始化时,处于RST_IDLE状态,当RST_EN信号有效时,进入RST_MINIT状态,由master发出初始化信号。当master的初始化信号发出一定时间以后,直接进入RST_SINIT状态。在RST_SINIT状态时,master去观察slave是否输出了正确的状态:如果slave没有输出正确的状态,则状态机重新回到RST_MINIT状态,由master重新发出初始化信号;如果slave输出了正确的状态,则意味着初始化正确完成,状态机回到RST_IDLE状态,整个初始化过程完成(这个文章里涉及到比较多的状态机,但状态机的转换都很简单,所以不会给出状态机的状态转换图,仅仅会用文字做简单叙述,有疑问的地方,可以仔细阅读相关代码)。

    wire RST_EN;

    wire RST_OVER;

    parameterRST_IDLE = 3'b001, //IDLE 状态

    RST_MINIT =3'b010, //master 初始化操作

    RST_SINIT =3'b100; //slave 初始化应答

    reg [2:0]RSTSM, RSTSMNXT;

    wirePHASE_RST_IDLE = RSTSM[0];

    wirePHASE_RST_MINIT = RSTSM[1];

    wirePHASE_RST_SINIT = RSTSM[2];

    wirePHASENXT_RST_IDLE = RSTSMNXT[0];

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RSTSM <=RST_IDLE;

    else

    RSTSM <=RSTSMNXT;

    end

    reg [9:0]MASTER_CNT; //用来控制master发出初始化信号的长度

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    MASTER_CNT<= 10'b0;

    elseif(~PHASE_RST_MINIT)

    MASTER_CNT<= 10'b0;

    else

    MASTER_CNT<= MASTER_CNT + 10'b1;

    end

    reg [9:0]SLAVE_CNT; //用来判断slave是否在恰当时间返回初始化结束的信号

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    SLAVE_CNT<= 10'b0;

    elseif(~PHASE_RST_SINIT)

    SLAVE_CNT<= 10'b0;

    else

    SLAVE_CNT<= SLAVE_CNT + 10'b1;

    end

    regSLAVE_IS_INIT; //采集并保存slave发出的初始化结束信号

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    SLAVE_IS_INIT<= 1'b1;

    elseif(SLAVE_CNT == 10'd70)

    SLAVE_IS_INIT<= DQ_IN;

    elseif(PHASE_RST_MINIT)

    SLAVE_IS_INIT<= 1'b1;

    else

    SLAVE_IS_INIT<= SLAVE_IS_INIT;

    end

    always @(RSTSMor RST_EN or MASTER_CNT or SLAVE_CNT or SLAVE_IS_INIT ) begin

    case(RSTSM)

    RST_IDLE:

    if(RST_EN)

    RSTSMNXT =RST_MINIT;

    else

    RSTSMNXT =RST_IDLE;

    RST_MINIT:

    if(MASTER_CNT== 10'd500)

    RSTSMNXT =RST_SINIT;

    else

    RSTSMNXT =RST_MINIT;

    RST_SINIT:

    if( (SLAVE_CNT== 10'd500) & ~SLAVE_IS_INIT)

    RSTSMNXT =RST_IDLE;

    elseif((SLAVE_CNT == 10'd500) & SLAVE_IS_INIT)

    RSTSMNXT =RST_MINIT;

    else

    RSTSMNXT =RST_SINIT;

    default:

    RSTSMNXT =RST_IDLE;

    endcase

    end

    assignRST_OVER = PHASE_RST_SINIT & PHASENXT_RST_IDLE; //初始化完成标志信号



    2. 单bit读操作

    在1-wire总线上,读数据的操作实际上是按bit来完成的。每次master可以从slave读回一个bit的数据。读回的数据可能是1或者0。


    需要注意的是,对于master来讲,无论读回来的数据是1还是0,其本身的操作及时序都是一样的,没有差异。

    仍然用一个简单的状态机来实现对DS18B20的单bit读操作。设计一个有5个状态的简单的状态机,这五个状态分别是RD_IDLE,RD_MPL,RD_MSAP,RD_WAIT和RD_OVER。系统初始化时,处于RD_IDLE状态,当RDBEGIN信号有效时,进入RD_MPL状态,由master发出读信号。3us以后,进入RD_MSAP状态(master在该状态结束的前一个us读取DQ上的值作为读bit的结果),在11us以后,进入RD_WAIT状态,而在读bit开始后的59us,系统进入RD_OVER状态,意味着读bit操作结束。RD_OVER状态是为了符合1-Wire总线的操作规范(在每个操作之间至少有1us的总线空闲时间)而存在的。

    wire RDBEGIN ;

    parameterRD_IDLE = 5'b00001, //resister pullup, larger than 1us

    RD_MPL =5'b00010, //master pull low, larger than 1us

    RD_MSAP =5'b00100, //ds18b20 pull low(read 0) or resister pullup(read 1), master sampledata, near 15us

    RD_WAIT =5'b01000, //ds18b20 pull low(read 0) or resister pullup(read 1)

    RD_OVER =5'b10000; //resister pullup, larger than 1us

    reg [4:0]RDSM, RDSMNXT;

    wirePHASE_RD_IDLE = RDSM[0];

    wirePHASE_RD_MPL = RDSM[1];

    wirePHASE_RD_MSAP = RDSM[2];

    wirePHASE_RD_OVER = RDSM[4];

    reg [5:0]RD_CNT;

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RD_CNT <=6'b0;

    elseif(~PHASE_RD_IDLE)

    RD_CNT <=RD_CNT + 6'b1;

    else

    RD_CNT <=6'b0;

    end

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RDSM <=RD_IDLE;

    else

    RDSM <=RDSMNXT;

    end

    always @(RDSMor RDBEGIN or RD_CNT) begin

    case(RDSM)

    RD_IDLE:

    if(RDBEGIN)

    RDSMNXT =RD_MPL;

    else

    RDSMNXT =RD_IDLE;

    RD_MPL:

    if(RD_CNT ==6'd3)

    RDSMNXT =RD_MSAP;

    else

    RDSMNXT =RD_MPL;

    RD_MSAP:

    if(RD_CNT ==6'd14)

    RDSMNXT =RD_WAIT;

    else

    RDSMNXT =RD_MSAP;

    RD_WAIT:

    if(RD_CNT ==6'd59)

    RDSMNXT =RD_OVER;

    else

    RDSMNXT =RD_WAIT;

    RD_OVER:

    if(RD_CNT ==6'd61)

    RDSMNXT =RD_IDLE;

    else

    RDSMNXT =RD_OVER;

    default:

    RDSMNXT =RD_IDLE;

    endcase

    end

    regRD_BIT_DATA; //读bit操作获得的数据

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RD_BIT_DATA<= 1'b0;

    else if(PHASE_RD_MSAP & (RD_CNT == 6'd13) )

    RD_BIT_DATA<= DQ;

    elseif(PHASE_RD_IDLE)

    RD_BIT_DATA<= 1'b0;

    else

    RD_BIT_DATA<= RD_BIT_DATA;

    end

    3. 单bit写操作

    在1-Wire总线上,写数据的操作也是按bit来完成的。每次master可以向slave写入一个bit的数据。写数据可能是1或者0。

    需要注意的是,对于master来讲,写数据不同的时候(1或者0),其本身的操作及时序是有差别的。

    对DS18B20的单bit写操作可以用一个有4个状态的简单的状态机来实现。这三个状态分别是WD_IDLE,WD_MPL,WD_OUT,和RD_OVER。系统初始化时,处于WD_IDLE状态,当WDBEGIN信号有效时,进入WD_MPL状态,由master发出写信号。9us以后,进入WD_MOUT状态(master将要写到slave的数据放到DQ上),而在写bit开始后的59us,系统进入RD_OVER状态,意味着写bit操作结束。WD_OVER状态是为了符合1-Wire总线的操作规范(在每个操作之间至少有1us的总线空闲时间)而存在的。

    wire WDBEGIN;//单bit写操作开始信号

    wireWD_DATA_OUT; //要写入到slave的值

    parameterWD_IDLE = 4'b0001, //resister pullup, no time request

    WD_MPL =4'b0010, //master pull low, larger than 1us, use 10us.

    WD_MOUT =4'b0100, //master pull low(write 0) or resister pull up(write 1), use 50us.

    WD_OVER =4'b1000; //resister pullup, larger than 1us

    reg [3:0]WDSM, WDSMNXT;

    wirePHASE_WD_IDLE = WDSM[0];

    wirePHASE_WD_MPL = WDSM[1];

    wirePHASE_WD_MOUT = WDSM[2];

    wirePHASE_WD_OVER = WDSM[3];

    wirePHASENXT_WD_MOUT = WDSMNXT[2];

    reg [5:0]WD_CNT;

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    WD_CNT <=6'b0;

    elseif(~PHASE_WD_IDLE)

    WD_CNT <=WD_CNT + 6'b1;

    else

    WD_CNT <=6'b0;

    end

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    WDSM <=WD_IDLE;

    else

    WDSM <=WDSMNXT;

    end

    always @(WDSMor WDBEGIN or WD_CNT) begin

    case(WDSM)

    WD_IDLE:

    if(WDBEGIN)

    WDSMNXT =WD_MPL;

    else

    WDSMNXT =WD_IDLE;

    WD_MPL:

    if(WD_CNT ==6'd9)

    WDSMNXT =WD_MOUT;

    else

    WDSMNXT =WD_MPL;

    WD_MOUT:

    if(WD_CNT ==6'd59)

    WDSMNXT =WD_OVER;

    else

    WDSMNXT =WD_MOUT;

    WD_OVER:

    if(WD_CNT ==6'd61)

    WDSMNXT =WD_IDLE;

    else

    WDSMNXT =WD_OVER;

    default:

    WDSMNXT =WD_IDLE;

    endcase

    end

    always@(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    DQ_OUT <=1'b0;

    elseif(PHASENXT_WD_MOUT )

    begin

    DQ_OUT <=WD_DATA_OUT;

    elseif(PHASE_WD_IDLE)

    DQ_OUT <=1'b1;

    else

    DQ_OUT <=DQ_OUT;

    end

    三,1-Wire总线上按Byte读写的Verilog实现及DS18B20的Byte操作

    上面用简单状态机实现了1-Wire总线上单bit数据的读写操作。在此基础上,可以通过状态机嵌套的方法实现按Byte的读写操作。实现Byte读写控制的状态机,控制8bit数据的读写操作,而每一bit的读写操作则通过嵌套单bit数据读写的状态机来实现。

    1. 按Byte读操作

    从DS18B20的datasheet上我们可以看到,1-Wire总线在发送或者接收数据的时候,LSB的数据在前,MSB的数据在后。所以对于Byte数据的读操作,只要依次在总线上读取8bit数据,并按照bit0~bit7的顺序将其存储到一个8bits的寄存器里即可。

    通常情况下,设计状态机的时候,都会设计一个默认的初始状态(也可以称之为IDLE状态)。在这里每个bit数据的读操作可以设定为一个状态。这样,整个状态机就需要九个状态。在我们的设计中,九个状态分别是RBD_IDLE,RBD_BIT0 ~RBD_BIT7,其中 RBD_IDLE是IDLE状态,RBD_BIT0~RBD_BIT7分别是读取bit0~bit7数据的状态。

    状态机在系统复位或者操作完成以后,进入RBD_IDLE状态。当读Byte数据的使能信号(RBDBEGIN)有效时,状态机进入RBD_BIT0,并调用1-Wire总线单bit读操作的状态机实现单bit的读操作,读取bit0的数据。为了在状态机进入RBD_BIT0的时候,正确调用单bit读操作的状态机,就要在RBD_BIT0状态下,使能RDBEGIN信号(单bit读操作的使能信号,详见上文)。在读完bit0的数据后(单bit读操作状态机回到IDLE状态),状态机需要进入RBD_BIT1状态,读取bit1的数据。也就是说,状态机从RBD_BIT0进入到RBD_BIT1的条件是bit0读操作结束(即其状态机回到IDLE状态),这个条件可以用单bit读操作状态机的PHASE_RD_OVER生成。上述的操作就实现了两个状态机的嵌套,顶层状态机(Byte读操作状态机)的信号触发底层状态机(单bit读操作状态机)进入工作状态,当底层状态机完成工作进入IDLE状态时,触发顶层状态机进入下一个状态。

    从RBD_BIT0到RBD_BIT7的操作是相同的,依次读取8bit的数据。不过读完bit7的数据后,状态机要转回到RBD_IDLE状态。另外就是在单bit读的过程中,要根据Byte读状态机的状态,将读到的单bit数据写入对应的寄存器中。

    下面是verilog的实现:

    reg [7:0] RD_BYTE_DATA; //Byte读数据结果

    wire RBDBEGIN ; // Byte读使能信号

    reg PHASE_RD_OVER_Q;

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    PHASE_RD_OVER_Q <= 1'b0;

    else

    PHASE_RD_OVER_Q <= PHASE_RD_OVER;

    end

    wire RD_BIT_OVER = PHASE_RD_OVER &PHASE_RD_OVER_Q; //单bit读操作结束信号

    parameter RBD_IDLE = 9'b0_0000_0001,

    RBD_BIT0 = 9'b0_0000_0010,

    RBD_BIT1 = 9'b0_0000_0100,

    RBD_BIT2 = 9'b0_0000_1000,

    RBD_BIT3 = 9'b0_0001_0000,

    RBD_BIT4 = 9'b0_0010_0000,

    RBD_BIT5 = 9'b0_0100_0000,

    RBD_BIT6 = 9'b0_1000_0000,

    RBD_BIT7 = 9'b1_0000_0000;

    reg [8:0] RBDSM, RBDSMNXT;

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RBDSM <= RBD_IDLE;

    else

    RBDSM <= RBDSMNXT;

    end

    always @(RBDSM or RBDBEGIN or RD_BIT_OVER) begin

    case(RBDSM)

    RBD_IDLE:

    if(RBDBEGIN)

    RBDSMNXT = RBD_BIT0;

    else

    RBDSMNXT = RBD_IDLE;

    RBD_BIT0:

    if(RD_BIT_OVER)

    RBDSMNXT = RBD_BIT1;

    else

    RBDSMNXT = RBD_BIT0;

    RBD_BIT1:

    if(RD_BIT_OVER)

    RBDSMNXT = RBD_BIT2;

    else

    RBDSMNXT = RBD_BIT1;

    RBD_BIT2:

    if(RD_BIT_OVER)

    RBDSMNXT = RBD_BIT3;

    else

    RBDSMNXT = RBD_BIT2;

    RBD_BIT3:

    if(RD_BIT_OVER)

    RBDSMNXT = RBD_BIT4;

    else

    RBDSMNXT = RBD_BIT3;

    RBD_BIT4:

    if(RD_BIT_OVER)

    RBDSMNXT = RBD_BIT5;

    else

    RBDSMNXT = RBD_BIT4;

    RBD_BIT5:

    if(RD_BIT_OVER)

    RBDSMNXT = RBD_BIT6;

    else

    RBDSMNXT = RBD_BIT5;

    RBD_BIT6:

    if(RD_BIT_OVER)

    RBDSMNXT = RBD_BIT7;

    else

    RBDSMNXT = RBD_BIT6;

    RBD_BIT7:

    if(RD_BIT_OVER)

    RBDSMNXT = RBD_IDLE;

    else

    RBDSMNXT = RBD_BIT7;

    default:

    RBDSMNXT = RBD_IDLE;

    endcase

    end

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RD_BYTE_DATA <= 8'b0;

    else if(PHASE_RD_MSAP & (RD_CNT == 6'd13) )

    begin

    case(RBDSM)

    9'b0_0000_0010:

    RD_BYTE_DATA[0] <= DQ;

    9'b0_0000_0100:

    RD_BYTE_DATA[1] <= DQ;

    9'b0_0000_1000:

    RD_BYTE_DATA[2] <= DQ;

    9'b0_0001_0000:

    RD_BYTE_DATA[3] <= DQ;

    9'b0_0010_0000:

    RD_BYTE_DATA[4] <= DQ;

    9'b0_0100_0000:

    RD_BYTE_DATA[5] <= DQ;

    9'b0_1000_0000:

    RD_BYTE_DATA[6] <= DQ;

    9'b1_0000_0000:

    RD_BYTE_DATA[7] <= DQ;

    default:

    RD_BYTE_DATA <= RD_BYTE_DATA;

    endcase

    end

    else

    RD_BYTE_DATA <= RD_BYTE_DATA;

    end

    2. 按Byte写操作

    按Byte写操作的状态机与按Byte读操作的状态机的控制原理基本上是相同的。只不过在这里要嵌套的是单bit写操作的状态机。另外就是在单bit写操作的时候,要把对应bit的数据放到DQ总线上。

    限于篇幅,状态机的实现不再给出,可自行参照Byte读操作的状态机做修改。

    3. DS18B20的Byte操作

    DS18B20的控制主要包括初始化,ROM命令和功能命令等。初始化的控制方式前面已经讨论过了。这里主要讨论一下ROM命令和功能命令。实际上不管是ROM命令还是功能命令,都可以归结为一个字节的命令(Byte写操作)加上0或者多个的Byte读/写操作。所以我们完全可以用上面所述的Byte读写状态机来实现ROM和功能命令。

    对于只有一个单Byte写操作的命令,我们直接引用单Byte写操作的状态机就可以实现了。


    而对于如读ROM数据的操作,则是一个Byte的写命令操作加上连续多个Byte的读数据的操作。在实现的时候,我们完全可以依照Byte读写操作状态机嵌套单bit读写操作状态机的方法,设计一个多BYTE读写操作的状态机,嵌套单Byte读写状态机。

    下面是从DS18B20读出9Byte数据的状态机的verilog实现:

    reg [7:0] TMP_LSB;

    reg [7:0] TMP_MSB;

    reg [7:0] USERBYTE1;

    reg [7:0] USERBYTE2;

    reg [7:0] CFGREG;

    reg [7:0] RESERVED5;

    reg [7:0] RESERVED6;

    reg [7:0] RESERVED7;

    reg [7:0] REGCRC;

    wire RDSPAD_EN; //读DS18B20 rom数据使能信号

    wire BYTE_READ_OVER = RD_BIT_OVER &PHASE_RBD_BIT7; //Byte读操作结束信号。

    wire BYTE_WRITE_OVER = WD_BIT_OVER &PHASE_WBD_BIT7; //Byte 写操作结束信号。

    parameter RDSPAD_IDLE = 11'b000_0000_0001,

    RDSPAD_CMD = 11'b000_0000_0010, //读ROM命令

    RDSPAD_BYTE1 = 11'b000_0000_0100, // 读ROM Byte 1

    RDSPAD_BYTE2 = 11'b000_0000_1000,

    RDSPAD_BYTE3 = 11'b000_0001_0000,

    RDSPAD_BYTE4 = 11'b000_0010_0000,

    RDSPAD_BYTE5 = 11'b000_0100_0000,

    RDSPAD_BYTE6 = 11'b000_1000_0000,

    RDSPAD_BYTE7 = 11'b001_0000_0000,

    RDSPAD_BYTE8 = 11'b010_0000_0000,

    RDSPAD_BYTE9 = 11'b100_0000_0000;

    reg [10:0] RDSPADSM, RDSPADSMNXT;

    wire PHASE_RDSPAD_IDLE = RDSPADSM[0];

    wire PHASE_RDSPAD_CMD = RDSPADSM[1];

    wire PHASE_RDSPAD_BYTE1 = RDSPADSM[2];

    wire PHASE_RDSPAD_BYTE2 = RDSPADSM[3];

    wire PHASE_RDSPAD_BYTE3 = RDSPADSM[4];

    wire PHASE_RDSPAD_BYTE4 = RDSPADSM[5];

    wire PHASE_RDSPAD_BYTE5 = RDSPADSM[6];

    wire PHASE_RDSPAD_BYTE6 = RDSPADSM[7];

    wire PHASE_RDSPAD_BYTE7 = RDSPADSM[8];

    wire PHASE_RDSPAD_BYTE8 = RDSPADSM[9];

    wire PHASE_RDSPAD_BYTE9 = RDSPADSM[10];

    wire PHASENXT_RDSPAD_IDLE = RDSPADSMNXT[0];

    wire PHASENXT_RDSPAD_CMD = RDSPADSMNXT[1];

    wire PHASENXT_RDSPAD_BYTE1 = RDSPADSMNXT[2];

    wire PHASENXT_RDSPAD_BYTE2 = RDSPADSMNXT[3];

    wire PHASENXT_RDSPAD_BYTE3 = RDSPADSMNXT[4];

    wire PHASENXT_RDSPAD_BYTE4 = RDSPADSMNXT[5];

    wire PHASENXT_RDSPAD_BYTE5 = RDSPADSMNXT[6];

    wire PHASENXT_RDSPAD_BYTE6 = RDSPADSMNXT[7];

    wire PHASENXT_RDSPAD_BYTE7 = RDSPADSMNXT[8];

    wire PHASENXT_RDSPAD_BYTE8 = RDSPADSMNXT[9];

    wire PHASENXT_RDSPAD_BYTE9 = RDSPADSMNXT[10];

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RDSPADSM <= RDSPAD_IDLE;

    else

    RDSPADSM <= RDSPADSMNXT;

    end

    always @( RDSPADSM or RDSPAD_EN or BYTE_READ_OVER orBYTE_WRITE_OVER) begin

    case(RDSPADSM)

    RDSPAD_IDLE:

    if(RDSPAD_EN)

    RDSPADSMNXT = RDSPAD_CMD;

    else

    RDSPADSMNXT = RDSPAD_IDLE;

    RDSPAD_CMD:

    if(BYTE_WRITE_OVER)

    RDSPADSMNXT = RDSPAD_BYTE1;

    else

    RDSPADSMNXT = RDSPAD_CMD;

    RDSPAD_BYTE1:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_BYTE2;

    else

    RDSPADSMNXT = RDSPAD_BYTE1;

    RDSPAD_BYTE2:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_BYTE3;

    else

    RDSPADSMNXT = RDSPAD_BYTE2;

    RDSPAD_BYTE3:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_BYTE4;

    else

    RDSPADSMNXT = RDSPAD_BYTE3;

    RDSPAD_BYTE4:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_BYTE5;

    else

    RDSPADSMNXT = RDSPAD_BYTE4;

    RDSPAD_BYTE5:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_BYTE6;

    else

    RDSPADSMNXT = RDSPAD_BYTE5;

    RDSPAD_BYTE6:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_BYTE7;

    else

    RDSPADSMNXT = RDSPAD_BYTE6;

    RDSPAD_BYTE7:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_BYTE8;

    else

    RDSPADSMNXT = RDSPAD_BYTE7;

    RDSPAD_BYTE8:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_BYTE9;

    else

    RDSPADSMNXT = RDSPAD_BYTE8;

    RDSPAD_BYTE9:

    if(BYTE_READ_OVER)

    RDSPADSMNXT = RDSPAD_IDLE;

    else

    RDSPADSMNXT = RDSPAD_BYTE9;

    default:

    RDSPADSMNXT = RDSPAD_IDLE;

    endcase

    end

    //采集并保存TMP_LSB

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    TMP_LSB <= 8'b0;

    else if(PHASENXT_RDSPAD_BYTE2 & BYTE_READ_OVER)

    TMP_LSB <= RD_BYTE_DATA;

    else

    TMP_LSB <= TMP_LSB;

    end

    //采集并保存 TMP_MSB

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    TMP_MSB <= 8'b0;

    else if(PHASENXT_RDSPAD_BYTE3 & BYTE_READ_OVER)

    TMP_MSB <= RD_BYTE_DATA;

    else

    TMP_MSB <= TMP_MSB;

    end

    //采集并保存 USERBYTE1

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    USERBYTE1 <= 8'b0;

    else if(PHASENXT_RDSPAD_BYTE4 & BYTE_READ_OVER)

    USERBYTE1 <= RD_BYTE_DATA;

    else

    USERBYTE1 <= USERBYTE1;

    End

    //采集并保存USERBYTE2

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    USERBYTE2 <= 8'b0;

    else if(PHASENXT_RDSPAD_BYTE5 & BYTE_READ_OVER)

    USERBYTE2 <= RD_BYTE_DATA;

    else

    USERBYTE2 <= USERBYTE2;

    end

    //采集并保存 CFGREG

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    CFGREG <= 8'b0;

    else if(PHASENXT_RDSPAD_BYTE6 & BYTE_READ_OVER)

    CFGREG <= RD_BYTE_DATA;

    else

    CFGREG <= CFGREG;

    end

    //采集并保存 RESERVED5

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RESERVED5 <= 8'b0;

    else if(PHASENXT_RDSPAD_BYTE7 & BYTE_READ_OVER)

    RESERVED5 <= RD_BYTE_DATA;

    else

    RESERVED5 <= RESERVED5;

    end

    //采集并保存 RESERVED6

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RESERVED6 <= 8'b0;

    else if(PHASENXT_RDSPAD_BYTE8 & BYTE_READ_OVER)

    RESERVED6 <= RD_BYTE_DATA;

    else

    RESERVED6 <= RESERVED6;

    end

    //采集并保存 RESERVED7

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    RESERVED7 <= 8'b0;

    else if(PHASENXT_RDSPAD_BYTE9 & BYTE_READ_OVER)

    RESERVED7 <= RD_BYTE_DATA;

    else

    RESERVED7 <= RESERVED7;

    end

    //采集并保存 REGCRC

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    REGCRC <= 8'b0;

    else if(PHASENXT_RDSPAD_IDLE & BYTE_READ_OVER)

    REGCRC <= RD_BYTE_DATA;

    else

    REGCRC <= REGCRC;

    end

    下图是从示波器上抓出的单Byte读操作的DQ上的波形。


     

    三,DS18B20的温度测量控制

    在该系统中,1-Wire总线上只存在1个DS18B20,在控制DS18B20进行温度测量的时候,根据1-Wire总线的特性和DS18B20的控制要求,可以采用相对简单的控制流程。

    我们用状态机来实现这个控制流程。从控制流程图可以看到,系统从空闲状态到读出温度数据,总共有8个大的步骤。而其中2和6,3和7是相同的,用状态机实现的时候,可以用同一个状态表示2和6,用同一个状态表示3和7。这样整个状态机就只需要6个状态。下面表格是状态机所有状态的定义:

    状态名称

    含义

    TTST_IDLE

    系统空闲状态

    TTST_RST

    总线初始化

    TTST_SKPROM

    SKIP ROM 命令

    TTST_CONVERT

    温度转换命令

    TTST_WOVER

    等待温度转换结束

    TTST_GETDATA

    读取温度数据

    为了区分2和6以及3和7。用IS_CONVERT信号表示总线初始化和SKIPROM命令是用来执行CONVERT命令的。用~IS_CONVERT信号表示总线初始化和SKIPROM命令是用来读取温度数据的。在状态机进入TTST_IDLE状态时,令IS_CONVERT为1并保持,当状态机进入TTST_WOVER状态时,令IS_CONVERT为0并保持。

    用TEST_EN信号作为温度测量的开始信号,这个信号至少保持1个时钟周期(CLK1MHZ),如果TEST_EN的长度超过了一次测试所需要的总的时间,则在一次测试完成以后,就直接开始进行第二次测量。

    用RST_OVER信号作为总线初始化的结束和下一个状态的开始的标志信号。

    用WBD_OVER信号作为命令(SKIPROM,CONVERT)结束和下一个状态开始的标志信号。

    用RD_BIT_DATA & PHASE_RD_OVER表示等待温度转换结束和下一个状态开始的信号(详细的原因可参照DS18B20 datasheet的相关部分)。

    用BYTE_READ_OVER &PHASENXT_RDSPAD_IDLE表示读取温度数据结束的信号(该两个信号的与表示读DS18B20 9ByteROM数据状态机完成工作并进入IDLE状态)。


    下面是verilog的实现:

    reg IS_CONVERT;

    parameter TTST_IDLE = 6'b00_0001,

    TTST_RST = 6'b00_0010,

    TTST_SKPROM = 6'b00_0100,

    TTST_CONVERT = 6'b00_1000,

    TTST_WOVER = 6'b01_0000,

    TTST_GETDATA = 6'b10_0000;

    reg [5:0] TTSTSM, TTSTSMNXT;

    wire PHASE_TTST_IDLE = TTSTSM[0];

    wire PHASE_TTST_RST = TTSTSM[1];

    wire PHASE_TTST_SKPROM = TTSTSM[2];

    wire PHASE_TTST_CONVERT = TTSTSM[3];

    wire PHASE_TTST_WOVER = TTSTSM[4];

    wire PHASE_TTST_GETDATA = TTSTSM[5];

    wire PHASENXT_TTST_IDLE = TTSTSMNXT[0];

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    TTSTSM <= TTST_IDLE;

    else

    TTSTSM <= TTSTSMNXT;

    end

    always @(posedge CLK1MHZ or negedge RESET)

    begin

    if(~RESET)

    IS_CONVERT <= 1'b1;

    else if(PHASE_TTST_IDLE)

    IS_CONVERT <= 1'b1;

    else if(PHASE_TTST_WOVER)

    IS_CONVERT <= 1'b0;

    else

    IS_CONVERT <= IS_CONVERT;

    end

    always @(TTSTSM or TEST_EN or RST_OVER or IS_CONVERTor WBD_OVER or RD_BIT_DATA or PHASE_RD_OVER or PHASE_RD_OVER or BYTE_READ_OVERor PHASENXT_RDSPAD_IDLE ) begin

    case(TTSTSM)

    TTST_IDLE:

    if(TEST_EN)

    TTSTSMNXT = TTST_RST;

    else

    TTSTSMNXT = TTST_IDLE;

    TTST_RST:

    if(RST_OVER)

    TTSTSMNXT = TTST_SKPROM;

    else

    TTSTSMNXT = TTST_RST;

    TTST_SKPROM:

    if(WBD_OVER)

    if(IS_CONVERT)

    TTSTSMNXT = TTST_CONVERT;

    else

    TTSTSMNXT = TTST_GETDATA;

    else

    TTSTSMNXT = TTST_SKPROM;

    TTST_CONVERT:

    if(WBD_OVER)

    TTSTSMNXT = TTST_WOVER;

    else

    TTSTSMNXT = TTST_CONVERT;

    TTST_WOVER:

    if(RD_BIT_DATA & PHASE_RD_OVER)

    TTSTSMNXT = TTST_RST;

    else

    TTSTSMNXT = TTST_WOVER;

    TTST_GETDATA:

    if(BYTE_READ_OVER & PHASENXT_RDSPAD_IDLE )

    TTSTSMNXT = TTST_IDLE;

    else

    TTSTSMNXT = TTST_GETDATA;

    default:

    TTSTSMNXT = TTST_IDLE;

    endcase

    end

    assign WD_DATA_OUT = PHASE_TTST_SKPROM ? 8'hcc :(PHASE_TTST_CONVERT ? 8'h44 : ( PHASE_RDSPAD_CMD ? 8'hbe : 8'h00) );

    四,DQ总线的控制

    1-Wire总线只有一根信号线,是双向的,带上拉电阻的。在FPGA中做DQ的逻辑如下,至于这么做的原因,不再做叙述。当弄懂了1-Wire总线的规范和DS18B20的基本操作的话,很容易就理解了。

    wire DQ, DQ_OUT, DQ_IN, DQ_OUT_EN;

    assign DQ = DQ_OUT_EN ? DQ_OUT : 1'bz;

    assign DQ_IN = DQ;

    wire RD_DQ_OUT = ~PHASE_RD_MPL;

    wire RD_DQ_OUT_EN = PHASE_RD_MPL ;

    wire WD_DQ_OUT = 1’b0;

    wire WD_DQ_OUT_EN = PHASE_WD_MPL | (~WD_BIT_DATA& PHASE_WD_MOUT);

    wire DQ_RST_OUT = ~ PHASE_RST_MINIT;

    wire DQ_RST_EN = PHASE_RST_MINIT;

    assign DQ_OUT = RD_DQ_OUT & RD_DQ_OUT_EN |WD_DQ_OUT & WD_DQ_OUT_EN | DQ_RST_OUT & DQ_RST_EN;

    assign DQ_OUT_EN = RD_DQ_OUT_EN | WD_DQ_OUT_EN |DQ_RST_EN;

    展开全文
  • 英文版文献,介绍DS18B20温度传感器设计原理
  • DS18B20是单线接口数字温度传感器,具体特征如下图。 DS18B20包括寄生电源电路、64位ROM和单线接口电路、暂存器、EEPROM、8位CRC生成器和温度传感器等。寄生电源电路可以实现外部电源供电和单线寄生供电,64位ROM中...
  • 基于51单片机及DS18B20温度传感器的数字温度计设计,完整的数据代码和程序设计思路
  • DS18B20温度传感器实验,仿真实例,现成调用封装使用,可运行的仿真电路图和调好的程序,开箱即用。适用于教学案例、毕业设计、电子设计比赛、出书项目实例,实际设计、个人DIY参考。 已调试好,proteus直接可以运行...
  • 51单片机驱动DS18B20温度传感器测量温度1.DS18B20温度传感器介绍;2.51单片机驱动DS18B20测量温度 1.DS18B20温度传感器介绍; ①引脚定义 引脚 符号 说明 1 GND 地 2 DQ 单总线的数据输入/输出引脚(需接...

    51单片机驱动DS18B20温度传感器测量温度

    1.DS18B20温度传感器介绍;

    ①引脚定义

    引脚 符号 说明
    1 GND
    2 DQ 单总线的数据输入/输出引脚(需接5KΩ上拉电阻)
    3 V DD 电源正

    在这里插入图片描述
    ②测量范围从-55 至+125 增量值为 0.5 等效的华氏温度范围是-67 F至257 F
    增量值为 0.9 F;
    ③在设计电路中数据线需接5KΩ电阻;
    ④单线接口访问 DS1820 的协议:
    ·初始化
    ·ROM 操作命令
    ·存贮器操作命令
    ·处理/数据

    2.51单片机驱动DS18B20测量温度

    ds18b20.c 文件▼

    #include"ds18b20.h"
    /*******************************************************************************
    * 函 数 名         : Delay1ms 12Mhz
    * 函数功能		   : 延时函数
    * 输    入         : 无
    * 输    出         : 无
    *******************************************************************************/
    
    void Delay1ms(uint y)
    {
    	uint x;
    	for( ; y>0; y--)
    	{
    		for(x=110; x>0; x--);
    	}
    }
    /*******************************************************************************
    * 函 数 名         : Ds18b20Init
    * 函数功能		   : 初始化
    * 输    入         : 无
    * 输    出         : 初始化成功返回1,失败返回0
    *******************************************************************************/
    
    uchar Ds18b20Init()
    {
    	uchar i;
    	DSPORT = 0;			 //将总线拉低480us~960us
    	i = 70;	
    	while(i--);//延时642us
    	DSPORT = 1;			//然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
    	i = 0;
    	while(DSPORT)	//等待DS18B20拉低总线
    	{
    		Delay1ms(1);
    		i++;
    		if(i>5)//等待>5MS
    		{
    			return 0;//初始化失败
    		}
    	
    	}
    	return 1;//初始化成功
    }
    
    /*******************************************************************************
    * 函 数 名         : Ds18b20WriteByte
    * 函数功能		   : 向18B20写入一个字节
    * 输    入         : 无
    * 输    出         : 无
    *******************************************************************************/
    
    void Ds18b20WriteByte(uchar dat)
    {
    	uint i, j;
    
    	for(j=0; j<8; j++)
    	{
    		DSPORT = 0;	     	  //每写入一位数据之前先把总线拉低1us
    		i++;
    		DSPORT = dat & 0x01;  //然后写入一个数据,从最低位开始
    		i=6;
    		while(i--); //延时68us,持续时间最少60us
    		DSPORT = 1;	//然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
    		dat >>= 1;
    	}
    }
    /*******************************************************************************
    * 函 数 名         : Ds18b20ReadByte
    * 函数功能		   : 读取一个字节
    * 输    入         : 无
    * 输    出         : 无
    *******************************************************************************/
    
    
    uchar Ds18b20ReadByte()
    {
    	uchar byte, bi;
    	uint i, j;	
    	for(j=8; j>0; j--)
    	{
    		DSPORT = 0;//先将总线拉低1us
    		i++;
    		DSPORT = 1;//然后释放总线
    		i++;
    		i++;//延时6us等待数据稳定
    		bi = DSPORT;	 //读取数据,从最低位开始读取
    		/*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
    		byte = (byte >> 1) | (bi << 7);						  
    		i = 4;		//读取完之后等待48us再接着读取下一个数
    		while(i--);
    	}				
    	return byte;
    }
    /*******************************************************************************
    * 函 数 名         : Ds18b20ChangTemp
    * 函数功能		   : 让18b20开始转换温度
    * 输    入         : 无
    * 输    出         : 无
    *******************************************************************************/
    
    void  Ds18b20ChangTemp()
    {
    	Ds18b20Init();
    	Delay1ms(1);
    	Ds18b20WriteByte(0xcc);		//跳过ROM操作命令		 
    	Ds18b20WriteByte(0x44);	    //温度转换命令
    	//Delay1ms(100);	//等待转换成功,而如果你是一直刷着的话,就不用这个延时了
       
    }
    /*******************************************************************************
    * 函 数 名         : Ds18b20ReadTempCom
    * 函数功能		   : 发送读取温度命令
    * 输    入         : 无
    * 输    出         : 无
    *******************************************************************************/
    
    void  Ds18b20ReadTempCom()
    {	
    
    	Ds18b20Init();
    	Delay1ms(1);
    	Ds18b20WriteByte(0xcc);	 //跳过ROM操作命令
    	Ds18b20WriteByte(0xbe);	 //发送读取温度命令
    }
    /*******************************************************************************
    * 函 数 名         : Ds18b20ReadTemp
    * 函数功能		   : 读取温度
    * 输    入         : 无
    * 输    出         : 无
    *******************************************************************************/
    
    int Ds18b20ReadTemp()
    {
    	int temp = 0;
    	uchar tmh, tml;
    	Ds18b20ChangTemp();			 	//先写入转换命令
    	Ds18b20ReadTempCom();			//然后等待转换完后发送读取温度命令
    	tml = Ds18b20ReadByte();		//读取温度值共16位,先读低字节
    	tmh = Ds18b20ReadByte();		//再读高字节
    	temp = tmh;
    	temp <<= 8;
    	temp |= tml;
    	return temp;
    }
    

    ② ds18b20.h 文件▼

    #ifndef __DS18B20_H_
    #define __DS18B20_H_
    
    #include<reg52.h>
    //---重定义关键词---//
    #ifndef uchar
    #define uchar unsigned char
    #endif
    
    #ifndef uint 
    #define uint unsigned int
    #endif
    
    //--定义使用的IO口--//
    sbit DSPORT=P3^7;
    
    //--声明全局函数--//
    void Delay1ms(uint );
    uchar Ds18b20Init();
    void Ds18b20WriteByte(uchar com);
    uchar Ds18b20ReadByte();
    void  Ds18b20ChangTemp();
    void  Ds18b20ReadTempCom();
    int Ds18b20ReadTemp();
    
    #endif
    
    展开全文
  • 这是DS18B20数字温度传感器测温程序,参加各种电子设计比赛可能会用到。DS18B20 是美信公司的一款温度传感器,单片机可以通过 1-Wire 协议与 DS18B20 进行通信,最终将温度读出。1-Wire 总线的硬件接口很简单,只...
  • 设计采用的主控芯片是ATMEL公司的AT89S52单片机,数字温度传感器是DALLAS公司的DS18B20。本设计用数字传感器DS18B20测量温度,测量精
  • 该温度检测器,使用了10米线长的DS18b20温度传感器和51开发板,传感器由于使用长距离的接线,所以需增加上拉电阻。使用Lcd1602显示温度和所设置的上限报警值,可使用矩阵键盘设置上限报警的温度值,报警系统包括:LED...
  • 毕设 火灾智能报警 ds18b20 mq2 仿真 ;基于DS18B20温度传感器和MQ2烟雾传感器的火灾报警器设计
  • PIC和DS18B20温度传感器的接口设计
  • STM32CubeMX系列|DS18B20温度传感器

    千次阅读 2020-09-29 21:26:09
    DS18B20温度传感器 1. DS18B20简介 2. 硬件设计 3. 软件设计 3.1 STM32CubeMX设置 3.2 MDK-ARM编程 4. 下载验证
  • 设计了一种基于 DS18B20温度传感器的虚拟温度指示系统。该系统以数字温度传感器和单片机作为数据采集终端并以 RS232作为数据传输链路,利用以 LabVIEW为平台开发的温度指示系统对光学系统中照明光源的工作温度进行了...
  • ds18b20温度传感器的编程驱动,适用于单片机c语言程序设计
  • DS18B20温度传感器 ------ 自学笔记

    千次阅读 2020-05-14 18:17:01
    4.3、DS18B20温度传感器的存储器 4.4、配置寄存器 4.5、DS18B20的ROM指令表 4.6、DS18B20的RAM指令表 4.7、DS18B20初始化 4.8、DS18B20读时序 4.9、DS18B20写时序 4.10、DS18B20电路图 五、软件设计 5.1、...
  • 本人毕业论文,详细介绍如何用51单片机驱动1602LCD, DS1302时钟,DS18B20温度传感器,并最终完成火灾自动报警器的设计,不可多得的毕业论文参考资料。
  • ds18b20温度传感器

    2012-12-26 22:58:00
    本文以单片机AT89S52为核心的,从硬件和软件两个方面设计实现温度显示报警系统,报警...本系统采用DS18B20采集现场温度,用LED数码管显示温度值,并可根据需要设置控制温度的上限,系统具有超过设置上限自动报警的功能。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 994
精华内容 397
关键字:

ds18b20温度传感器设计