FPGA强化(6):数字温湿度传感器

第38讲:DS18B20数字温度传感器

理论部分

DS18B20是DALLAS半导体公司生产的单总线数字温度传感器,其输出的是数字信号,具有体积小,功耗低,抗干扰能力强,精度高的特点。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



设计与实现

在这里插入图片描述

系统框图绘制
在这里插入图片描述

ds18b20_ctrl

在这里插入图片描述
在这里插入图片描述

`timescale  1ns/1ns

module  ds18b20_ctrl
(
    input   wire        sys_clk     ,   //系统时钟,频率50MHz
    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   define
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       ;   //输出总线数据使能信号

//温度转换,由于数码管位数有限,在这里保留小数点后三位
assign data_out = (data * 10'd625)/ 4'd10;

//当使能信号为1是总线的值为dq_out的值,为0时为高阻态
assign  dq  =   (dq_en ==1 ) ? dq_out : 1'bz;

//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;

//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)
        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;

//状态跳转
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

//给各状态下的总线相应的时序
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

//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;

endmodule 

ds18b20

`timescale  1ns/1ns

module  ds18b20
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效

    inout   wire            dq          ,   //数据总线

    output  wire            stcp        ,   //输出数据存储寄时钟
    output  wire            shcp        ,   //移位寄存器的时钟输入
    output  wire            ds          ,   //串行数据输入
    output  wire            oe

);

//wire  define
wire    [19:0]  data_out ;
wire            sign     ;

//-------------ds18b20_ctrl_inst--------------
ds18b20_ctrl    ds18b20_ctrl_inst
(
    .sys_clk     (sys_clk  ),   //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n),   //复位信号,低电平有效

    .dq          (dq       ),   //数据总线

    .data_out    (data_out ),   //输出温度
    .sign        (sign     )    //输出温度符号位

);

//-------------seg7_dynamic_inst--------------
seg_595_dynamic  seg_595_dynamic_inst
(
    .sys_clk     (sys_clk  ), //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n), //复位信号,低有效
    .data        (data_out ), //数码管要显示的值
    .point       (6'b001000), //小数点显示,高电平有效
    .seg_en      (1'b1     ), //数码管使能信号,高电平有效
    .sign        (sign     ), //符号位,高电平显示负号

    .stcp        (stcp     ), //输出数据存储寄时钟
    .shcp        (shcp     ), //移位寄存器的时钟输入
    .ds          (ds       ), //串行数据输入
    .oe          (oe       )  //输出使能信号

);

endmodule

tb_ds18b20

`timescale  1ns/1ns
module  tb_ds18b20();

//wire  define
wire    stcp    ;
wire    shcp    ;
wire    ds      ;
wire    oe      ;
wire    dq      ;

//reg   define
reg     sys_clk     ;
reg     sys_rst_n   ;

//对sys_clk,sys_rst_n赋初始值
initial
    begin
        sys_clk     =   1'b1;
        sys_rst_n   <=  1'b0;
        #100
        sys_rst_n   <=  1'b1;
    end

//clk:产生时钟
always  #10 sys_clk <=  ~sys_clk;

//重新定义参数值,缩短仿真时间
defparam    ds18b20_inst.ds18b20_ctrl_inst.S_WAIT_MAX   =   750;

//-------------ds18b20_inst-------------
ds18b20     ds18b20_inst
(
    .sys_clk     (sys_clk  ),   //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n),   //复位信号,低电平有效

    .dq          (dq       ),   //数据总线

    .stcp        (stcp     ),   //输出数据存储寄时钟
    .shcp        (shcp     ),   //移位寄存器的时钟输入
    .ds          (ds       ),   //串行数据输入
    .oe          (oe       )    //输出使能信号
);

endmodule

在这里插入图片描述



第39讲:DHT11数字温湿度传感器

理论部分

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。具有稳定等优点,可应用于暖通空调、除湿器、农业、医疗等相关湿度检测控制。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设计与实现

系统框图绘制
在这里插入图片描述

key_filter

`timescale  1ns/1ns

module  key_filter
#(
    parameter CNT_MAX = 20'd999_999 //计数器计数最大值
)
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    key_in      ,   //按键输入信号

    output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                    //key_flag为0时表示没有检测到按键被按下
);

