基于野火 FPGA EP4CE10征途系列开发板的DS18B20数字温度传感器设计

1 DS18B20数字温度传感器 

1.1 简述

        DS18B20是美国DALLAS半导体公司生产的单总线数字温度传感器,其可直接将温度转化成数字信号输出。

        DS18B20数字温度传感器提供9-Bit至12-Bit(可配置)温度读数和一个用户可编程的非易失性且具有高温和低温触发报警的报警功能。DS18B20采用1-Wire通信即仅采用一根数据线(DQ)与微控制器进行通信。该传感器的温度检测范围为-55℃至+125℃,在范围-10℃至+85℃之间具有±0.5℃的精 度。此外读取、写入和执行温度转换的电源可以从数据线本身获得,而不需要外部电源,当然使用外部电源供电也行。

        DS18B20可以无需外部电源供电当数据线DQ为高时由数据线为设备供电。当总线拉高时给内部电容(Cpp)充电 ,当总线拉低时由该电容给设备供电这种由总线给设备供电的方式称为“寄生电源”。

   1.2 DS18B20特性:

  • 独特的1-Wire接口只需要一个端口引脚用于通信。

  • 多路采集能力使得分布式温度采集应用更加简单。

  • 无需外围器件。

  • 可以采用数据线供电,供电范围为3.0V至5.5V。

  • 温度可测量范围为:-55℃到+125℃(-67℉℉至+125℉℉)。

  • 内部温度采集精度可以由用户自定义为9-Bit至12-Bit。

  • 12Bit的温度采集精度转换时间最大为750ms。

  • 用户可自定义非易失性的温度报警设置。

  • 报警搜索命令识别并寻址温度超出编程限值的设备(温度报警条件)。

  • 应用于温度控制、工业系统、民用产品、温度传感器或者任何温度检测系统中。

 1.3 内部结构

        内部的64位ROM存储着其独一无二的序列号。高速缓存器包含了存储有数字温度结果的2个字节宽度的温度寄存器。另外,高速缓存器还提供了一个字节的高温(TH)和一个字节的低温(TL)温度报警寄存器和一个字节的配置寄存器。 配置寄存器允许用户自定义温度转换精度(9位,10位,11位,12位)。高温和低温温度报警器寄存是非易失性的(EEPROM),其可以在断电的情况下保存。

1.4 温度测量

        其温度转换可由用户自定义为9、10、11、12位,精度分别为0.5℃、0.25℃、0.125℃、0.0625℃分辨率,若不设置则默认为12位的转换精度。若要测量温度,主设备必须向DS18B20发送温度转换命令[44h]才能开始温度转换。温度转换后,转换的温度值将会保存在高速缓存器的温度寄存器中。只有通过读高速缓存器命令[BEh]才能将数据读出,数据通过1-Wire总线传输,传输顺序为低位到高位依次传输温度数据中包含“符号”(S)位,表示温度的正负。

        温度以一个16位标志扩展二进制补码的形式存储在温度寄存器中。符号标志位(S)温度的正负极性:若S=0,则为正数;若S=1,则为负数。如果DS18B20被定义为12位的转换精度,温度寄存器中所有位都将包含有效数据。若定义为11位转换精度,则bit 0(最低位)为未定义的。若定义为10位转换精度,则bit 0和bit 1为未定义的。若定义9位转换精度,则bit 0、bit 1和bit 2为未定义的。下表为在12位转换精度下温度输出数据与相对应温度之间的关系。

        温度是以二进制补码的形式存储在温度寄存器中,所以我们需要先求出其原码再去计算温度值。正数的原码反码补码都是一样的,而负数的补码就是对反码加一当输出温度为正数时输出二进制转换为十进制数后乘以其精度即为其温度值。如:+125℃的十进制(通过二进制转得)输出为2000,乘以精度为:2000*0. 0625=125;+10.125十进制值为162,乘以精度为:162*0.0625=10.125 。当温度为负数时,需先求得其原码(补码取反加一即为其原码,和原码求补码是一样的),这里需要特别说明的是符号位只代表数据的正负,无论是取反还是求值它都是不算在里面的。如:-25.0625的二进制输出原码为1111 1001 1001 0001,其十进制数值为-401,乘以精度为:-401*0.0625=-20.0625 。同理其他温度值也可用该方法求得。另外上电复位时寄存器中的初始值为+85℃。 

