十七、DS18B20温度传感器(1)

1、ds18b20_driver.v

/*
*
*@Author: X-Z
*@Date:2023-02-21 11:15:07
*@Function:DS18B20驱动模块,实现主机FPGA与从机ds18b20之间的单总线通信
*/
module ds18b20_driver (
    input                   clk         ,
    input                   rst_n       ,

    input                   dq_in       ,//数据输入端口
    output   reg            dq_out      ,//数据输出端口
    output   reg            dq_out_en   ,//数据输出使能端口

    output   reg   [23:0]   temp_out    ,
    output   reg            temp_out_vld,
    output   reg            temp_sign    //输出温度正负  
);

//独热码对状态进行编码
//主状态机
localparam M_IDLE = 9'b000_000_001,//空闲状态,等待开始通信
           M_RST  = 9'b000_000_010,//发送复位脉冲
           M_REL  = 9'b000_000_100,//释放总线
           M_RACK = 9'b000_001_000,//接收存在脉冲
           M_RSKP = 9'b000_010_000,//发送跳过ROM命令
           M_SCON = 9'b000_100_000,//发送温度转换命令
           M_WAIT = 9'b001_000_000,//等待750ms
           M_SRTM = 9'b010_000_000,//发送读取温度指令
           M_RTMP = 9'b100_000_000;//读取温度值

//从状态机
localparam S_IDLE = 6'b000_001,//空闲状态,等待传输请求
           S_LOW  = 6'b000_010,//发送数据前先拉低至少1us
           S_SEND = 6'b000_100,//发送1bit数据
           S_SAMP = 6'b001_000,//接收1bit数据
           S_RELE = 6'b010_000,//释放总线
           S_DONE = 6'b100_000;//发送/接收一次数据完成

//参数定义
parameter   TIME_1US    = 50         ,//1us计数器
            TIME_RST    = 500        ,//复位480us
            TIME_RELE   = 20         ,//主机释放总线
            TIME_PRE    = 200        ,//从机发送存在脉冲
            TIME_WAIT   = 750_000    ,
            TIME_LOW    = 2          ,//主机拉低总线
            TIME_RW     = 60         ,//读写1bit
            TIME_REC    = 3          ;//释放

//命令定义
localparam  CMD_SROM    = 8'hCC      ,//跳过ROM指令
            CMD_CONT    = 8'h44      ,//温度转换
            CMD_RTMP    = 8'hBE      ;//读暂存器

//fsm状态定义
    reg     [8:0]       m_state_c    ;
    reg     [8:0]       m_state_n    ;

    reg     [5:0]       s_state_c    ;
    reg     [5:0]       s_state_n    ;

//主机状态转移条件定义
    wire     m_idle_2_m_rst  ; 
    wire     m_rst_2_m_rel   ; 
    wire     m_rel_2_m_rack  ;
    wire     m_rack_2_m_rskp ; 
    wire     m_rskp_2_m_scon ; 
    wire     m_rskp_2_m_srtm ; 
    wire     m_scon_2_m_wait ; 
    wire     m_wait_2_m_rst  ; 
    wire     m_srtm_2_m_rtmp ; 
    wire     m_rtmp_2_m_idle ; 

//从状态转移条件定义
    wire     s_idle_2_s_low  ; 
    wire     s_low_2_s_send  ; 
    wire     s_low_2_s_samp  ; 
    wire     s_send_2_s_rele ; 
    wire     s_rele_2_s_low  ; 
    wire     s_rele_2_s_done ; 
    wire     s_samp_2_s_rele ; 

//采样存在脉冲
    reg      dq_samp         ;//60-240us的低电平,我们在200us处采样

//进入温度转换还是进入读取温度寄存器标志信号定义
    reg      cmd_flag        ;//取0时进入温度转换指令,为1时进入都暂存器状态

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

//主状态机计数器
    reg     [19:0]      m_cnt       ;
    wire                add_m_cnt   ;
    wire                end_m_cnt   ;
    reg     [19:0]      M_X         ;//存放不同状态下计数器的计数最大值