//reg   define
reg     [19:0]  cnt_20ms    ;   //计数器

//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else    if(key_in == 1'b1)
        cnt_20ms <= 20'b0;
    else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 1'b1;

//key_flag:当计数满20ms后产生按键有效标志位
//且key_flag在999_999时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else    if(cnt_20ms == CNT_MAX - 1'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

dht11_ctrl

控制模块的转态转移图
在这里插入图片描述
在这里插入图片描述

`timescale  1ns/1ns

module  dht11_ctrl
(
    input   wire        sys_clk     ,   //系统时钟,频率50MHz
    input   wire        sys_rst_n   ,   //复位信号,低电平有效
    input   wire        key_flag    ,   //按键消抖后标志信号

    inout   wire        dht11       ,   //控制总线

    output  reg [19:0]  data_out    ,   //输出显示的数据
    output  reg         sign            //输出符号位,高电平显示负号

);

//parameter define
parameter   S_WAIT_1S  = 3'd1   ,   //上电等待1s状态
            S_LOW_18MS = 3'd2   ,   //主机拉低18ms,发送开始信号状态
            S_DLY1     = 3'd3   ,   //等待20-40us状态
            S_REPLY    = 3'd4   ,   //DHT11响应80us状态
            S_DLY2     = 3'd5   ,   //拉高等待80us状态
            S_RD_DATA  = 3'd6   ;   //接收数据状态

parameter   T_1S_DATA    = 999999 ; //1s时间计数值
parameter   T_18MS_DATA  = 17999  ; //18ms时间计数值

//reg define
reg         clk_1us     ;   //1us时钟,用于驱动整个模块
reg [4:0]   cnt         ;   //时钟分频计数器
reg [2:0]   state       ;   //状态机状态
reg [20:0]  cnt_us      ;   //us计数器
reg         dht11_out   ;   //总线输出数据
reg         dht11_en    ;   //总线输出使能信号
reg [5:0]   bit_cnt     ;   //字节计数器
reg [39:0]  data_tmp    ;   //读出数据寄存器
reg         data_flag   ;   //数据切换标志信号
reg         dht11_d1    ;   //总线信号打一拍
reg         dht11_d2    ;   //总线信号打两拍
reg [31:0]  data        ;   //除校验位数据
reg [6:0]   cnt_low     ;   //低电平计数器

//wire  define
wire            dht11_fall; //总线下降沿
wire            dht11_rise; //总线上升沿

//当使能信号为1是总线的值为DATA_out的值,为0时值为高阻态
assign  dht11  =   (dht11_en == 1 ) ? dht11_out : 1'bz;

//检测总线信号的上升沿下降沿
assign  dht11_rise =   (~dht11_d2) & (dht11_d1)    ;
assign  dht11_fall =   (dht11_d2)  & (~dht11_d1)   ;

//对dht11信号打拍
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            dht11_d1  <=  1'b0 ;
            dht11_d2  <=  1'b0 ;
        end
    else
        begin
            dht11_d1  <=  dht11    ;
            dht11_d2  <=  dht11_d1 ;
        end

//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;

//bit_cnt:读出数据bit位数计数器
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <=  6'b0;
    else    if(bit_cnt == 40 && dht11_rise == 1'b1)
        bit_cnt <=  6'b0;
    else    if(dht11_fall == 1'b1 && state == S_RD_DATA)
        bit_cnt <=  bit_cnt + 1'b1;

//data_flag:数据变换标志信号的产生,按一次键变换一次
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_flag   <=  1'b0;
    else    if(key_flag == 1'b1)
        data_flag   <=  ~data_flag;
    else
        data_flag   <=  data_flag;

//状态机状态跳转
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  S_WAIT_1S   ;
    else
        case(state)
        S_WAIT_1S:
            if(cnt_us == T_1S_DATA) //上电1s后跳入起始状态
                state   <=  S_LOW_18MS  ;
            else
                state   <=  S_WAIT_1S   ;
        S_LOW_18MS:
            if(cnt_us == T_18MS_DATA)
                state   <=  S_DLY1     ;
            else
                state   <=  S_LOW_18MS  ;
        S_DLY1:
            if(cnt_us == 10)    //等待10us后进入下一状态
                state   <=  S_REPLY     ;
            else
                state   <=  S_DLY1     ;
        S_REPLY:  //上升沿到来且低电平保持时间大于70us,则跳转到下一状态
            if(dht11_rise == 1'b1 && cnt_low >= 70)
                state   <=  S_DLY2     ;
                 //若1ms后,dht11还没响应,则回去继续发送起始信号
            else    if(cnt_us >= 1000)
                state   <=  S_LOW_18MS ;
            else
                state   <=  S_REPLY    ;
        S_DLY2: //下降沿到来且计数器值大于70us,则跳转到下一状态
            if(dht11_fall == 1'b1 && cnt_us >= 70)
                state   <=  S_RD_DATA   ;
            else
                state       <=  S_DLY2  ;
        S_RD_DATA:  //读完数据后,回到起始状态
            if(bit_cnt == 40 && dht11_rise == 1'b1)
                state   <=  S_LOW_18MS  ;
            else
                state   <=  S_RD_DATA   ;
        default:
                state   <=  S_WAIT_1S   ;
        endcase

//各状态下的计数器赋值
//cnt_us:每到一个新的状态就让该计数器重新计数
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            cnt_low <=  7'd0      ;
            cnt_us   <=  21'd0    ;
        end
    else
        case(state)
        S_WAIT_1S:
            if(cnt_us == T_1S_DATA) 
                cnt_us   <=  21'd0  ;
            else
                cnt_us   <=  cnt_us + 1'b1;
        S_LOW_18MS:
            if(cnt_us == T_18MS_DATA)
                cnt_us   <=  21'd0  ;
            else
                cnt_us   <=  cnt_us + 1'b1;
        S_DLY1:
            if(cnt_us == 10)
                cnt_us   <=  21'd0  ;
            else
                cnt_us   <=  cnt_us + 1'b1;
        S_REPLY:
            if(dht11_rise == 1'b1 && cnt_low >= 70)
                begin
                    cnt_low <=  7'd0    ;
                    cnt_us   <=  21'd0  ;
                end
            //当dht11发送低电平回应时,计算其低电平的持续时间
            else    if(dht11 == 1'b0)
                begin
                    cnt_low  <=  cnt_low + 1'b1 ;
                    cnt_us   <=  cnt_us + 1'b1  ;
                end
            //若1ms后,dht11还没响应,则回去继续发送起始信号
            else    if(cnt_us >= 1000)
                begin
                    cnt_low <=  7'd0   ;
                    cnt_us  <=  21'd0  ;
                end
            else
                begin
                    cnt_low <=  cnt_low        ;
                    cnt_us  <=  cnt_us + 1'b1  ;
                end
        S_DLY2:
            if(dht11_fall == 1'b1 && cnt_us >= 70)
                cnt_us   <=  21'd0  ;
            else
                cnt_us   <=  cnt_us + 1'b1;
        S_RD_DATA:
            if(dht11_fall == 1'b1 || dht11_rise == 1'b1)
                cnt_us   <=  21'd0  ;
            else
                cnt_us   <=  cnt_us + 1'b1;
        default:
            begin
                cnt_low  <=  7'd0   ;
                cnt_us   <=  21'd0  ;
            end
        endcase

//各状态下的单总线赋值
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
               dht11_out <=  1'b0    ;
               dht11_en  <=  1'b0    ;
        end
    else
        case(state)
        S_WAIT_1S:
                begin
                    dht11_out    <=  1'b0    ;
                    dht11_en     <=  1'b0    ;
                end
        S_LOW_18MS: //拉低总线18ms
                begin
                    dht11_out    <=  1'b0    ;
                    dht11_en     <=  1'b1    ;
                end
    //后面状态释放总线即可,由DHT11操控总线
        S_DLY1:
                begin
                    dht11_out    <=  1'b0    ;
                    dht11_en     <=  1'b0    ;
                end
        S_REPLY:
                begin
                    dht11_out    <=  1'b0    ;
                    dht11_en     <=  1'b0    ;
                end
        S_DLY2:
                begin
                    dht11_out    <=  1'b0    ;
                    dht11_en     <=  1'b0    ;
                end
        S_RD_DATA:
                begin
                    dht11_out    <=  1'b0    ;
                    dht11_en     <=  1'b0    ;
                end
        default:;
        endcase

//data_tmp:将读出的数据寄存在data_tmp中
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_tmp    <=  40'b0;
    else    if(state == S_RD_DATA && dht11_fall == 1'b1 && cnt_us<=50)
        data_tmp[39-bit_cnt]   <=  1'b0;
    else    if(state == S_RD_DATA && dht11_fall == 1'b1 && cnt_us>50)
        data_tmp[39-bit_cnt]   <=  1'b1;
    else
        data_tmp    <=  data_tmp;

//data_out:输出数据显示,按一次按键切换一次数据
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data    <=  32'b0;
    else    if(data_tmp[7:0] == data_tmp[39:32] + data_tmp[31:24] +
                                      data_tmp[23:16] + data_tmp[15:8])
        data    <=  data_tmp[39:8];   //若检验位正确,则数据值有效
     else
        data    <=  data;

//data_out:对数码管显示的湿度和温度进行赋值
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_out    <=  20'b0;
    else    if(data_flag == 1'b0 )
        data_out    <=  data[31:24] * 10; //湿度小数位为0
    else    if(data_flag == 1'b1)
    //温度低四位显示温度小数数据
        data_out    <=  data[15:8] * 10 + data[3:0];

//sign:符号位的显示
always@(posedge clk_1us or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sign    <=  1'b0;
    else    if(data[7] == 1'b1 && data_flag == 1'b1)
    //当温度低八位最高位为1时,显示负号
        sign    <=  1'b1;
    else
        sign    <=  1'b0;

endmodule

dht11

`timescale  1ns/1ns