1.5 配置寄存器

        高速缓存器中第四个字节即为配置寄存器;如图所示。用户通过改变R1和R0的值来配置DS18B20的分辨率。上电默认为R1=1以及R0=1(12位分辨率)。需要注意的是转换时间与分辨率时间是有关系的,如表格所示。另外寄存器中最高位和低5位作为内部使用而保留使用,不可被写入。

1.6 DS18B20实现温度转换 

1.6.1 ROM命令      

 当初始化完成之后,就可以执行ROM命令。这些命令是对每个设备的64位ROM编码进行操作的,当总线上连接有多个设备时,可以通过这些命令识别各个设备。总共包含有5种ROM命令,每个命令的长度都是8bit。

  • 搜索ROM[F0h]

        当系统上电初始化后,主设备可识别该总线上所有的从设备的ROM编码,这样就可以使得主设备确定总线上的从设备的类型以及数量。

  • 读ROM[33h]

        该命令允许主设备读取DS18B20的64位ROM编码,只有在总线上只有一个DS18B20时才能使用这个命令。如果总线上存在多个从设备,发送此命令,则当所有从设备都会回应时,将会引起数据冲突。

  • 匹配ROM[55h]

        该匹配ROM命令之后接着发出64位ROM编码,使主设备在多点总线上定位一只特定的DS18B20。只有和64位ROM序列完全匹配的DS18B20才会做出响应。总线上的其他从设备都将等待下一个复位脉冲。此命令在总线上有单个或多个器件时都可以使用。

  • 跳过ROM[CCh]

        这条命令可以不用提供64位ROM编码就进行下一步操作,在单点总线(一个DS18B20传感器)情况下可以节省时间。如果总线上不止一个从设备,在跳过ROM命令之后跟着发一条读命令,则所有从设备将会同时执行温度转换,总线上就会发生数据冲突。

  • 警报搜索[ECh]

        该命令的操作与跳过ROM命令基本相同,但是不同的是只有温度高于TH或低于TL(达到报警条件)的从设备才会响应。只要不掉电,警报状态将一直保持,直到温度不在警报范围内为止。

当总线上的主设备通过ROM命令确定了哪个DS18B20可以进行通信时,主设备就可以向其中一个从设备发送功能命令。这些命令可以使得主设备操控从设备进行一系列的操作。下面对这些功能命令做个简单的描述。

1.6.2 RAM命令 

  • 温度转换[44h]

        此命令为初始化单次温度转换,温度转换完后,转换的温度数据会寄存在高速缓存器的byte0(温度数据低八位)和byte1(温度数据高八位)中,之后DS18B20恢复到低功耗的闲置状态。如果总线在该命令后发出读时隙,若DS18B20正在进行温度转换则会响应“0”,若完成了温度转换则响应“1”。如果是用的“ 寄生电源”供电模式,则在命令发出后应立即强制拉高总线,拉高时间应大于表格 28‑2中的温度转换时间。

  • 写入暂存器[4Eh]

        该命令使得主设备向高速缓存器写入3个字节的数据。第一个字节写入高速缓存器的byte2中(TH寄存器),第二个字节的数据写入byte3中(TL寄存器),第三个字节的数据写入byte4中(配置寄存器)。所有的数据都是由低位到高位的顺序写入。复位可随时中断写入。

  • 读取高速缓存器[BEh]

        该命名是读取高速缓存器里的值,从byte0(温度低八位)开始一直读到byte8(CRC校验),每个字节的数据从低位开始传送。若是不想读取这么多数据则在读取数据时随时可以通过复位来终止。

  • 复制高速缓存器[48h]

        该命令是将高速缓存器中的TH(byte2)、TL(byte3)以及配置寄存器(byte4)里的值拷贝到非易失性的存储器EEPROM里。如果总线控制器在这条命令之后跟着发出读时隙,而DS18B20又正在忙于把暂存器拷贝到EEPROM存储器,DS18B20就会输出一个“0”,如果拷贝结束的话,DS18B 20则输出“1”。如果设备采用“寄生电源”供电模式,则在该命令发送后,必须立即强制拉高总线至少10ms。

  • 召回EEPROM[B8h]

        该命令将温度报警触发值(TH和TL)及配置寄存器的数据从EEPROM中召回至高速缓存器中。这个操作会在上电后自动执行一次,所以在上电期间暂存器中一直会存在有效的数据。若在召回命令之后启动读时隙,若DS18B20正在进行召回EEPROM则会响应“0”,若召回完成则响应“1”。

  • 读取供电模式[B4h]

        该命令可以读取总线上的DS18B20是否是由“寄生电源”供电。在读取数据时序中“0”表示“寄生电源供”模式供电,“1”表示外部电源供电。

