FPGA project : DHT11

news2024/11/16 7:37:42

我犯下的错误:

1,在START状态跳转到REPLAY状态,sda会由高电平跳转到低电平。这个下降沿是传感器产生低电平响应而产生的。而不是传感器准备高电平结束而产生的。
REPLAYtoWAIT_2S     = ( state_c == REPLAY  ) && (nege && flag0 == 1'b0 && flag1 == 1'b0 && cnt_us >= 30) ; // 这里错过,cnt_us >= 30 是后加上去的;

2,RD_DATAtoWAIT_2S    = ( state_c == RD_DATA ) && (pose && data_done == 1); // 写错了,应该用data_done 。之前把flag_base 与了上去。这就导致状态机卡在了RD_DATA状态。因为pose是随机的。虽然做了同步打拍同步,和flag_base是同一时钟域但是他俩几乎不可能同时满足。

总结:在时序比较简单的工程,容易出错的地方大概率是状态机,而状态机容易出错的地方就是状态转移条件描述了。所以以后设计状态机时,要格外慎重。

3,在编译时的一个警告:

这个是状态机,状态参数定义时,忘记改值,导致的。

4,被quartus优化的信号。

 在Quartus中,使用/*synthesis noprune*/,/*synthesis preserve*/等语句,注意,这些语句如果是用于信号的定义时,需要放在定义的语句尾部。

在Vivado中,使用(* keep="true" *),(* keep_hierarchy="yes" *)语句。这些语句是放在信号定义之前的

 

 

module dht11(
    input       wire            sys_clk     ,
    input       wire            sys_rst_n   ,
    input       wire            key_in      ,

    inout       wire            sda         ,

    output      reg     [19:0]  data_out    ,
    output      reg             sign
);

    // localparam
    localparam          WAIT_2S = 4'b0001 ,
                        START   = 4'b0010 ,
                        REPLAY  = 4'b0100 ,
                        RD_DATA = 4'b1000 ;
    // wire signal define 
    wire                nege              ;
    wire                pose              ;
    wire                flag_base         ;
    wire                WAIT_2StoSTART    ;
    wire                STARTtoREPLAY     ;
    wire                REPLAYtoRD_DATA   ;
    wire                REPLAYtoWAIT_2S   ;
    wire                RD_DATAtoWAIT_2S  ;
    // reg signal define
    reg                 sda_reg0  ;
    reg                 sda_reg1  ;
    reg                 sda_reg2  ;
    reg                 sda_en    ;
    reg                 sda_out   ;
    reg                 flag0     ;
    reg                 flag1     ;
    reg                 key_flag  ;
    reg                 data_done ;
    reg     [ 5:0]      cnt_base  ;
    reg     [ 3:0]      state_c   /*synthesis preserve*/;
    reg     [ 3:0]      state_n   ;
    reg     [ 5:0]      cnt_bit   ;
    reg     [20:0]      cnt_us    ;
    reg     [39:0]      data_temp ;

