【FPGA】DS18B20温度显示

目录

专用名词

一. 温度传感器介绍

1.2 DS18B20结构

二. DS18B20工作流程

2.1 三步骤

2.2 初始化序列

2.3 ROM命令

2.4 功能ROM命令

2.5 读写时序

三 代码设计

3.1 状态跳转——工作流程

3.2 状态跳转条件

3.3 代码

四 上板验证

五 总结


专用名词

CONFIGURATION REGISTER 配置寄存器
scrathpad memory 缓冲存储器、高速暂存存储器、暂时存储器
​
​

一. 温度传感器介绍

DS18B20 默认温度数据是 12bit(补码),最高位为符号位(1:负温,0:正温),低 4bit为小数位,低 11bit 转化为十进制后乘以 0.0625 即为实际温度值。

DS18B20 是一款常用的数字式温度传感器,输出数字信号来表示温度值,具有体积小,精度高等特点。DS18B20 的温度测量范围为-55℃~+125℃;精度为±0.5℃;输出温度值位宽可编程(9—12bit);单总线接口允许多个设备挂在同一总线,可以用于部署分布式温度采集系统;可配置的温度转换时间,最大温度转换时间为 750ms。

DS18B20的传输序列:

1. 初始化
2. ROM指令
3. 功能指令
​
当需要读取 DS18B20 的温度转换结果时,ROM 指令发送完后,需要回到步骤 1,再次发送初始化序列,然后发送功能指令。

1.2 DS18B20结构

高速缓存器的结构框图如下:

高速缓存器一共有九个八位寄存器,字节0和字节1是存储温度的,从低字节LSB到高字节MSB,这两个字节不可以更改,只能读出。认配置温度数据为12位,其中最高位为符号位,即温度值共11位,最低四位为小数位。FPGA在读取温度数据时,一次会读2字节共16位,读完后将低11位的二进制数转化为十进制数后再乘以0.0625得到所测的实际温度值。另外还需要判断温度的正负,前5个数字为符号位,这5位同时变化,我们只需要判断其中任何一位就可以了。前5位为1时,读取的温度为负值,则测到的数值需要取反加1再乘以0.0625才可得到实际温度值。前5位为0时,读取的温度为正值,只要将测得的数值乘以0.0625即可得到实际温值。高温出发值 TH 和低温触发值 TL 都是要满足一定条件才会响应,需要自己设置。

由上图可知DS18B20的高速缓存器共有9个8位寄存器,其中温度数据低位(LSB)对应字节 地址0,温度数据高位(MSB)对应字节地址1,以此类推,配置寄存器的字节地址为4。温度数据存放的格式如下图:

DS18B20在出厂时默认配置温度数据为12位,其中最高位为符号位,即温度值共11位,最低四位为小数位。FPGA在读取温度数据时,一次会读2字节共16位,读完后将低11位的二进制 数转化为十进制数后再乘以0.0625得到所测的实际温度值。

另外还需要判断温度的正负,前5 个数字为符号位,这5位同时变化,我们只需要判断其中任何一位就可以了。前5位为1时,读 取的温度为负值,则测到的数值需要取反加1再乘以0.0625才可得到实际温度值。前5位为0时, 读取的温度为正值,只要将测得的数值乘以0.0625即可得到实际温度值。

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

二. 单总线协议

2.1 单总线传输原理

由于DS18B20温度传感器采用单总线的方式进行通信,因此我们先简略的介绍一下单总线

通信的原理。

单总线传输的定义:顾名思义,即主机和从机用一根总线进行通信,是一种半双工的通信
方式,单线=时钟线+数据线+控制线(+电源线)。理想状况下一条总线上的从器件数量几乎不
受数量限制。

 单总线优劣势:

单总线技术具有线路简单,硬件开销少,成本低廉,便于总线扩展和维护等优点。但由于只有一根总线,驱动能力一般较差,不能接过多的从器件,实际使用中,一般最多只能接8个从器件;抗干扰能力较差,一般只能在中短距离的低速传输中使用;软件设计复杂,事物往往有两面性,硬件部分的简单往往需要软件在复杂度上做出牺牲。

 数据传输:

   单总线通信协议为确保数据的完整性,定义了如下几种单线信号类型:复位脉冲、存在脉
冲、写0、写1、读0和读1。所有这些信号,除存在脉冲外,都是由总线控制器发出的。
   单总线协议中主机和从机(DS18B20)间的任何通讯都需要以初始化序列开始

2.2 数据传输三步骤

1. 初始化序列
2. rom命令
3. 功能命令

2.3 初始化序列

 即初始化序列分为三个步骤:
1.主机发送复位脉冲 至少480us
2.上拉电阻将总线拉高 15~60us
3.DS18B20发送存在脉冲应答主机 60~240us

初始化—复位和存在脉冲

单总线上的所有事件都必须以初始化为开始。初始化序列由总线上的主设备发出的复位脉冲以及紧跟着从设备回应的存在脉冲构成。该存在脉冲是让总线主设备知道 DS18B20 在总线上并准备好运行

与 DS18B20 所有的通信都是由初始化开始的,初始化由主设备发出的复位脉冲及 DS18B20 响应的存在脉冲组成。如下图 所示。当 DS18B20 响应复位信号的存在脉冲 后,则其向主设备表明其在该总线上,并且已经做好了执行命令的准备。 在初始化状态,总线上的主设备通过拉低 1-Wire 总线最少 480us 来表示发送复位脉 冲。发送完之后,主设备要释放总线进入接收模式。当总线释放后,上拉电阻将 1- Wire 总线拉至高电平。当 DS18B20 检测到该上升沿信号后,其等待 15us 至 60us 后将总线 拉低 60us 至 240us 来实现发送一个存在脉冲。