module  dht11
(
    input   wire    sys_clk     ,   //系统时钟,频率50MHz
    input   wire    sys_rst_n   ,   //复位信号,低电平有效
    input   wire    key_in      ,   //按键信号

    inout   wire    dht11       ,   //数据总线
    output  wire    stcp        ,   //输出数据存储寄时钟
    output  wire    shcp        ,   //移位寄存器的时钟输入
    output  wire    ds          ,   //串行数据输入
    output  wire    oe
);

//wire  define
wire    [19:0]  data_out;   //需要显示的数据
wire            key_flag;   //按键消抖后输出信号
wire            sign    ;   //输出符号

//-------------dht11_ctrl_inst--------------
dht11_ctrl  dht11_ctrl_inst
(
    .sys_clk     (sys_clk  ),   //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n),   //复位信号,低电平有效
    .key_flag    (key_flag ),   //按键消抖后标志信号

    .dht11       (dht11    ),   //控制总线

    .data_out    (data_out ),   //输出显示的数据
    .sign        (sign     )    //输出符号
);

//-------------key_fifter_inst--------------
key_filter  key_filter_inst
(
    .sys_clk      (sys_clk  )   ,   //系统时钟50Mhz
    .sys_rst_n    (sys_rst_n)   ,   //全局复位
    .key_in       (key_in   )   ,   //按键输入信号

    .key_flag     (key_flag )       //按键消抖后输出信号
);

//-------------seg_595_dynamic_inst--------------
seg_595_dynamic     seg_595_dynamic_inst
(
    .sys_clk     (sys_clk  ), //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n), //复位信号,低有效
    .data        (data_out ), //数码管要显示的值
    .point       (6'b000010), //小数点显示,高电平有效
    .seg_en      (1'b1     ), //数码管使能信号,高电平有效
    .sign        (sign     ), //符号位,高电平显示负号

    .stcp        (stcp     ), //输出数据存储寄时钟
    .shcp        (shcp     ), //移位寄存器的时钟输入
    .ds          (ds       ), //串行数据输入
    .oe          (oe       )  //输出使能信号
);

endmodule

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_39236499/article/details/128049289