1.6.3 单总线时序协议

         DS18B20采用严谨的1-Wire总线通信协议来保证数据的完整性。该协议定义多个信号形式:复位脉冲,存在脉冲,写0,写1,读0,读1 。除了存在脉冲由从设备发出,其他信号都由主设备控制。

  • 初始化—复位和存在脉冲

        在初始化状态,总线上的主设备通过拉低1-Wire总线最少480us来表示发送复位脉冲。发送完之后,主设备要释放总线进入接收模式。当总线释放后,5kΩ的上拉电阻将1-Wire总线拉至高电平。当DS18B20检测到该上升沿信号后,其等待15us至60us后将总线拉低60us至240us来实现发送一个存在脉冲。 

  • 写时隙

        主设备通过写时隙将命令写入DS18B20中,写时隙有两种情况:写“1”和写“0”时隙。当主设备将总线从高电平拉至低电平时,启动写时隙,所有的写时隙持续时间最少为60us,每个写时隙间的恢复时间最少为1us。

        当总线(DQ)拉低后,DS18B20在15us至60us之间对总线进行采样,如果采的DQ为高电平则发生写1,如果为低电平则发生写0,如图所示(图中的总线控制器即为主设备)。

        如果要产生写1时隙,必须先将总线拉至逻辑低电平然后释放总线,允许总线在写时隙开始后15us内上拉至高电平。若要产生写0时隙,必须将总线拉至逻辑低电平并保持不变最少60us。

  • 读时隙 

        当我们发送完读取供电模式[B4h]或读高速缓存器[BEh]命令时,必须及时地生成读时隙,只有在读时隙DS18B20才能向主设备传送数据。每个读时隙最小必须有60us的持续时间以及每个读时隙间至少要有1us的恢复时间。当主设备将总线从高电平拉至低电平超过1us,启动读时隙,如图 所示。

        当启动读时隙后,DS18B20将会向主设备发送“0”或者“1”。DS18B20通过将总线拉高来发送1,将总线拉低来发送0 。当读时隙完成后,DQ引脚将通过上拉电阻将总线拉高至高电平的闲置状态。从DS18B20中输出的数据在启动读时隙后的15us内有效,所以,主设备在读时隙开始后的15us内必须释放总线,并且对总线进行采样。

2 项目设计 

2.1 项目框图

2.2 状态机设计

         对DS18B20所有的操作都是由初始化开始的,初始化开始后就需要识别设备,因为本次设计只控制一个DS18B20,所以发送跳过ROM命令即可,识别完设备之后我们就可以对这个设备发送功能命令了,我们需要对其发送温度转换命令,命令发送完之后我们等待一段时间让其将温度转换完成,同时拉高总线让其供电以适配“寄生电源”模式。虽然本次实验为外部供电方式,但是我们可以编写同样适用“寄生电源”供电模式来增加模块的复用性(若以后我们需要用到“寄生电源”供电模式同样可以直接调用)。当等待温度转换之后再一次初始化,跳过ROM,然后发送读取高速缓存器命令来读取温度,发送完读命名之后进入读温度状态生成读时序,当读完两个字节的温度后我们就达到了我们读取温度的目的,就发送初始化的复位脉冲停止读数,同时进入下一个初始化开始下一轮的温度转换读取。这一整个过程我们可以用下面的流程框图来概括,如图所示。