2.4 ROM命令

用于对 64 位的 ROM 操作;

F0H:搜索ROM 
    当系统上电初始化后,主设备可识别该总线上所有的从设备的 ROM 编码,这样就可以使得主设备确定总线上的从设备的类型以及数量。 
33H:读 ROM  
    该命令允许主设备读取 DS18B20 的 64 位 ROM 编码,只有在总线上只有一个 DS18B20 时才能使用这个命令。如果总线上存在多个从设备,发送此命令,则当所有从设 备都会回应时,将会引起数据冲突。
55H:匹配 ROM 
    该匹配 ROM 命令之后接着发出 64 位 ROM 编码,使主设备在多点总线上定位一只特定的 DS18B20。只有和 64 位 ROM 序列完全匹配的 DS18B20 才会做出响应。总线上的其 他从设备都将等待下一个复位脉冲。此命令在总线上有单个或多个器件时都可以使用。
CCH:跳过 ROM
    这条命令可以不用提供 64 位 ROM 编码就进行下一步操作,在单点总线(一个 DS18B20 传感器)情况下可以节省时间。如果总线上不止一个从设备,在跳过 ROM 命令 之后跟着发一条读命令,则所有从设备将会同时执行温度转换,总线上就会发生数据冲突。
ECH:报警搜索
    该命令的操作与跳过 ROM 命令基本相同,但是不同的是只有温度高于 TH 或低于 TL (达到报警条件)的从设备才会响应。只要不掉电,警报状态将一直保持,直到温度不在警报范围内为止。
​
如果系统中只有一个 DS18B20,就不需要读取 ROM 与匹配 ROM 操作,可以直接发送跳过 ROM(CCH)命令,然后就可以开始测量温度。

2.5 功能ROM命令

用于主机向DS18B20 的暂存器写/读数据,发温度转换以及读取供电模式;

44H:温度转换 
    此命令为初始化单次温度转换,温度转换完后,转换的温度数据会寄存在高速缓存器 的 byte0(温度数据低八位)和 byte1(温度数据高八位)中,之后 DS18B20 恢复到低功耗 的闲置状态。如果总线在该命令后发出读时隙,若 DS18B20 正在进行温度转换则会响应 “0”,若完成了温度转换则响应“1”。如果是用的“寄生电源”供电模式,则在命令发 出后应立即强制拉高总线,拉高时间应大于时序要求。
4EH:写暂存寄存器
    该命令使得主设备向高速缓存器写入 3 个字节的数据。第一个字节写入高速缓存器的 byte2 中(TH 寄存器),第二个字节的数据写入 byte3 中(TL 寄存器),第三个字节的数据写入 byte4 中(配置寄存器)。所有的数据都是由低位到高位的顺序写入。复位可随时中断写入。
48H:复制 RAM 
    该命令是将高速缓存器中的 TH(byte2)、TL(byte3)以及配置寄存器(byte4)里的 值拷贝到非易失性的存储器 EEPROM 里。如果总线控制器在这条命令之后跟着发出读时 隙,而 DS18B20 又正在忙于把暂存器拷贝到 EEPROM 存储器,DS18B20 就会输出一个 “0”,如果拷贝结束的话,DS18B20 则输出“1”。如果设备采用“寄生电源”供电模 式,则在该命令发送后,必须立即强制拉高总线至少 10ms。
BEH:读暂存寄存器
    从 byte0(温度低八位)开始一直读到 byte8(CRC 校验),每个字节的数据从低位开始传送。若是不想读取这么多数据则在读取数据时随时可以通过主机发送复位脉冲来停止读取。
B8H:重调 EEPROM 
    该命令将温度报警触发值(TH 和 TL)及配置寄存器的数据从 EEPROM 中召回至高速 缓存器中。这个操作会在上电后自动执行一次,所以在上电期间暂存器中一直会存在有效 的数据。若在召回命令之后启动读时隙,若 DS18B20 正在进行召回 EEPROM 则会响应 “0”,若召回完成则响应“1”。
​
B4H:读供电方式
    该命令可以读取总线上的 DS18B20 是否是由“寄生电源”供电。在读取数据时序中 “0”表示“寄生电源供”模式供电,“1”表示外部电源供电。
    
暂存器字节 0 和字节 1 是只读的,用于存储传感器测量的温度值

2.6 读写时序

写时隙:

主设备通过写时隙将命令写入 DS18B20 中,写时隙有两种情况:写“1”和写“0”时 隙。主设备通过写 1 时隙来向 DS18B20 中写入逻辑 1,通过写 0 时隙来向 DS18B20 中写入 逻辑 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 内必须释放总线,并且对总线进行采样。

三 代码设计

3.1 状态跳转——工作流程

DS18B20的典型温度读取过程为:初始化➔发跳过ROM命令(CCH)➔发开始转换命令(44H)➔延时➔初始化➔发送跳过ROM命令(CCH)➔发读存储器命令(BEH)➔连续读出两个字节数据(即温度)➔结束或开始下一循环。

主状态机

就是用来表示温度传感器的工作流程的,首先是初始化

即初始化序列分为三个步骤:
1.主机发送复位脉冲 至少480us
2.上拉电阻将总线拉高即 释放总线 15~60us
3.DS18B20发送存在脉冲应答主机 60~240us