//从状态机计数器
    reg     [5:0]       s_cnt       ;
    wire                add_s_cnt   ;
    wire                end_s_cnt   ;
    reg     [5:0]       S_X         ;

//比特计数器参数定义
    reg     [3:0]       cnt_bit     ;
    wire                add_cnt_bit ;
    wire                end_cnt_bit ;

//发送或接收bit数定义
    reg     [3:0]       S_BIT       ;

//暂存指令
    reg     [7:0]       cmd_r       ;    

//暂存读取的16位暂存器值
    reg     [15:0]      temp_out_r  ;

//暂存经过正负处理后的温度数据
    reg     [10:0]      temp_data   ;//除符号扩展位外的有效温度位

//暂存实际转换后的温度值
    wire    [23:0]      temp_data_r ;//将temp_data*0.0625

//主机采样存在脉冲
    //主状态机处于响应状态并且计数器计满40us时采样,低电平的时间为60-240us
    //assign  dq_samp = (m_state_c == M_RACK && m_cnt == 20'd40 && end_cnt_1us) ?  dq_in : 1'b1;
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            dq_samp <= 1;
        else if(m_state_c == M_RACK && m_cnt == 40 && end_cnt_1us)
            dq_samp <= dq_in; 
    end

//发送或接收的最大比特数赋值
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            S_BIT <= 0;
        end
        else begin
            case(m_state_c)
                M_RSKP : S_BIT = 4'd7;
                M_SCON : S_BIT = 4'd7;
                M_SRTM : S_BIT = 4'd7;
                M_RTMP : S_BIT = 4'd15;//接收温度值,两个字节
                default:;
            endcase
        end
    end