2.3 波形图设计

         在整个时序过程中无论是发送开始信号,还是“1”,“0”的时序组成我们都需要用到时间信号,而这些时间信号的最小单位为 us,所以我们先产生单位时钟为 1us 的时钟来作为 DS18B20 的驱动时钟。如上波形图所示。

        dq:dq为数据总线,其类型为输入输出型,定义为wire型变量,这里我们用组合逻辑对齐进行赋值,所以我们将借助dq_out(总线输入)和dq_en(总线使能信号)来给dq赋值, 

        S_INIT:初始化状态,上面说到首先拉低总线至少480us,在这里我们将其拉低500us后再将总线释放。前面理论部分多次说到释放总线,那么代码编写中我们如何释放总线呢?代码中让总线处于高阻态即为释放总线,高阻态用z表示。总线释放后器件内置电阻会将其上拉,当DS18B20检测到上升沿后,等待15us 至60us后会拉低总线持续时间为60us至240us。在这里我们在计数器到570us后检测,即释放总线后的70us检测。检测到总线拉低了才表示DS18B20发送了存在脉冲。从初始化时序图可以看到,整个初始化的持续时间必须大于960us,所以这里我们在计数器计到1000us(该值也可设为其它值,但是需 大于960)时,同时在570us采到低电平(检测到存在脉冲)时才让其跳转。很明显我们在同一个上升沿踩不到这两个条件同时满足,所以我们需要借助一个标志信号来显示我们在570us采到低电平,然后使用这个标志信号标志是否检测到存在脉冲,这样在同一个时钟上升沿就可以同时采到这两个条件了。如图所示,我们产生一 个检测存在脉冲的标志信号(flag_pulse)。

        flag_pulse:当采到存在脉冲后,拉高标志信号,让其持续到状态跳转时,作为状态跳转的判断条件。

        S_WR_CMD:当满足初始化状态跳转后,让其跳转到写跳过ROM命令和温度转换命令状态,同时需要让cnt_1us计数器清0开始下一轮的计数。所有的命令都是由八位二进制数组成的,所以我们只要从低位到高位依次将八位二进制命令写进去就行。而我们一个状态是写两个命令所以我们一个状态需要写16位二进数,那如何 判断我们是要写0还是写1呢?所以这里又引入了一个bit计数器(bit_cnt),去对我们写入第几个bit进行判断,当bit_cnt等于0时表示此时写的是两个连起来命令的最低位,也就是第一个命令的最低位。当bit_cnt等于7时表示此时写的是两个连起来命令的第8位,也就是第一个命令的最高位。当bit_ cnt等于8时表示此时写的是两个连起来命令的第9位,也就是第二个命令的最低位,以此类推。当状态机转态跳转到S_WR_CMD时让cnt_1us计数器从0开始计数,计到64us让其清零并开始下一轮的65us计数。因为我们的写时隙最少为60us且每个写时隙间的恢复时间最少为1us,所以计数时间必须大于61 us,这里我们让其计了65s。由写时隙图可以知道无论是写0还是写1都必须先将总线拉低,若是写0就将总线一直拉低,直到写0结束,若是写1就将总线在拉低后15us内释放总线,释放总线后总线会自动拉高并不用我们自己去控制。当bit_cnt等于15且cnt_1us等于64时表示两个命令都已经写进去了,此时我们跳转到下一状态等待温度转换完成,并且让计数器清零。

        S_WAIT:跳转到这个状态后,计数器开始计数,由于本次实验的转化位数为12位,所以需等待750ms。如图所示当计数器计到750000(750ms)时让状态跳转且计数器清零。

         当温度转换完成之后,跳到下一个初始换状态,初始化状态完成之后,进入到写跳过ROM以及读温度命令,这两个状态的控制时序与S_INIT和S_WR_CMD的控制时序是一样的。在此就不再次重复叙述了。

        当读高速缓存器命令发送完之后我们需立即生成读时隙,这样DS18B20才会向总线发送温度数据。如图 28‑9 读时隙时序图可知,每个读时隙之间都需要最少60us的读时隙持续时间以及最少1us的恢复时间,无论是读1时隙还是读0时隙,都需要拉低总线超过1us来启动读时隙,当读时隙开始后DS18B20会发送 高速缓存器的值,从低位开始发送,读1和读0的数据有效时间都为15us,所以当读时隙开始后我们需要在15us内采集数据,如图 28‑20所示我们将总线拉低2us后释放总线启动读时隙,而当计数器计到13us时我们对数据进行采集,同时将采集的数据寄存在data_tmp中。当bit_cnt等于15且cnt_ 1us=64时说明我们高速缓存器中两个字节的温度值已经读出来了,后面的数据我们不需要读取,直接让状态机回到初始状态终止读数据。

        当读完16bit的温度后,我们需要对其转换后才是我们的温度值。如图中所示,当其符号位为正时温度值不变,为负时温度值取反加一。此时data里的值显示的是没有转换为小数温度的值。我们在理论部分知道乘以其精度0.0625才是显示的温度值。数码管的动态显示章节中我们学习到给数码管动态显示的数字是整数的。我们 只能给相应的位数给小数点才能让其显示变为小数。因为数码管位数原因,我们让数码管显示温度值小数点后三位,第四位小数不显示。所以我们需要对data进行(data*625)/10操作才能满足数码管显示的数值要求。这样数码管就能显示DS18B20读出的温度值了。