初始化完成之后,主机就可以向从机读写数据。主机向从机发送跳过ROM命令,跳过ROM命令对单总线上仅有一个从机的情况下可以节省很多时间,不用提供64位ROM编码就可以进行下一步操作。

条件:

当从机接收到应答,且计数器记到60us进行采样,slave_ack == 1'b0 方可发送rom指令
即从机接收应答到发送rom指令是,跳过ROm命令发出 M_ACK2M_ROMS

本次实验包含三个指令:

CMD_ROMS  = 8'hCC,//跳过ROM指令
CMD_CONT  = 8'h44,//温度转化
CMD_RTMP  = 8'hBE;//读暂存器

发送完跳过ROM指令之后进入发送功能指令

包括温度转换和读取温度暂存器命令,需要我们标记区分,因此需要一个flag标志,当m_wait2m_rest阶段,flag为1,当m_rtmp2m_idle阶段,则flag为0

若m_roms2m_cont,表示转换温度命令,当跳过rom指令写入完成,即当从状态机完成数据写入并且从状态机处于S_DONE,且标志flag为0,将rom指令写入之后,进入温度转换

温度转换也需要向暂存器写入数据,即温度最开始的LSB到MSB两个字节的数据,CMD_CONT = 8'h44命令写入后,即当从状态机完成数据写入并且从状态机处于S_DONE,进入等待状态

等待状态即等待温度转换完成,需要等待750ms,之后直接进入idle状态或者主机发送复位脉冲状态,之后再次进行初始化序列,完成后发送rom指令,再进行读取温度寄存器指令

若m_roms2m_rcmd,表示读温度暂存器命令,即当从状态机完成数据写入并且从状态机处于S_DONE,当跳过rom指令写完后且flag为1,进入读温度指令。

读温度命令写需要进行写数据操作,当CMD_RTMP = 8'hBE写入之后,即当从状态机完成数据写入并且从状态机处于S_DONE,进入读取温度值,读温度值则进行读操作,读取温度完成后进入idle状态

从状态机:

主机想从机进行写数据(写操作),或从机向主机发送数据(读操作)

从机开启条件:

assign s_idle2s_low  = s_state_c == S_IDLE && (m_state_c == M_ROMS || 
             m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);  

即当主机处于M_ROMS|| M_CONT || M_RCMD || M_RTMP时,从机开启进入总线拉低状态,s_low

assign s_low2s_send  = s_state_c == S_LOW  && (m_state_c == M_ROMS || 
            m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;//拉低2us

即当主机处于M_ROMS|| M_CONT || M_RCMD ,从机近些写操作,向从机写入八比特数据,

写数据是一比特一比特的数据写入,写入一个比特需要60us,进入释放总线状态,S_RELE,一个字节没有写完则继续进行写入,释放总线 3us (至少1us),即s_rele2s_low,然后主机拉低总线至少1us,进行写“0”或写“1”,再进入释放总线状态,直到一个字节数据写完,从机从s_rele2s_done,释放总线 3us (至少1us)。

读温度暂存器:当主机处于M_RTMP,从机进行读操作,主机拉低总线至少1us,进行读数据,读1bit需要60us,直到2字节数据读完(低字节数据LSB到高字节MSB)一共16位。

计数器

计数器主要记主状态机状态转移时间,和从状态机的读写时间,以及数据位数

设置时间参数:

   parameter   TIME_1US  = 50,      //基本时间1us
                //主状态机延时
                TIME_RST  = 500,     //复位脉冲 500us
                TIME_REL  = 20,      //主机释放总线 20us
                TIME_PRE  = 200,     //主机接收存在脉冲 200us
                TIME_WAIT = 750000,  //主机发完温度转换命令 等待750ms
                //从状态机的延时
                TIME_LOW  = 2,       //主机拉低总线 2us
                TIME_RW   = 60,      //主机读、写1bit 60us
                TIME_REC  = 3;       
                //主机读写完1bit释放总线 3us包括   rele_low,rele_done

bit计数器主要用于记写入一个字节数据或读出两个字节数据

    assign add_cnt_bit = s_state_c == S_RELE && end_cnt1; // 从机处于RELE状态,时隙恢复时间,表明1 bit 数据传输完成
    assign end_cnt_bit = add_cnt_bit && cnt_bit == ((m_state_c == M_RTMP)?16-1:8-1);

slave_ack:采样传感器的存在脉冲

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            slave_ack <= 1'b1;
        end     //接收应答状态      计数器计到60us  进行采样
        else if(m_state_c == M_RACK && cnt0 == 60 && end_cnt_1us)begin 
            slave_ack <= dq_din;
        end 
    end 

输出信号

    //dq_out_en     
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            dq_out_en <= 0;
        end 
        else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin 
            dq_out_en <= 1'b1;      //输出 dq_out
        end 
        else if(m_rest2m_rele | s_send2s_rele | s_low2s_samp)begin  
            dq_out_en <= 1'b0;      //不输出 dq_out
        end 
    end 
​
    //dq_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dq_out <= 0;
        end 
        else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin 
            dq_out <= 1'b0;
        end 
        else if(s_low2s_send)begin 
            dq_out <= wr_data[cnt_bit];
        end 
    end

温度采集转换并输出:

//orign_data    温度采集
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            orign_data <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt1 == 12 && end_cnt_1us)begin //温度采集,在时隙起始后15us内采样
            orign_data[cnt_bit] <= dq_din; 
        end 
    end
​
//temp_data     温度判断
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            temp_data <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin 
            if(orign_data[15])                          //判断正负温度,决定是否需要做补码-原码转化
                temp_data <= ~orign_data[10:0] + 1'b1;  //负温 则取反加1 
            else 
                temp_data <= orign_data[10:0];          //正温
        end 
    end