//温度转换/读取温度暂存器标志信号赋值
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cmd_flag <= 1'b0;//初始时为温度转换指令
        else if(m_wait_2_m_rst)//当它进入M_WAIT说明前面进行了温度转换,则下一次在跳过ROM后进入读暂存器状态
            cmd_flag <= 1'b1;
        else if(m_rtmp_2_m_idle)//因为第一次进入温度转换并完成后直接进入了M_REST状态,所以可以用M_IDLE状态时赋值为0,代表一次温度转换并读取完成
            cmd_flag <= 1'b0; 
    end

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

    //第二段
    always @(*)begin
        case(m_state_c)
            M_IDLE : begin
                if(m_idle_2_m_rst)begin
                    m_state_n = M_RST;
                end
                else begin
                    m_state_n = m_state_c;
                end
            end 
            M_RST : begin
                if(m_rst_2_m_rel)begin
                    m_state_n = M_REL; 
                end
                else begin
                    m_state_n = m_state_c;
                end
            end
            M_REL : begin
                if(m_rel_2_m_rack)begin
                    m_state_n = M_RACK;
                end
                else begin
                    m_state_n = m_state_c;
                end
            end
            M_RACK : begin
                if(m_rack_2_m_rskp)begin
                    m_state_n = M_RSKP;
                end
                else begin
                    m_state_n = m_state_c;
                end
            end
            M_RSKP : begin
                if(m_rskp_2_m_scon)begin
                    m_state_n = M_SCON;
                end
                else if(m_rskp_2_m_srtm)begin
                    m_state_n = M_SRTM;
                end
                else begin
                    m_state_n = m_state_c;
                end
            end
            M_SCON : begin
                if(m_scon_2_m_wait)begin
                    m_state_n = M_WAIT;
                end
                else begin
                    m_state_n = m_state_c;
                end
            end
            M_WAIT : begin
                if(m_wait_2_m_rst)begin
                    m_state_n = M_RST;
                end
                else begin
                    m_state_n = m_state_c;
                end
            end
            M_SRTM : begin
                if(m_srtm_2_m_rtmp)begin
                    m_state_n = M_RTMP;
                end
                else begin
                    m_state_n = m_state_c;
                end
            end
            M_RTMP : begin
                if(m_rtmp_2_m_idle)begin
                    m_state_n = M_IDLE;
                end
                else begin
                    m_state_n = m_state_c;
                end
            end
            default : m_state_n = M_IDLE;
        endcase
    end

    assign  m_idle_2_m_rst   =  m_state_c == M_IDLE && 1'b1;
    assign  m_rst_2_m_rel    =  m_state_c == M_RST  && end_m_cnt;
    assign  m_rel_2_m_rack   =  m_state_c == M_REL  && end_m_cnt;
    assign  m_rack_2_m_rskp  =  m_state_c == M_RACK &&( dq_samp == 0) && end_m_cnt;                             //检测到存在脉冲
    assign  m_rskp_2_m_scon  =  m_state_c == M_RSKP && (s_state_c == S_DONE) && (cmd_flag == 1'b0);//主状态机处在跳过ROM状态,并且从状态机发送完了该指令,cmd_flag=0表示进入温度转换
    assign  m_rskp_2_m_srtm  =  m_state_c == M_RSKP && (s_state_c == S_DONE) && (cmd_flag == 1'b1);//发送完命令并且第二次进入跳过ROM   
    assign  m_scon_2_m_wait  =  m_state_c == M_SCON && (s_state_c == S_DONE);                      //温度转换命令发送完
    assign  m_wait_2_m_rst   =  m_state_c == M_WAIT &&  end_m_cnt;                                 //计数750ms结束
    assign  m_srtm_2_m_rtmp  =  m_state_c == M_SRTM && (s_state_c == S_DONE);                      //读取温度指令发送完
    assign  m_rtmp_2_m_idle  =  m_state_c == M_RTMP && (s_state_c == S_DONE);                      //读取2字节数据完成

//从状态机
    //第一段时序逻辑描述状态转移
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            s_state_c <= S_IDLE ;
        else 
            s_state_c <= s_state_n; //现态跟随次态
    end
        
    //第二段组合逻辑,描述状态跳转条件
    always@(*)begin
        case(s_state_c)
            S_IDLE : begin
                if(s_idle_2_s_low)begin
                    s_state_n = S_LOW;
                end 
                else begin
                    s_state_n = s_state_c;
                end
            end
            S_LOW   : begin
                if(s_low_2_s_send)begin
                    s_state_n = S_SEND;
                end
                else if(s_low_2_s_samp)begin
                    s_state_n = S_SAMP;
                end
                else begin
                    s_state_n = s_state_c;
                end
            end
            S_SEND  : begin
                if(s_send_2_s_rele)begin
                    s_state_n = S_RELE;
                end
                else begin
                    s_state_n = s_state_c;
                end
            end
            S_RELE  : begin
                if(s_rele_2_s_low)begin
                    s_state_n = S_LOW;
                end
                else if(s_rele_2_s_done)begin
                    s_state_n = S_DONE;
                end
                else begin
                    s_state_n = s_state_c;
                end
            end
            S_SAMP : begin
                if(s_samp_2_s_rele)begin
                    s_state_n = S_RELE;
                end
                else begin
                    s_state_n = s_state_c;
                end
            end
            S_DONE : begin
                s_state_n = S_IDLE;
            end
                default : s_state_n = S_IDLE;
        endcase
    end
        
//给转移条件赋值
    //主状态机处在发送跳过ROM,发送温度转换,发送读暂存器,接收温度值时
    assign  s_idle_2_s_low  = s_state_c == S_IDLE && (m_state_c == M_RSKP || m_state_c == M_SCON || m_state_c == M_SRTM || m_state_c == M_RTMP);
    //跳过ROM,开始温度转换,发送读暂存器都是主机发送指令,并且拉低的2us计数结束
    assign  s_low_2_s_send  = s_state_c == S_LOW  && (end_s_cnt && (m_state_c == M_RSKP || m_state_c == M_SCON || m_state_c == M_SRTM));
    assign  s_low_2_s_samp  = s_state_c == S_LOW  && (end_s_cnt && (m_state_c == M_RTMP));//接收温度值时,主机开启的是接收时序
    assign  s_send_2_s_rele = s_state_c == S_SEND && end_s_cnt;                         //发送1bit数据60us计数完成
    assign  s_rele_2_s_low  = s_state_c == S_RELE &&( (~end_cnt_bit) && end_s_cnt);                    //8bit数据未发完或16bit温度数据未接收完
    assign  s_rele_2_s_done = s_state_c == S_RELE &&  (end_cnt_bit && end_s_cnt);                      //发送完8bit数据指令或接收完16bit温度值
    assign  s_samp_2_s_rele = s_state_c == S_SAMP && (end_s_cnt);                    //接收完1bit数据
    
//1us计数器模块
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_1us <= 1'd0;//复位初始化计数器值为0
        end
        else if(add_cnt_1us)begin//满足计数器开启条件
            if(end_cnt_1us)begin//满足计数器结束条件
                cnt_1us <= 1'd0;//计数值清零
            end
            else begin//不满足结束条件,计数器加1
                cnt_1us <= cnt_1us + 1'b1;
            end
        end
    end
        
    assign add_cnt_1us = (m_state_c != M_IDLE) || (s_state_c != S_IDLE); //只要主状态机或从状态机不处于IDLE状态就开启1us基准计数器
    assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US - 1;        //计数器结束计数条件

//根据主状态机所处的不同状态对M_X赋值
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            M_X <= 0;
        end
        else begin
            case(m_state_c)
                M_RST   : M_X <= TIME_RST ;//主机发送复位
                M_REL   : M_X <= TIME_RELE;//主机释放总线时间
                M_RACK  : M_X <= TIME_PRE ;//从机响应存在脉冲
                M_WAIT  : M_X <= TIME_WAIT;//发送温度转换命令后需要等待750ms等待温度转换完成
                default :;
            endcase
        end
    end

//根据从状态机所处的不同状态对S_X赋值
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            S_X <= 0;
        end
        else begin
            case(s_state_c)
                S_LOW   : S_X <= TIME_LOW;//发送或接收1bit数据前主机拉低2us
                S_SEND  : S_X <= TIME_RW ;//发送或接收1bit数据所需时间
                S_SAMP  : S_X <= TIME_RW ;
                S_RELE  : S_X <= TIME_REC;//发送完1bit数据后释放总线
                default : ;
            endcase
        end 
    end

//主状态机计数器
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            m_cnt <= 1'd0;//复位初始化计数器值为0
        end
        else if(add_m_cnt)begin//满足计数器开启条件
            if(end_m_cnt)begin//满足计数器结束条件
                m_cnt <= 1'd0;//计数值清零
            end
            else begin//不满足结束条件,计数器加1
                m_cnt <= m_cnt + 1'b1;
            end
        end
    end
        
    assign add_m_cnt = end_cnt_1us && (m_state_c == M_RST || m_state_c == M_REL || m_state_c == M_RACK || m_state_c == M_WAIT);//计数器开始计数条件
    assign end_m_cnt = add_m_cnt && m_cnt == M_X - 1;//计数器结束计数条件

//从状态机计数器
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            s_cnt <= 1'd0;//复位初始化计数器值为0
        end
        else if(add_s_cnt)begin//满足计数器开启条件
            if(end_s_cnt)begin//满足计数器结束条件
                s_cnt <= 1'd0;//计数值清零
            end
            else begin//不满足结束条件,计数器加1
                s_cnt <= s_cnt + 1'b1;
            end
        end
    end
        
    assign add_s_cnt = end_cnt_1us && (s_state_c == S_LOW || s_state_c == S_SEND || s_state_c == S_SAMP || s_state_c == S_RELE);//计数器开始计数条件
    assign end_s_cnt = add_s_cnt && s_cnt == S_X - 1;//计数器结束计数条件

//比特计数器
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_bit <= 1'd0;//复位初始化计数器值为0
        end
        else if(add_cnt_bit)begin//满足计数器开启条件
            if(end_cnt_bit)begin//满足计数器结束条件
                cnt_bit <= 1'd0;//计数值清零
            end
            else begin//不满足结束条件,计数器加1
                cnt_bit <= cnt_bit + 1'b1;
            end
        end
    end
        
    assign add_cnt_bit = (s_state_c == S_RELE) && end_s_cnt;//从机释放一次总线代表发送/接收1bit数据完成
    assign end_cnt_bit = add_cnt_bit && cnt_bit == S_BIT;//计数器结束计数条件

//dq_out_en
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            dq_out_en <= 1'b0;
        end
        else if(m_idle_2_m_rst || m_wait_2_m_rst || s_idle_2_s_low || s_rele_2_s_low)begin//将总线拉低输出需要使能
            dq_out_en <= 1'b1;
        end
        else if(m_rst_2_m_rel || s_low_2_s_samp || s_send_2_s_rele)begin//释放总线将使能信号拉低,读取温度值时也将使能信号拉低
            dq_out_en <= 1'b0;   
        end
    end

//cmd_r更据不同的状态选择不同的指令
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cmd_r <= 1'b0;
        end 
        else begin
            case(m_state_c)
                M_RSKP : cmd_r <= CMD_SROM;//跳过ROM
                M_SCON : cmd_r <= CMD_CONT;//温度转换
                M_SRTM : cmd_r <= CMD_RTMP;//读取暂存器
                default :;
            endcase
        end
    end

//dq_out
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            dq_out <= 1'b0;
        end
        else if(m_idle_2_m_rst || m_wait_2_m_rst || s_idle_2_s_low || s_rele_2_s_low)begin//复位,等待,发送或接收前将总线拉低
            dq_out <= 1'b0;
        end
        else if(s_low_2_s_send)begin
            dq_out <= cmd_r[cnt_bit];//每次发送1bit的数据,先发低位
        end
    end

//读取温度暂存器temp_out_r
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            temp_out_r <= 0;
        end//在15us处采样,因为前面拉低总线时已经2us了,所以在s_cnt=12时采样刚好是15us,0-12是13us
        else if(s_state_c == S_SAMP && s_cnt == 12 && end_cnt_1us)begin//从状态机处于读取状态下,在15us内进行采样,读时隙的有效时长为15us
            temp_out_r[cnt_bit] <= dq_in;
        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_samp_2_s_rele)begin//16bit温度数据读取完
            if(temp_out_r[15])begin                                         //判读正负(0为正1为负),确定是否需要补码转原码
                temp_data <= ~temp_out_r[10:0] + 1'b1;                     //温度为负时将补码转为原码
            end
            else begin//正温度
                temp_data <= temp_out_r[10:0];//只要有效位
            end
        end
    end

//输出转换后的温度值
    assign temp_data_r = temp_data * 625;//应该*0.0625,为了保留小数点后4位扩大了10000倍

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            temp_out <= 0;
        end
        else if(m_rtmp_2_m_idle)begin//一次温度转换完将温度值输出
            temp_out <= temp_data_r;
        end
    end

//temp_out_vld输出温度有效标志信号
    always @(posedge clk or negedge rst_n)begin//数据有效后拉高一个时钟周期
        if(!rst_n)begin
            temp_out_vld <= 1'b0;
        end
        else if(m_rtmp_2_m_idle)begin
            temp_out_vld <= 1'b1;
        end
        else begin
            temp_out_vld <= 1'b0;
        end
    end 

//temp_sign ---输出温度的正负
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            temp_sign <= 1'b0;
        end
        else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp_2_s_rele)begin
            temp_sign <= temp_out_r[15];
        end
    end

    