2.4 程序设计

module  ds18b20_ctrl
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    inout   wire            dq          ,
    output  wire    [19:0]  data_out    ,
    output  reg             sign        
);

//parameter define
parameter   S_INIT       = 3'd1,    //初始状态
            S_WR_CMD     = 3'd2,    //给跳过ROM及温度转换指令
            S_WAIT       = 3'd3,    //等待温度转换完成
            S_INIT_AGAIN = 3'd4,    //再次回到初始化
            S_RD_CMD     = 3'd5,    //给跳过ROM及读温度转换指令
            S_RD_TEMP    = 3'd6;    //读温度状态
parameter WR_44CC_CMD = 16'h44cc;   //跳过ROM及温度转换命令,低位在前
parameter WR_BECC_CMD = 16'hbecc;   //跳过ROM及读取温度命令,低位在前
parameter S_WAIT_MAX  = 750000  ;   //750ms

reg         clk_1us     ;   //分频时钟,单位时钟1us
reg [4:0]   cnt         ;   //分频计数器
reg [2:0]   state       ;   //状态机状态
reg [19:0]  cnt_1us     ;   //微秒计数器
reg [3:0]   bit_cnt     ;   //字节计数器
reg [15:0]  data_tmp    ;   //读取ds18b20的温度
reg [19:0]  data        ;   //判断完正负后的温度
reg         flag_pulse  ;   //初始化存在脉冲标志信号
reg         dq_out      ;   //输出总线数据,即FPGA给的总线数据值
reg         dq_en       ;   //输出总线数据使能信号