​
/*
    实际的温度值为 temp_data * 0.0625;
    为了保留4位小数精度,将实际温度值放大了10000倍,
    即 temp_data * 625;
​
*/
    assign temp_data_w = temp_data * 625;       //组合逻辑计算十进制温度
​
//temp_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_out <= 0;
        end 
        else if(m_state_c == M_RTMP && s_rele2s_done)begin 
            temp_out <= temp_data_w;
        end 
    end 
​
//temp_out_vld                                     指示温度值输出有效
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_out_vld <= 0;
        end 
        else begin 
            temp_out_vld <= m_state_c == M_RTMP && s_rele2s_done;
        end 
    end 

状态跳转条件

主状态机:

    assign m_idle2m_rest = m_state_c == M_IDLE && (1'b1);       //进入m_idle状态,下一个时钟周期上升沿将进入复位状态
    assign m_rest2m_rele = m_state_c == M_REST && (end_cnt0);
    assign m_rele2m_rack = m_state_c == M_RELE && (end_cnt0);
    assign m_rack2m_roms = m_state_c == M_RACK && (end_cnt0  && slave_ack == 1'b0);
    assign m_roms2m_cont = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b0);
    assign m_roms2m_rcmd = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b1);
    assign m_cont2m_wait = m_state_c == M_CONT && (s_state_c == S_DONE);
    assign m_wait2m_rest = m_state_c == M_WAIT && (end_cnt0);
    assign m_rcmd2m_rtmp = m_state_c == M_RCMD && (s_state_c == S_DONE);
    assign m_rtmp2m_idle = m_state_c == M_RTMP && (s_state_c == S_DONE);

从状态机:

    assign s_idle2s_low  = s_state_c == S_IDLE && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);     
​
    assign s_low2s_send  = s_state_c == S_LOW  && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;   
    assign s_low2s_samp  = s_state_c == S_LOW  && (m_state_c == M_RTMP && end_cnt1); 
​
    assign s_send2s_rele = s_state_c == S_SEND && (end_cnt1);       
    assign s_samp2s_rele = s_state_c == S_SAMP && (end_cnt1);       
    assign s_rele2s_low  = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b0);   
    assign s_rele2s_done = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b1);   

3.2 代码

3.2 状态跳转条件

主状态机状态跳转条件:

    assign m_idle2m_rest = m_state_c == M_IDLE && (1'b1);       //进入m_idle状态,下一个时钟周期上升沿将进入复位状态
    assign m_rest2m_rele = m_state_c == M_REST && (end_cnt0);
    assign m_rele2m_rack = m_state_c == M_RELE && (end_cnt0);
    assign m_rack2m_roms = m_state_c == M_RACK && (end_cnt0  && slave_ack == 1'b0);
    assign m_roms2m_cont = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b0);
    assign m_roms2m_rcmd = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b1);
    assign m_cont2m_wait = m_state_c == M_CONT && (s_state_c == S_DONE);
    assign m_wait2m_rest = m_state_c == M_WAIT && (end_cnt0);
    assign m_rcmd2m_rtmp = m_state_c == M_RCMD && (s_state_c == S_DONE);
    assign m_rtmp2m_idle = m_state_c == M_RTMP && (s_state_c == S_DONE);

从状态机:

    assign s_idle2s_low  = s_state_c == S_IDLE && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);     
​
    assign s_low2s_send  = s_state_c == S_LOW  && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;   
    assign s_low2s_samp  = s_state_c == S_LOW  && (m_state_c == M_RTMP && end_cnt1); 
​
    assign s_send2s_rele = s_state_c == S_SEND && (end_cnt1);       
    assign s_samp2s_rele = s_state_c == S_SAMP && (end_cnt1);       
    assign s_rele2s_low  = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b0);   
    assign s_rele2s_done = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b1);   

3.3 代码

ds18_driver:

module ds18_driver(
    input                       clk          ,
    input                       rst_n        ,
    input                       dq_din        ,
    output  reg                 dq_out       ,
    output  reg                 dq_out_en    , 
    output  reg                 temp_sign    ,//温度值符号位 0:正 1:负温
    output  reg   [23:0]        temp_out     ,
    output  reg                 temp_out_vld 
);

//状态机参数

    localparam  M_IDLE = 9'b0_0000_0001,//空闲状态
                M_REST = 9'b0_0000_0010,//复位
                M_RELE = 9'b0_0000_0100,//释放总线 -- ds18b20等待
                M_RACK = 9'b0_0000_1000,//接收应答 -- 主机接收存在脉冲
                M_ROMS = 9'b0_0001_0000,//rom指令  -- 跳过rom指令
                M_CONT = 9'b0_0010_0000,//转化
                M_WAIT = 9'b0_0100_0000,//等待     -- 12bit分辨率下的温度转化时间
                M_RCMD = 9'b0_1000_0000,//读命令   -- 读暂存器命令
                M_RTMP = 9'b1_0000_0000;//读温度   -- 产生读时隙 -- 接收2字节带符号位的补码温度值

    localparam  S_IDLE = 6'b00_0001,
                S_LOW  = 6'b00_0010,//拉低总线 -- 时隙的开始
                S_SEND = 6'b00_0100,//发送     -- 15us内
                S_SAMP = 6'b00_1000,//采样     -- 在15us内
                S_RELE = 6'b01_0000,//释放     -- 时隙的恢复时间
                S_DONE = 6'b10_0000;

    parameter   TIME_1US  = 50,      //基本时间1us
                //主状态机延时
                TIME_RST  = 500,     //复位脉冲 500us
                TIME_REL  = 20,      //主机释放总线 20us
                TIME_PRE  = 200,     //主机接收存在脉冲 200us
                TIME_WAIT = 750000,  //主机发完温度转换命令 等待750ms
                //从状态机的延时
                TIME_LOW  = 2,       //主机拉低总线 2us
                TIME_RW   = 60,      //主机读、写1bit 60us
                TIME_REC  = 3;       //主机读写完1bit释放总线 3us
    
    localparam  CMD_ROMS  = 8'hCC,//跳过ROM指令
                CMD_CONT  = 8'h44,//温度转化
                CMD_RTMP  = 8'hBE;//读暂存器