endmodule

2、control.v

/*
*
*@Author: X-Z
*@Date:2023-02-21 11:17:32
*@Function:对驱动模块传进来的数据进行处理,然后送到数码管显示
*/

module control(
    input                           clk         ,
    input                           rst_n       ,
    input                           sign_in     ,
    input           [23:0]          t_data      ,
    input                           t_data_vld  ,

    output                          sign_out    ,
    output    wire  [23:0]          t_out      
);

//定义信号存储数据的每一位
    wire    [3:0]          temp_r0;//最高位
    wire    [3:0]          temp_r1;    
    wire    [3:0]          temp_r2;
    wire    [3:0]          temp_r3;
    wire    [3:0]          temp_r4;
    wire    [3:0]          temp_r5;//最低位

//暂存传进来的温度数据
    reg     [23:0]         t_data_r;

//t_data_r
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            t_data_r <= 24'd0;
        end
        else if(t_data_vld)begin
            t_data_r <= t_data;
        end
    end

//输出符号赋值
    assign sign_out = sign_in;

//提取出数据的每一位
    assign temp_r0 =  t_data_r/1000_00;
    assign temp_r1 = (t_data_r%1000_00)/1000_0;
    assign temp_r2 = (t_data_r%1000_0)/1000;
    assign temp_r3 = (t_data_r%1000)/100;
    assign temp_r4 = (t_data_r%100)/10; 
    assign temp_r5 = t_data_r%10;