//cnt:分频计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <= 5'b0;
    else if(cnt == 5'd24)
        cnt <= 5'b0;
    else
        cnt <= cnt + 1'b1;

//clk_1us:产生单位时钟为1us的时钟
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        clk_1us <= 1'b0;
    else if(cnt == 5'd24)
        clk_1us <= ~clk_1us;
    else
        clk_1us <= clk_1us;
        
//cnt_1us:1us时钟计数器,用于状态跳转
always@(posedge clk_1us or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1us <= 20'b0;
    else if(((state==S_WR_CMD || state==S_RD_CMD || state==S_RD_TEMP)
    && cnt_1us==20'd64) || ((state==S_INIT || state==S_INIT_AGAIN) &&
    cnt_1us==20'd999) || (state==S_WAIT && cnt_1us==S_WAIT_MAX))
        cnt_1us <= 20'b0;
    else
        cnt_1us <= cnt_1us + 1'b1;    

//初始化存在脉冲标志信号:初始化状态时,当总线发来存在脉冲才能初始化成功
always@(posedge clk_1us or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        flag_pulse <= 1'b0;
    else if(cnt_1us == 20'd570 && dq == 1'b0 && (state == S_INIT ||
        state == S_INIT_AGAIN))
        flag_pulse <= 1'b1;
    else if(cnt_1us == 999)
        flag_pulse <= 1'b0;
    else
        flag_pulse <= flag_pulse;   

//bit_cnt:bit计数器,写1bit或读1bit加1,一次写完之后清零
always@(posedge clk_1us or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else if((state == S_RD_TEMP || state == S_WR_CMD || state == S_RD_CMD) && (cnt_1us == 20'd64 && bit_cnt == 4'd15))
        bit_cnt <= 4'b0;
    else if((state == S_WR_CMD || state == S_RD_CMD || state == S_RD_TEMP) && cnt_1us == 20'd64)
        bit_cnt <= bit_cnt + 1'b1;
        
//状态跳转
always@(posedge clk_1us or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= S_INIT;
    else
    case(state)
    //初始化最小时间为960us
        S_INIT: //收到存在脉冲且时间需大于960us跳转
            if(cnt_1us == 20'd999 && flag_pulse == 1'b1 )
                state <= S_WR_CMD;
            else
                state <= S_INIT;
        S_WR_CMD: //发送完跳过ROM和温度转换命令后跳转
            if(bit_cnt == 4'd15 && cnt_1us == 20'd64 )
                state <= S_WAIT;
            else
                state <= S_WR_CMD;
        S_WAIT: //等待750ms(等待温度转换)后跳转
            if(cnt_1us == S_WAIT_MAX)
                state <= S_INIT_AGAIN; 
            else
                state <= S_WAIT;
        S_INIT_AGAIN: //再次初始化后跳转
            if(cnt_1us == 20'd999 && flag_pulse == 1'b1 )
                state <= S_RD_CMD;
            else
                state <= S_INIT_AGAIN;
        S_RD_CMD: //发送完跳过ROM和读取温度命令后跳转
            if(bit_cnt == 4'd15 && cnt_1us == 20'd64)
                state <= S_RD_TEMP;
            else
                state <= S_RD_CMD;
        S_RD_TEMP: //读完2字节的温度后跳转
            if(bit_cnt == 4'd15 && cnt_1us == 20'd64)
                state <= S_INIT;
            else
                state <= S_RD_TEMP;
        default:
                state <= S_INIT;
endcase 

//data_tmp:读出温度,寄存在data_tmp里
always@(posedge clk_1us or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_tmp <= 12'b0;
    //总线拉低后数据有效时间为15us
    else if(state == S_RD_TEMP && cnt_1us == 20'd13)
        data_tmp <= {dq,data_tmp[15:1]};
    else
        data_tmp <= data_tmp;
       
 //温度判断,输出温度
always@(posedge clk_1us or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data <= 20'b0;
    else if(data_tmp[15] == 1'b0 && state == S_RD_TEMP && cnt_1us == 20'd60 && bit_cnt == 4'd15)
        data <= data_tmp[10:0];
    else if(data_tmp[15] == 1'b1 && state == S_RD_TEMP && cnt_1us == 20'd60 && bit_cnt == 4'd15)
        data <= ~data_tmp[10:0] + 1'b1;

 //温度判断,输出符号位
always@(posedge clk_1us or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sign <= 1'b0;
    else if(data_tmp[15] == 1'b0 && state == S_RD_TEMP && cnt_1us == 20'd60 && bit_cnt == 4'd15)
        sign <= 1'b0;
    else if(data_tmp[15] == 1'b1 && state == S_RD_TEMP && cnt_1us == 20'd60 && bit_cnt == 4'd15)
        sign <= 1'b1; 
        
//温度转换,由于数码管位数有限,在这里保留小数点后三位
assign data_out = (data * 10'd625)/ 4'd10;
//当使能信号为1是总线的值为dq_out的值,为0时为高阻态
assign dq = (dq_en == 1 ) ? dq_out : 1'bz;

//给各状态下的总线相应的时序
always@(posedge clk_1us or negedge sys_rst_n)
    if(sys_rst_n == 1'b0) begin
        dq_out <= 1'b0;
        dq_en <= 1'b0;
    end
    else
    case(state)
        //初始化是最小480us低电平,然后释放总线
        S_INIT:
            if(cnt_1us < 20'd499) begin
                dq_out <= 1'b0;
                dq_en <= 1'b1;
            end
            else begin
                dq_out <= 1'b0;
                dq_en <= 1'b0;
            end
        //每一个写时段最少有60us的持续时间和最少1us的恢复时间
        //写0:总线拉低后一直拉低,最少60us 
        //写1:总线拉低后必须在15us内释放总线
        S_WR_CMD:
            if(cnt_1us > 20'd62) begin
                dq_out <= 1'b0;
                dq_en <= 1'b0;
            end
            else if(cnt_1us <= 20'b1) begin
                dq_out <= 1'b0;
                dq_en <= 1'b1;
            end
            else if(WR_44CC_CMD[bit_cnt] == 1'b0) begin
                dq_out <= 1'b0;
                dq_en <= 1'b1;
            end
            else if(WR_44CC_CMD[bit_cnt] == 1'b1) begin
                dq_out <= 1'b0;
                dq_en <= 1'b0;
            end
            //为适应寄生电源,温度转换命令后将总线拉高,给钽电容充电
        S_WAIT:
            begin
                dq_out <= 1'b1;
                dq_en <= 1'b1;
            end
            //与第一次初始化时序一致
        S_INIT_AGAIN:
            if(cnt_1us < 20'd499) begin
                dq_out <= 1'b0;
                dq_en <= 1'b1;
            end
            else begin
                dq_out <= 1'b0;
                dq_en <= 1'b0;
            end
            //与发送跳过ROM和读取温度命的时序一致
        S_RD_CMD:
            if(cnt_1us > 20'd62) begin
                dq_out <= 1'b0;
                dq_en <= 1'b0;
            end
            else if(cnt_1us <= 20'b1) begin
                dq_out <= 1'b0;
                dq_en <= 1'b1;
            end 
            else if(WR_BECC_CMD[bit_cnt] == 1'b0) begin
                dq_out <= 1'b0;
                dq_en <= 1'b1;
            end
            else if(WR_BECC_CMD[bit_cnt] == 1'b1) begin
                dq_out <= 1'b0;
                dq_en <= 1'b0;
            end
            //拉低总线超过1us后释放总线
        S_RD_TEMP:
            if(cnt_1us <=1) begin
                dq_out <= 1'b0;
                dq_en <= 1'b1;
            end
            else begin
                dq_out <= 1'b0;
                dq_en <= 1'b0;
            end
        default:;
    endcase        
endmodule

注:因为:inout   wire dq,仿真需要DS18B20仿真模型,暂未对上述文件进行仿真,该程序来自于野火,仅供学习参考!!!

  • 8
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
探索者STM32F4开发板是一种用于嵌入式系统开发的硬件平台,它集成了强大的STM32F407芯片和丰富的外设资源。要学习DS18B20数字温度传感器的使用,我们首先需要了解DS18B20的原理和特性。 DS18B20是一种数字温度传感器,采用一线制串行总线进行数据传输。它具有高精度、宽温度测量范围、低功耗等特点,因此在许多嵌入式应用中被广泛使用。 在探索者STM32F4开发板上学习DS18B20的使用,我们需要以下步骤: 1. 硬件连接:将DS18B20的引脚连接到探索者STM32F4开发板的GPIO引脚上。确保连接正确,例如将DS18B20的VCC引脚连接到3.3V电源,GND引脚连接到地,数据引脚连接到开发板的GPIO引脚。 2. 配置GPIO引脚:在开发板上选择一个GPIO引脚作为DS18B20的数据线,并设置为输出模式。这个GPIO引脚将用来与DS18B20进行通信。 3. 程序编写:使用探索者STM32F4开发板开发环境进行程序编写。首先,我们需要编写一些函数来实现与DS18B20的通信,例如初始化DS18B20、发送读取温度命令、接收温度数据等。然后,在主程序中调用这些函数,实现温度的读取和显示。 4. 温度数据处理:DS18B20采集到的温度数据是以16位二进制补码形式存储的,需要进行转换和处理才能得到实际温度值。我们可以编写函数来将温度数据转换为摄氏度或华氏度,并将其显示在探索者STM32F4开发板的显示屏上。 通过以上步骤,我们就可以在探索者STM32F4开发板上学习和使用DS18B20数字温度传感器了。这个过程将帮助我们深入了解硬件和软件的协作,提高嵌入式系统开发的能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super_WY_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值