//信号定义

    reg     [8:0]       m_state_c   ;//主状态机
    reg     [8:0]       m_state_n   ;

    reg     [5:0]       s_state_c   ;//从状态机
    reg     [5:0]       s_state_n   ;

    reg     [5:0]       cnt_1us     ;//1us计数器
    wire                add_cnt_1us ;
    wire                end_cnt_1us ;

    reg     [19:0]      cnt0        ;//复位脉冲、释放、存在脉冲、等待750ms
    wire                add_cnt0    ;
    wire                end_cnt0    ;
    reg     [19:0]      X           ;

    reg     [5:0]       cnt1        ;//计数从状态机每个状态多少us
    wire                add_cnt1    ;
    wire                end_cnt1    ;
    reg     [5:0]       Y           ;   

    reg     [4:0]       cnt_bit     ;
    wire                add_cnt_bit ;
    wire                end_cnt_bit ;

    reg                 slave_ack   ;//接收存在脉冲
    reg                 flag        ;//0:发温度转换命令 1:发温度读取命令
    reg     [7:0]       wr_data     ;
    reg     [15:0]      orign_data  ;//采样温度值寄存器
    
    reg     [10:0]      temp_data   ;
    wire    [23:0]      temp_data_w ;//组合逻辑计算实际温度值 十进制

    wire                m_idle2m_rest   ;
    wire                m_rest2m_rele   ;
    wire                m_rele2m_rack   ;
    wire                m_rack2m_roms   ;
    wire                m_roms2m_cont   ;
    wire                m_roms2m_rcmd   ;
    wire                m_cont2m_wait   ;
    wire                m_wait2m_rest   ;
    wire                m_rcmd2m_rtmp   ;
    wire                m_rtmp2m_idle   ;

    wire                s_idle2s_low    ;
    wire                s_low2s_send    ;
    wire                s_low2s_samp    ;
    wire                s_send2s_rele   ;
    wire                s_samp2s_rele   ;
    wire                s_rele2s_low    ;
    wire                s_rele2s_done   ;


//状态机设计
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            m_state_c <= M_IDLE;
        end 
        else begin 
            m_state_c <= m_state_n;
        end 
    end

    always @(*)begin 
        case(m_state_c)
            M_IDLE:begin 
                if(m_idle2m_rest)
                    m_state_n = M_REST;
                else 
                    m_state_n = m_state_c;
            end 
            M_REST:begin 
                if(m_rest2m_rele)
                    m_state_n = M_RELE;
                else 
                    m_state_n = m_state_c;
            end
            M_RELE:begin 
                if(m_rele2m_rack)
                    m_state_n = M_RACK;
                else 
                    m_state_n = m_state_c;
            end 
            M_RACK:begin 
                if(m_rack2m_roms)
                    m_state_n = M_ROMS;
                else 
                    m_state_n = m_state_c;
            end 
            M_ROMS:begin 
                if(m_roms2m_cont)
                    m_state_n = M_CONT;
                else if(m_roms2m_rcmd)
                    m_state_n = M_RCMD;
                else 
                    m_state_n = m_state_c;
            end 
            M_CONT:begin 
                if(m_cont2m_wait)
                    m_state_n = M_WAIT;
                else 
                    m_state_n = m_state_c;
            end 
            M_WAIT:begin 
                if(m_wait2m_rest)
                    m_state_n = M_REST;
                else 
                    m_state_n = m_state_c;
            end 
            M_RCMD:begin 
                if(m_rcmd2m_rtmp)
                    m_state_n = M_RTMP;
                else 
                    m_state_n = m_state_c;
            end 
            M_RTMP:begin 
                if(m_rtmp2m_idle)
                    m_state_n = M_IDLE;
                else 
                    m_state_n = m_state_c;
            end         
            default:m_state_n = M_IDLE;
        endcase 
    end

    assign m_idle2m_rest = m_state_c == M_IDLE && (1'b1);       //进入m_idle状态,下一个时钟周期上升沿将进入复位状态
    assign m_rest2m_rele = m_state_c == M_REST && (end_cnt0);
    assign m_rele2m_rack = m_state_c == M_RELE && (end_cnt0);
    assign m_rack2m_roms = m_state_c == M_RACK && (end_cnt0  && slave_ack == 1'b0);
    assign m_roms2m_cont = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b0);
    assign m_roms2m_rcmd = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b1);
    assign m_cont2m_wait = m_state_c == M_CONT && (s_state_c == S_DONE);
    assign m_wait2m_rest = m_state_c == M_WAIT && (end_cnt0);
    assign m_rcmd2m_rtmp = m_state_c == M_RCMD && (s_state_c == S_DONE);
    assign m_rtmp2m_idle = m_state_c == M_RTMP && (s_state_c == S_DONE);