//输出24位宽的数据赋值
    //assign t_out = {temp_r0,temp_r1,temp_r2,temp_r3,temp_r4,temp_r5};
    assign t_out = {4'hf,temp_r0,temp_r1,temp_r2,temp_r3,temp_r4};

endmodule

3、dynamic_dig.v

/*
*
*@Author: X-Z
*@Date:2023-02-08 18:46:20
*@Function:数码管驱动
*/

module dynamic_dig (
    input       wire        clk     ,
    input       wire        rst_n   ,
    input       wire [23:0] data    ,//输入24位宽的数据
    input                   sign    ,

    output      reg  [5:0]  sel     ,//位选
    output      reg  [7:0]  seg     //段选
);

    //参数定义共阳极
    parameter TIME_1MS = 49_999,
    //1ms扫描一个数码管
              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,
              A        = 7'b000_1000,
              B        = 7'b000_0011,
              C        = 7'b010_0011,
              D        = 7'b110_0011,
              E        = 7'b111_1110,//只亮g段
              F        = 7'b111_1111;//全灭,4'hf  : seg = 8'b1000_1110;

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

    reg   [3:0]      disp_num   ;//暂存数码管要显示的数值

    wire  [3:0]      sign_r;//处理温度符号
    reg              dip   ;//显示小数点

    assign sign_r = sign ? 4'hE : 4'hF;


    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt_1ms <= 1'b0;
        else if(add_cnt_1ms)begin
            if(end_cnt_1ms)
                cnt_1ms <= 1'b0;
            else 
                cnt_1ms <= cnt_1ms + 1'b1;
        end
    end 

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

    //数码管位选信号
    //时序电路
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            sel <= 6'b111_110;//复位初始化
        end
        else if(end_cnt_1ms)begin
            sel <= {sel[4:0],sel[5]};
        end
    end

    always@(*)begin
            case(sel)
                //6'b111_110: disp_num <= data[23:20];
                6'b111_110: dip <= 1'b1;//显示符号
                6'b111_101: dip <= 1'b1;
                6'b111_011: dip <= 1'b0;
                6'b110_111: dip <= 1'b1;
                6'b101_111: dip <= 1'b1;
                6'b011_111: dip <= 1'b1;
                default   : dip <= 1'b1;
            endcase
    end

    //每片数码管将要显示的数值
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            disp_num <= 4'd0;
        end
        else begin
            case(sel)
                //6'b111_110: disp_num <= data[23:20];
                6'b111_110: disp_num <= sign_r     ;//显示符号
                6'b111_101: disp_num <= data[19:16];
                6'b111_011: disp_num <= data[15:12];
                6'b110_111: disp_num <= data[11:8] ;
                6'b101_111: disp_num <= data[7:4]  ;
                6'b011_111: disp_num <= data[3:0]  ;
                default   : disp_num <= 4'd0       ;
            endcase
        end
    end

    //seg 段选信号译码
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            seg <= {1'b0,ZERO};//复位数码管显示字符
        end
        else begin//不需要显示小数点
            case(disp_num)
                0 :seg <= {dip,ZERO  }   ;
                1 :seg <= {dip,ONE   }   ;
                2 :seg <= {dip,TWO   }   ;
                3 :seg <= {dip,THREE }   ;
                4 :seg <= {dip,FOUR  }   ;
                5 :seg <= {dip,FIVE  }   ;
                6 :seg <= {dip,SIX   }   ;
                7 :seg <= {dip,SEVEN }   ;
                8 :seg <= {dip,EIGHT }   ;
                9 :seg <= {dip,NINE  }   ; 
                10:seg <= {1'b1, A    }   ;
                11:seg <= {1'b1, B    }   ;
                12:seg <= {1'b1, C    }   ;
                13:seg <= {1'b1, D    }   ;
                14:seg <= {1'b1, E    }   ;
                15:seg <= {1'b1, F    }   ;
                //全灭
                default :seg <= {1'b1,F}  ;
            endcase
        end
    end    
endmodule

4、temp_detect.v

module temp_detect(
    input           clk     ,
    input           rst_n   ,

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

//中间变量定义
    wire            dq_in       ;
    wire            dq_out      ;
    wire            out_en      ;

    wire            sign        ;
    wire  [23:0]    t_out       ;
    wire            t_out_vld   ;

    wire  [23:0]    data        ;
    wire            sign_out    ;

//inout口赋值
    assign  dq_in = dq;
    assign  dq    = out_en == 1'b1 ? dq_out : 1'bz;//输出使能无效时,输出为高阻态,可以输入

//ds18b20驱动模块例化
    ds18b20_driver  u_ds18b20_driver(
        .clk            (clk        ),
        .rst_n          (rst_n      ),
        .dq_in          (dq_in      ),

        .dq_out         (dq_out     ),
        .dq_out_en      (out_en     ),

        .temp_sign      (sign       ),
        .temp_out       (t_out      ),
        .temp_out_vld   (t_out_vld  )
    );

//控制模块例化
    control   u_control(
        .clk        (clk        ),
        .rst_n      (rst_n      ),
        .sign_in    (sign       ),
        .t_data     (t_out      ),
        .t_data_vld (t_out_vld  ),

        .sign_out   (sign_out   ),
        .t_out      (data       )
    );

//数码管驱动模块例化
    dynamic_dig     u_dynamic_dig(
        .clk        (clk        ),
        .rst_n      (rst_n      ),
        .data       (data       ),
        .sign       (sign_out   ),

        .sel        (sel        ),
        .seg        (seg        )
    );

endmodule 

5、ds18b20_driver_tb.v

`timescale 1ns/1ns

module ds18b20_driver_tb();

    defparam u_ds18b20_driver.TIME_RST  = 200,
             u_ds18b20_driver.TIME_PRE  = 100,
             u_ds18b20_driver.TIME_WAIT = 750;

//信号定义
    reg                 clk             ;
    reg                 rst_n           ;

    reg                 dq_in           ;
    wire                dq_out          ;
    wire                dq_out_en       ;

    wire   [23:0]       temp_out        ;
    wire                temp_out_vld    ;
    wire                temp_sign       ;

    parameter  CYCLE = 20;

    integer i=0;

    initial begin
        clk   = 1'b1;
        rst_n = 1'b0;
        dq_in = 0;
        #2;
        #(CYCLE*20)
        rst_n = 1'b1;
        repeat(5)begin
            for(i=0;i<500000;i=i+1)begin
                dq_in = {$random};
                #(20*CYCLE);
            end
            #(20*CYCLE);
        end
        $stop;
    end

    always #(CYCLE/2) clk = ~clk;

    ds18b20_driver u_ds18b20_driver(
        .clk         (clk         ),
        .rst_n       (rst_n       ),
 
        .dq_in       (dq_in       ),//数据输入端口
        .dq_out      (dq_out      ),//数据输出端口
        .dq_out_en   (dq_out_en   ),//数据输出使能端口

        .temp_out    (temp_out    ),
        .temp_out_vld(temp_out_vld),
        .temp_sign   (temp_sign   )    //输出温度正负  
    );

endmodule 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值