/***********************************************************************/
    // 1us 标志信号的产生
    // reg     [5:0]       cnt_base  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            cnt_base <= 6'd0 ;
        end else begin
            if(flag_base) begin
                cnt_base <= 6'd0 ;
            end else begin
                cnt_base <= cnt_base + 1'b1 ;
            end
        end
    end
    // wire                flag_base ;
    assign flag_base = (cnt_base == 49) ? 1'b1 : 1'b0 ;

    // 同步与打两拍
    // reg sda_reg0 // reg sda_reg1 // reg sda_reg2  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            sda_reg0 <= 1'b1 ;
            sda_reg1 <= 1'b1 ;
            sda_reg2 <= 1'b1 ;
        end else begin
            sda_reg0 <= sda      ;
            sda_reg1 <= sda_reg0 ;
            sda_reg2 <= sda_reg1 ;
        end
    end

    // sda总线上升沿与下降沿的检测
    // wire                nege      ;
    // wire                pose      ;
    assign  nege = ~sda_reg1 &&  sda_reg2 ;
    assign  pose =  sda_reg1 && ~sda_reg2 ;

    // 三段式状态机 状态转移描述 状态转移条件描述 输出描述(与状态转移有关量描述)
    // reg     [ 4:0]      state_c   ;
    // reg     [ 4:0]      state_n   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            state_c <= WAIT_2S ;
        end else begin
            state_c <= state_n;
        end
    end
    always @(*) begin
        case (state_c)
        WAIT_2S:begin
                    if(WAIT_2StoSTART) begin
                        state_n <= START ;
                    end else begin
                        state_n <= WAIT_2S ;
                    end
                end
        START  :begin
                    if(STARTtoREPLAY) begin
                        state_n <= REPLAY ;
                    end else begin
                        state_n <= START ;
                    end
                end
        REPLAY :begin
                    if(REPLAYtoRD_DATA) begin
                        state_n <= RD_DATA ;
                    end else begin
                        if(REPLAYtoWAIT_2S) begin
                            state_n <= WAIT_2S ;
                        end else begin
                            state_n <= REPLAY ;
                        end
                    end
                end
        RD_DATA:begin
                    if(RD_DATAtoWAIT_2S) begin
                        state_n <= WAIT_2S ;
                    end else begin
                        state_n <= RD_DATA ;
                    end
                end 
        default: state_n <= WAIT_2S ;
        endcase
    end
    assign  WAIT_2StoSTART      = ( state_c == WAIT_2S ) && (flag_base && cnt_us == 1_999_999) ;
    assign  STARTtoREPLAY       = ( state_c == START   ) && (flag_base && cnt_us == 20_012   ) ;
    assign  REPLAYtoRD_DATA     = ( state_c == REPLAY  ) && (nege && flag0 && flag1)           ;
    assign  REPLAYtoWAIT_2S     = ( state_c == REPLAY  ) && (nege && flag0 == 1'b0 && flag1 == 1'b0 && cnt_us >= 30) ; // 这里错过一次,cnt_us >= 30 是后加上去的;因为第一次的低电平是传感器响应低电平拉低的,并不是响应高电平结束拉低的。
    assign  RD_DATAtoWAIT_2S    = ( state_c == RD_DATA ) && (pose && data_done == 1); // 写错了,应该用data_done 。而且pose 和 flag_base 是两个时钟域下的,根本不能同时满足。逻辑错误。
    // reg     [20:0]      cnt_us    ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            cnt_us <= 21'd0 ;
        end else begin
            case (state_c)
            WAIT_2S:begin
                        if(flag_base && cnt_us == 1_999_999) begin
                            cnt_us <= 21'd0 ;
                        end else begin
                            if(flag_base) begin
                                cnt_us <= cnt_us + 1'b1 ;
                            end else begin
                                cnt_us <= cnt_us ;
                            end
                        end
                    end
            START  :begin
                        if(flag_base && cnt_us == 20_012) begin
                            cnt_us <= 21'd0 ;
                        end else begin
                            if(flag_base) begin
                                cnt_us <= cnt_us + 1'b1 ;
                            end else begin
                                cnt_us <= cnt_us ;
                            end
                        end
                    end
            REPLAY :begin
                        if(nege || pose) begin
                            cnt_us <= 21'd0 ;
                        end else begin
                            if(flag_base) begin
                                cnt_us <= cnt_us + 1'b1 ;
                            end else begin
                                cnt_us <= cnt_us ;
                            end
                        end
                    end
            RD_DATA:begin
                        if(nege || pose) begin
                            cnt_us <= 21'd0 ;
                        end else begin
                            if(flag_base) begin
                                cnt_us <= cnt_us + 1'b1 ;
                            end else begin
                                cnt_us <= cnt_us ;
                            end
                        end
                    end
                default: cnt_us <= 21'd0 ;
            endcase
        end
    end
    // reg                 flag0     ;
    // reg                 flag1     ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            flag0 <= 1'b0 ;
            flag1 <= 1'b0 ;
        end else begin
            if(state_c == REPLAY) begin
                if(cnt_us >= 81 && sda == 0) begin // 这里其实可能有一个小问题,就是响应时序的低电平不足81拉高了,但是响应时序的高电平满足。那么也会判定为满足响应。
                    flag0 <= 1'b1 ;
                end else begin
                    flag0 <= flag0 ;   // 所以,在设计flag0信号的时候,还得加上一条,sda == 0
                end

                if(cnt_us >= 85 && sda == 1) begin
                    flag1 <= 1'b1 ;
                end else begin
                    flag1 <= flag1 ;
                end
            end else begin
                flag0 <= 1'b0 ;
                flag1 <= 1'b0 ;
            end
        end
    end
    // reg     [5:0]       cnt_bit   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            cnt_bit <= 6'd0 ;
        end else begin
            if(state_c == RD_DATA) begin
                if(nege) begin // 判断cnt_us 的值 是给data_temp 赋值时要考虑的事情。这里只是cnt_bit + 1
                    cnt_bit <= cnt_bit + 1'b1 ;
                end else begin
                    if(pose && cnt_bit == 40) begin
                        cnt_bit <= 6'd0 ;
                    end else begin
                        cnt_bit <= cnt_bit ;
                    end
                end
            end else begin
                cnt_bit <= 6'd0 ;
            end
        end
    end
    // reg                 data_done ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            data_done <= 1'b0 ;
        end else begin
            if(state_c == RD_DATA && cnt_bit == 40) begin
                data_done <= 1'b1 ;
            end else begin
                data_done <= 0 ;
            end
        end
    end
    // reg     [39:0]      data_temp ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            data_temp <= 40'd0 ;
        end else begin
            if(state_c == RD_DATA) begin
                if(nege) begin
                    if(cnt_us >= 23 && cnt_us <= 27) begin
                        data_temp[39 - cnt_bit] <= 1'b0 ;
                    end else begin
                        if(cnt_us >= 68 && cnt_us <= 74) begin
                            data_temp[39 - cnt_bit] <= 1'b1 ;
                        end else begin
                            data_temp[39 - cnt_bit] <= 1'b0 ;
                        end
                    end
                end else begin
                    data_temp <= data_temp ;
                end
            end
        end
    end
    // reg                 key_flag  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            key_flag <= 1'b0 ;
        end else begin
            if(key_in) begin
                key_flag <= ~key_flag ;
            end else begin
                key_flag <=  key_flag ;
            end
        end
    end
    // 三态使能与输出
    // reg                 sda_en    ;
    // reg                 sda_out   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            sda_en  <= 1'b1 ;
            sda_out <= 1'b1 ;
        end begin
            case (state_c)
            WAIT_2S :   begin
                           sda_en  <= 1'b1 ;
                           sda_out <= 1'b1 ;
                        end // 由于使用reg 类型,这里在start状态会多拉高20ns的高电平。
            START   :   begin
                            sda_en  <= 1'b1 ;
                            if(cnt_us <= 19_999) begin
                                sda_out <= 1'b0 ;
                            end else begin
                                sda_out <= 1'b1 ;
                            end
                        end
            REPLAY  :   begin
                            sda_en  <= 1'b0 ;
                            sda_out <= 1'b1 ;
                        end
            RD_DATA :   begin
                            sda_en  <= 1'b0 ;
                            sda_out <= 1'b1 ;
                        end
                default: begin
                            sda_en  <= 1'b0 ;
                            sda_out <= 1'b1 ;
                        end
            endcase
        end
    end
    