//从状态机
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            s_state_c <= S_IDLE;
        end 
        else begin 
            s_state_c <= s_state_n;
        end 
    end

    always @(*)begin 
        case(s_state_c)
            S_IDLE:begin 
                if(s_idle2s_low)
                    s_state_n = S_LOW;
                else 
                    s_state_n = s_state_c;
            end  
            S_LOW :begin 
                if(s_low2s_send)
                    s_state_n = S_SEND;
                else if(s_low2s_samp)
                    s_state_n = S_SAMP;
                else 
                    s_state_n = s_state_c;
            end  
            S_SEND:begin 
                if(s_send2s_rele)
                    s_state_n = S_RELE;
                else 
                    s_state_n = s_state_c;
            end  
            S_SAMP:begin 
                if(s_samp2s_rele)
                    s_state_n = S_RELE;
                else 
                    s_state_n = s_state_c;
            end  
            S_RELE:begin 
                if(s_rele2s_low)
                    s_state_n = S_LOW;
                else if(s_rele2s_done)
                    s_state_n = S_DONE;
                else 
                    s_state_n = s_state_c;
            end  
            S_DONE:begin               //进入s_done状态,下一个时钟周期上升沿将回到初始状态
                    s_state_n = S_IDLE;
            end  
            default:s_state_n = S_IDLE;
        endcase 
    end

    assign s_idle2s_low  = s_state_c == S_IDLE && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);     

    assign s_low2s_send  = s_state_c == S_LOW  && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;   
    assign s_low2s_samp  = s_state_c == S_LOW  && (m_state_c == M_RTMP && end_cnt1); 

    assign s_send2s_rele = s_state_c == S_SEND && (end_cnt1);       
    assign s_samp2s_rele = s_state_c == S_SAMP && (end_cnt1);       
    assign s_rele2s_low  = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b0);   
    assign s_rele2s_done = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b1);       

//计数器
    always @(posedge clk or negedge rst_n)begin //1us计数
        if(!rst_n)begin
            cnt_1us <= 0;
        end 
        else if(add_cnt_1us)begin 
            if(end_cnt_1us)begin 
                cnt_1us <= 0;
            end
            else begin 
                cnt_1us <= cnt_1us + 1;
            end 
        end
    end 
    assign add_cnt_1us = m_state_c != M_IDLE;   //非IDLE状态持续计数
    assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US-1;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            cnt0 <= 0;
        end 
        else if(add_cnt0)begin 
            if(end_cnt0)begin 
                cnt0 <= 0;
            end
            else begin 
                cnt0 <= cnt0 + 1;
            end 
        end
    end                   
    assign add_cnt0 = (m_state_c == M_REST || m_state_c == M_RELE || m_state_c == M_RACK || m_state_c == M_WAIT) && end_cnt_1us;
    assign end_cnt0 = add_cnt0 && cnt0 == X-1;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            X <= 0;
        end 
        else if(m_state_c == M_REST)begin //       复位:500us (480us)
            X <= TIME_RST;
        end 
        else if(m_state_c == M_RELE)begin //       释放总线:20us (15-60us 内)
            X <= TIME_REL;
        end 
        else if(m_state_c == M_RACK)begin //       接收应答:200us (60-240us)
            X <= TIME_PRE;
        end 
        else if(m_state_c == M_WAIT)begin //       等待:750ms (等待转换完成)
            X <= TIME_WAIT;
        end
    end

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt1 <= 0;
        end 
        else if(add_cnt1)begin 
            if(end_cnt1)begin 
                cnt1 <= 0;
            end
            else begin 
                cnt1 <= cnt1 + 1;
            end 
        end
    end 
    assign add_cnt1 = (s_state_c == S_LOW || s_state_c == S_SEND || 
                       s_state_c == S_SAMP || s_state_c == S_RELE) && end_cnt_1us;
    assign end_cnt1 = add_cnt1 && cnt1 == Y-1;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            Y <= 0;
        end 
        else if(s_state_c == S_LOW)begin 
            Y <= TIME_LOW;                  //      主机拉低总线 2us    (大于1us)
        end
        else if(s_state_c == S_SEND || s_state_c == S_SAMP)begin 
            Y <= TIME_RW;                   //      主机读写1bit 60us   ()
        end 
        else begin                      
            Y <= TIME_REC;                  //      主机读写完1bit释放总线 3us (至少1us)
        end 
    end 

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_bit <= 0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1;
            end 
        end
    end 
    assign add_cnt_bit = s_state_c == S_RELE && end_cnt1; // 从机处于RELE状态,时隙恢复时间,表明1 bit 数据传输完成
    assign end_cnt_bit = add_cnt_bit && cnt_bit == ((m_state_c == M_RTMP)?16-1:8-1);

//slave_ack  采样传感器的存在脉冲
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            slave_ack <= 1'b1;
        end     //接收应答状态      计数器计到60us  进行采样
        else if(m_state_c == M_RACK && cnt0 == 60 && end_cnt_1us)begin 
            slave_ack <= dq_din;
        end 
    end 

    always @(posedge clk or negedge rst_n)begin //命令发送标志 (区分温度转换和温度读取命令)
        if(!rst_n)begin 
            flag <= 0;
        end 
        else if(m_wait2m_rest)begin 
            flag <= 1'b1;
        end 
        else if(m_rtmp2m_idle)begin 
            flag <= 1'b0;
        end 
    end

//输出信号
    //dq_out_en     
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            dq_out_en <= 0;
        end 
        else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin 
            dq_out_en <= 1'b1;      //输出 dq_out
        end 
        else if(m_rest2m_rele | s_send2s_rele | s_low2s_samp)begin  
            dq_out_en <= 1'b0;      //不输出 dq_out
        end 
    end 

    //dq_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dq_out <= 0;
        end 
        else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin 
            dq_out <= 1'b0;
        end 
        else if(s_low2s_send)begin 
            dq_out <= wr_data[cnt_bit];
        end 
    end

//wr_data   命令
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            wr_data <= 0;
        end 
        else if(m_rack2m_roms)begin 
            wr_data <= CMD_ROMS;
        end 
        else if(m_roms2m_cont)begin 
            wr_data <= CMD_CONT;
        end
        else if(m_roms2m_rcmd)begin 
            wr_data <= CMD_RTMP;
        end 
    end

//orign_data    温度采集
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            orign_data <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt1 == 12 && end_cnt_1us)begin //温度采集,在时隙起始后15us内采样
            orign_data[cnt_bit] <= dq_din; 
        end 
    end

//temp_data     温度判断
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            temp_data <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin 
            if(orign_data[15])                          //判断正负温度,决定是否需要做补码-原码转化
                temp_data <= ~orign_data[10:0] + 1'b1;  //负温 则取反加1 
            else 
                temp_data <= orign_data[10:0];          //正温
        end 
    end

/*
    实际的温度值为 temp_data * 0.0625;
    为了保留4位小数精度,将实际温度值放大了10000倍,
    即 temp_data * 625;

*/
    assign temp_data_w = temp_data * 625;

//temp_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_out <= 0;
        end 
        else if(m_state_c == M_RTMP && s_rele2s_done)begin 
            temp_out <= temp_data_w;
        end 
    end 

//temp_out_vld                                     指示温度值输出有效
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_out_vld <= 0;
        end 
        else begin 
            temp_out_vld <= m_state_c == M_RTMP && s_rele2s_done;
        end 
    end 

//temp_sign                                       (输出至数码管显示时使用)
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_sign <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin 
            temp_sign <= orign_data[15];
        end 
    end 

endmodule

control:

module control( 
    input				clk		    ,
    input				rst_n	    ,

    input				din_sign    ,
    input		[23:0]  din		    ,//输入24位十进制温度值(*10000)
    input		    	din_vld 	,

    output  wire        dout_sign   ,
    output	wire[23:0]	dout	    ,//输出每位数码管对应显示的值
    output	reg	    	dout_vld	
);	
             
    //中间信号定义		 
    reg		[23:0]	din_r       ;
    reg             din_vld_r0  ;
    reg             din_vld_r1  ;

    wire    [7:0]   tmp_int_w   ;//整数部分
    reg     [7:0]   tmp_int_r   ;
    
    wire	[3:0]	tmp_int_w2  ;
    wire	[3:0]	tmp_int_w1  ;
    wire	[3:0]	tmp_int_w0  ;

    reg 	[3:0]	tmp_int_r2  ;
    reg 	[3:0]	tmp_int_r1  ;
    reg 	[3:0]	tmp_int_r0  ; 

    wire    [15:0]  tmp_dot_w   ;//小数部分
    reg     [15:0]  tmp_dot_r   ;

    wire	[3:0]	tmp_dot_w3  ;
    wire	[3:0]	tmp_dot_w0  ;
    wire	[3:0]	tmp_dot_w1  ;
    wire	[3:0]	tmp_dot_w2  ;

    reg 	[3:0]	tmp_dot_r3  ;
    reg 	[3:0]	tmp_dot_r0  ;
    reg 	[3:0]	tmp_dot_r1  ;
    reg 	[3:0]	tmp_dot_r2  ;

//din_r 
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            din_r <= 0;
        end 
        else if(din_vld)begin 
            din_r <= din;
        end 
    end

//din_vld_r0  din_vld_r1
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            din_vld_r0 <= 0;
            din_vld_r1 <= 0;
        end 
        else begin 
            din_vld_r0 <= din_vld;
            din_vld_r1 <= din_vld_r0;
        end 
    end

    assign tmp_int_w = din_r/10000;//拆分整数部分及小数部分
    assign tmp_dot_w = din_r%10000;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tmp_int_r <= 0;
        end 
        else if(din_vld_r0)begin 
            tmp_int_r <= tmp_int_w;
            tmp_dot_r <= tmp_dot_w;
        end 
    end

    assign tmp_int_w2 = tmp_int_r/100;//百位
    assign tmp_int_w1 = tmp_int_r/10%10 ;//十位
    assign tmp_int_w0 = tmp_int_r%10;//个位

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tmp_int_r2 <= 0;
            tmp_int_r1 <= 0;
            tmp_int_r0 <= 0;
        end 
        else if(din_vld_r1)begin 
            tmp_int_r2 <= tmp_int_w2;
            tmp_int_r1 <= tmp_int_w1;
            tmp_int_r0 <= tmp_int_w0;
        end 
    end

    assign tmp_dot_w0 = tmp_dot_r/1000;
    assign tmp_dot_w1 = tmp_dot_r/100%10;
    assign tmp_dot_w2 = tmp_dot_r/10%10;
    assign tmp_dot_w3 = tmp_dot_r%10;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tmp_dot_r0 <= 0;
            tmp_dot_r1 <= 0;
            tmp_dot_r2 <= 0;
            tmp_dot_r3 <= 0;
        end 
        else if(din_vld_r1)begin 
            tmp_dot_r0 <= tmp_dot_w0;
            tmp_dot_r1 <= tmp_dot_w1;
            tmp_dot_r2 <= tmp_dot_w2;
            tmp_dot_r3 <= tmp_dot_w3;
        end 
    end

    assign dout = {tmp_int_r1,tmp_int_r0,tmp_dot_r0,tmp_dot_r1,tmp_dot_r2,tmp_dot_r3}; 

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dout_vld <= 1'b0;
        end 
        else begin 
            dout_vld <= din_vld_r1; 
        end 
    end

    assign dout_sign = din_sign;