/**********************************output signal*****************************************/
    // wire            sda         ,
    assign sda = (sda_en == 1'b1) ? sda_out : 1'bz ; // 注意三态输出赋值z
    // reg     [19:0]  data_out    ,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            data_out <= 20'd0 ;
        end else begin
            if(key_flag == 1'b0) begin // 显示湿度 前8位是整数,后8位是小数。
                data_out <= (data_temp[39:24] >> 8) * 20'd10 ; // 因为数码管的最后一位是显示小数的,所以×10。
            end else begin
                data_out <= data_temp[23:16] * 20'd10 + data_temp[11:8] ;                     
            end
        end
    end
    // 目前 DHT11 温度只能精确到 0.1℃,所以温度 8bit 小数数据的值是小于 10 的,
    // 我们应用时用小数数据的低四位来表示
    // 温度的小数值,最高位表示温度的正负即可。
    // reg             sign
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            sign <= 1'b0 ;
        end else begin
            if(data_done && (data_temp[7:0] == (data_temp[15:8] + data_temp[23:16] + data_temp[31:24] + data_temp[39:32]))) begin
                if(data_temp[15] == 1'b1) begin
                    sign <= 1'b1 ;
                end else begin
                    sign <= 1'b0 ;
                end
            end
        end
    end

endmodule
module top(
    input       wire        sys_clk     ,
    input       wire        sys_rst_n   ,
    input       wire        key         ,

    inout       wire        dht11       ,

    output      wire        ds          ,
    output      wire        oe          ,
    output      wire        shcp        ,
    output      wire        stcp          
);

    // 例化连线
    wire                    key_out_w ;
    wire        [19:00]     data_w    ;
    wire                    sign_w    ;
    wire        [ 5: 0]     point_w   ;
    wire                    en_w      ;
    assign       point_w = 6'b000_010 ;
    assign       en_w    = 1'b1       ;

key_filter key_filter_insert(
    .sys_clk                ( sys_clk   ) ,
    .sys_rst_n              ( sys_rst_n ) ,
    .key_in                 ( key       ) ,

    .key_out                ( key_out_w )         
);

dht11 dht11_insert(
    .sys_clk                ( sys_clk    ) ,
    .sys_rst_n              ( sys_rst_n  ) ,
    .key_in                 ( key_out_w  ) ,

    .sda                    ( dht11      ) ,

    .data_out               ( data_w     ) ,
    .sign                   ( sign_w     )
);

seg_595_dynamic seg_595_dynamic_insert(
    .sys_clk                ( sys_clk    ) ,
    .sys_rst_n              ( sys_rst_n  ) ,
    .data                   ( data_w     ) ,        
    .point                  ( point_w    ) ,
    .sign                   ( sign_w     ) ,        
    .seg_en                 ( en_w       ) ,

    .ds                     ( ds         ) ,
    .oe                     ( oe         ) ,
    .shcp                   ( shcp       ) ,
    .stcp                   ( stcp       )    
);

endmodule

 木有仿真,只有signaltap抓取的波形,但我忘记截屏了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035152.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java本地缓存框架Caffeine介绍

Caffeine是一个进程内部缓存框架&#xff0c;使用了Java 8最新的[StampedLock]乐观锁技术&#xff0c;极大提高缓存并发吞吐量&#xff0c;一个高性能的 Java 缓存库&#xff0c;被称为最快缓存。 缓存简介 &#xff08;一&#xff09;缓存对比 从横向对常用的缓存进行对比&a…

(本地安装clickhouse)执行 nstall/doinst.sh时报错: cp: 无法创建普通文件“/usr/bin/clickho

问题描述 在本地安装clickhouse时&#xff0c; 解压&#xff1a;tar -zxvf clickhouse-common-static-21.9.4.35.tgz -C ../module/ 再进入cd clickhouse-common-static-21.9.4.35/ 执行&#xff1a;install/doinst.sh 报错 报错信息和截图&#xff1a; rootbigdata1 click…

【带RL负载的全波桥式整流器】功能齐全的单相非控整流器(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Qt: 鼠标形状设置

设置全局鼠标形状 设置完毕后&#xff0c;整个APP的任何窗体&#xff0c;包括Dialog中的鼠标形状都会被修改为设定类型&#xff0c;某一个控件设定的鼠标形状将被替换。一般不建议使用 QCursor cursor;//创建鼠标对象 cursor.setShape(Qt::CursorShape::ClosedHandCursor);//…

scikit-learn机器学习算法封装

K近邻算法 K-最近邻&#xff08;KNN&#xff09;是一种有监督的机器学习算法&#xff0c;可用于解决分类和回归问题。它基于一个非常简单的想法&#xff0c;数据点的值由它周围的数据点决定。考虑的数据点数量由k值确定。因此&#xff0c;k值是算法的核心。 我们现在已经知道。…

【03】FISCOBCOS配置及使用控制台

官网文档https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/installation.html# 配置及使用控制台 第一步. 准备依赖 安装java &#xff08;推荐使用java 14&#xff09; # ubuntu系统安装java sudo apt install -y default-jdk#centos系统安装java sudo yu…

深入了解Java中的StringJoiner类

在Java编程中&#xff0c;字符串的拼接是一个常见的操作。Java提供了多种方法来实现字符串拼接&#xff0c;其中之一就是StringJoiner类。本文将详细介绍StringJoiner的用法和功能。 StringJoiner简介 StringJoiner是Java 8引入的一个用于拼接字符串的工具类。它允许我们以指定…

如何建立团队知识库管理系统,把分散信息有效整理?

对于我们广大企业管理者来说&#xff0c;知识库作为一个「统一的资料收集中心」&#xff0c;意义在于将分散的资料集中起来&#xff0c;统一处理&#xff0c;降低管理成本。 以常见的项目小组为例&#xff0c;如果你希望将小组内优秀的方法论和工作SOP文档在整个部门分享学习&a…

PID之Simulink仿真

昨天&#xff0c;在中南大学面试的老师&#xff0c;老师突然问到如何调PID&#xff0c;关于PID&#xff0c;我只知道一些基本概念&#xff0c;实际中并没有做过相关PID的项目&#xff0c;就连仿真也没搞过&#xff0c;所以今天就照着网上的教程做了PID的仿真&#xff0c;调PID的…

【李沐深度学习笔记】自动求导实现

课程地址和说明 自动求导实现p2 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 自动求导 # 创建变量 import torch x torch.arange(4, dtypetorch.float32) #只有浮点数才能求导 # 计算y关于x的梯度之前&#x…

VUE之模板解析(v-for)

v-for的多种写法 1. item in list 2. (item, index) in list 3. (item, name, index) in object forAliasRE 非贪婪模式匹配 &#xff1f; 正则表达式默认都是贪婪匹配&#xff0c;添加&#xff1f;后将其变成非贪婪模式 由下面例子可以看出如果没有添加&#xff1f;正则…

虚拟机软件Parallels Desktop 18 mac中文新增功能(PD18虚拟机)

Parallels Desktop 18可以在 Mac 计算机上下载并安装 Windows 操作系统。在 Mac 与 Windows 之间无缝复制和粘贴文本或拖放对象。在 Mac 虚拟机中跨多个操作系统开发和测试。毫不费力地运行 Windows 应用程序&#xff0c;不会减慢 Mac 的运行速度。 Parallels Desktop 18 for M…

lv5 嵌入式开发-5 线程的创建和参数传递

目录 1 线程基础 2 Linux线程库 2.1 线程创建 – pthread_create 2.2 线程结束 – pthread_exit 2.3 线程查看tid函数 2.4 线程间参数传递&#xff08;重点&#xff09; 2.4.1 练习 2.5 线程查看命令&#xff08;多线程&#xff09; 2.6 线程回收 – pthread_join 2.…

【重新定义matlab强大系列十四】基于问题求解有/无约束非线性优化

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

玩转Mysql系列 - 第22篇:mysql索引原理详解

这是Mysql系列第22篇。 背景 使用mysql最多的就是查询&#xff0c;我们迫切的希望mysql能查询的更快一些&#xff0c;我们经常用到的查询有&#xff1a; 按照id查询唯一一条记录 按照某些个字段查询对应的记录 查找某个范围的所有记录&#xff08;between and&#xff09; …

如何解决python安装Crypto失败的问题

文章目录 准备安装相关链接准备 Python Crypto模块是一个第三方库,它提供了常见的加密算法和协议,比如AES、RSA、DES等。 PyCrypto 是加密工具包 Crypto 的 python 版,该模块实现了各种算法和协议的加密,提供了各种加密方式对应的算法的实现,包括单向加密、对称加密及公钥…

Linux(ubuntu)系统更新后不能进入图形界面

最近需要跑一个深度学习的程序&#xff0c;把许久没用的ubuntu系统调了出来&#xff0c;手欠的我更新了一下系统&#xff0c;结果再启动&#xff0c;系统就只停留在光标闪动那里&#xff0c;不能看到图形界面了。网上查了一下&#xff0c;说是因为更新后&#xff0c;显卡驱动没…

MySQL 8.0数据库主从搭建和问题处理

错误处理&#xff1a; 在从库通过start slave启动主从复制时出现报错 Last_IO_Error: error connecting to master slaveuser10.115.30.212:3306 - retry-time: 60 retries: 1 message: Authentication plugin caching_sha2_password reported error: Authentication require…

【draw】draw.io怎么设置默认字体大小

默认情况下draw里面的字体大小是12pt&#xff0c;我们可以将字体改成我们想要的大小&#xff0c;例如18pt。 然后点击样式&#xff0c;设置为默认样式。 下一次当我们使用文字大小时就是18pt了。

html和css相关操作

html第一个网页 <!DOCTYPE html> <!--html文档声明&#xff0c;声明此文档是一个html5的文档--> <html> <!--html文档开头标签--><head><!--html文档的设置标签&#xff0c;文档的设置及资源的引用都写在这个标签中--><meta charset&q…