endmodule

seg_driver:

module seg_driver(
    input                    clk     ,//时钟信号
    input                    rst_n   ,//复位信号
    input                    din_sign,
    input        [23:0]      din     ,
    input                    din_vld ,

    output  reg  [5:0]       sel     ,
    output  reg  [7:0]       dig     
);

//参数定义
parameter       TIME_1MS = 25_000,

                ZERO     = 7'b100_0000,
                ONE      = 7'b111_1001,
                TWO      = 7'b010_0100,
                THREE    = 7'b011_0000,
                FOUR     = 7'b001_1001,
                FIVE     = 7'b001_0010,
                SIX      = 7'b000_0010,
                SEVEN    = 7'b111_1000,
                EIGHT    = 7'b000_0000,
                NINE     = 7'b001_0000,
                P        = 7'b000_1111,
                N        = 7'b011_1111;

//信号定义
reg     [19:0]      cnt_1ms;//扫描频率计数器
wire                add_cnt_1ms;
wire                end_cnt_1ms;

reg     [3 :0]      disp_num;
reg                 dot;

//数码管扫描频率计数
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_1ms <= 0;
   end
   else if(add_cnt_1ms) begin
      if(end_cnt_1ms)begin
         cnt_1ms <= 0;
      end
      else begin
         cnt_1ms <= cnt_1ms + 1;
      end
   end

end
assign add_cnt_1ms = 1'b1;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == TIME_1MS - 1;


//seg_sel 数码管片选信号
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      sel <= 6'b111110;
   end
   else if(end_cnt_1ms) begin
      sel <= {sel[4:0],sel[5]};
   end
end

//译码
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      disp_num  <= 0;
   end
   else begin
      case(sel)
         6'b011111:begin disp_num <= din_sign?4'ha:4'hb;dot <= 1; end
         6'b101111:begin disp_num <= din[23:20];dot <= 1;end
         6'b110111:begin disp_num <= din[19:16];dot <= 0;end
         6'b111011:begin disp_num <= din[15:12];dot <= 1;end
         6'b111101:begin disp_num <= din[11:8] ;dot <= 1;end
         6'b111110:begin disp_num <= din[7 :4] ;dot <= 1;end
         default  :begin disp_num <= 4'hF ;end
      endcase
   end
end 


//segment 段选译码
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      dig <= 8'hff;
   end
   else begin//显示小数点
      case(disp_num)
         4'h0:dig <= {dot,ZERO }  ;
         4'h1:dig <= {dot,ONE  }  ;
         4'h2:dig <= {dot,TWO  }  ;
         4'h3:dig <= {dot,THREE}  ;
         4'h4:dig <= {dot,FOUR }  ;
         4'h5:dig <= {dot,FIVE }  ;
         4'h6:dig <= {dot,SIX  }  ;
         4'h7:dig <= {dot,SEVEN}  ;
         4'h8:dig <= {dot,EIGHT}  ;
         4'h9:dig <= {dot,NINE }  ;
         4'ha:dig <= {dot,N    }  ;
         4'hb:dig <= {dot,P    }  ;
         default:dig <= 8'hff ;
      endcase
   end
end


endmodule

temp_top

module temp_top (
    input        clk            ,
    input        rst_n           ,
    inout        dq             ,

    output [5:0] sel            ,
    output [7:0] seg
);

//信号定义
    wire            dq_in       ;
    wire            dq_out      ;
    wire            dq_out_en   ;
    wire            temp_sign   ;
    wire    [23:0]  temp_out    ;
    wire            temp_out_vld;
    wire            dout_sign   ;
    wire    [23:0]  dout        ;
    wire            dout_vld    ;

    assign dq = dq_out_en?dq_out:1'bz;
    assign dq_in = dq;


//模块例化
ds18_driver u_ds18_driver(
    .clk            (clk            ),
    .rst_n          (rst_n          ),
    .dq_din          (dq_in          ),//dq总线DS18B20输出
    .dq_out         (dq_out         ),//dq总线FPGA输出,DS18B20输入
    .dq_out_en      (dq_out_en      ),//dq总线输出使能控制信号
    .temp_sign      (temp_sign      ),//温度的正负
    .temp_out       (temp_out       ),//输出十进制温度
    .temp_out_vld   (temp_out_vld   ) //温度采集数据有效
);

control u_control(
    .clk            (clk            ),
    .rst_n          (rst_n          ),
    .din_sign       (temp_sign      ),
    .din            (temp_out       ),
    .din_vld        (temp_out_vld   ),
    .dout_sign      (dout_sign      ), 
    .dout           (dout           ),//输出温度值数码管对应位置的bcd码
    .dout_vld       (dout_vld       )
);

seg_driver seg_driver(
    .clk            (clk            ),
    .rst_n          (rst_n          ),
    .din_sign       (temp_sign      ),
    .din            (dout           ),
    .din_vld        (dout_vld       ),
    .sel            (sel            ),
    .dig            (seg            )
);
endmodule //temp_top

四 上板验证

五 总结

多调试,多动手,画好时序图,画好逻辑图,注意状态转移条件。

猜你喜欢

转载自blog.csdn.net/qq_52445967/article/details/125209497