当前位置:   article > 正文

FPGA实验笔记_Vivado:DDS信号发生器;数码管;基于DHT11的温湿度传感器_vivado dds

vivado dds

目录

1、 FPGA的DDS信号发生器

1.1、DDS简介

1.2、ROM IP核的生成

1.3、波形数据的生成

1.4、 ROM的调用

1.5、 完整代码(包括拓展部分)

2、数码管显示

2.1、数码管简要说明

2.2、SM410564

3、基于DHT11的温湿度传感器

3.1、DHT11

3.2、基本思路

3.3、数据分离模块(BTD)

3.4、数据转换模块(SMG)

3.5、DHT11控制模块

3.5.1、上升、下降沿的判定

3.5.2、端口IO状态控制

3.5.3、状态判断

3.5.4、数据读入

3.5.5、完整代码

3.6、TOP

3.7、结果展示


 

1、 FPGA的DDS信号发生器

1.1、DDS简介

        DSS全称为“直接数字式频率合成”,通过逻辑高低电平的相加合成数字信号,在将数信信号转换,生成模拟信号。

        DSS信号发生器的实现主要分为三个步骤:

  1.         生成所需波形数据并储存到IP核“ROM”中;
  2.         读取波形数据并输出数字信号;
  3.         将数字信号通过DA转换模块转换成模拟信号。

1.2、ROM IP核的生成

        调用ROM,首先需要生成一个ROM。步骤如下:

        1.         PROJECT MANAGER -- IP Catalog;

        2.        在IP Catalog界面搜索ROM,选择RAMs & ROMs & BRAM -- Block Memory Generator;

        3.        选择所需要的Memory Type ,这里我们选择Single Port ROM; 

01e2639d31ce419c90383f9cd5ce864f.png

        4.      设定Memory Size,根据所需要的Width 与 Depth,这里我们由于所用的波形数据为256个采样点,故设定Width = 8;Depth = 256;

67441a72faa54e4cb26172a7a6f862d1.png

              5.        设定波形文件,在Memory Initialization中勾选 Load Init File,然后在Coe File一栏选择对于的文件,随后点击OK,这样ROM就生成完毕了。

              调用端口如下:

  1. rom_name rom_name(
  2. .clka( ),
  3. //input ck
  4. .addra( ),
  5. //input address
  6. .douta( )
  7. //output data
  8. );

                 需要注意,rom的地址从0开始,依次向上加1。如0地址的下一位地址为1。

1.3、波形数据的生成

        波形数据生成较为简单,MATLAB代码如下:

  1. clc;
  2. clear all;
  3. N=2^8;
  4. A=2^8;%振幅
  5. f=1;9
  6. F1=0;%相位
  7. F2=256;
  8. n=[0:N-1];
  9. s1=round((A/2)*sin(2*pi*n/N+pi*F1)+F2/2);%256/2为0
  10. fild=fopen('wave_dds_sin.coe','wt');
  11. fprintf(fild,'memory_initialization_radix=10;\n');
  12. fprintf(fild,'memory_initialization_vector=\n');
  13. for i=1:N
  14. if(s1(i)==256)
  15. s1(i)=255;
  16. end
  17. fprintf(fild,'%d,\n',s1(i));
  18. end
  19. fclose(fild);
  20. plot(s1)

        通过程序生成单周期有256个数据点的正弦波,A为峰峰值。

        需要注意最小值为0,而最大值并非256,而是255,因为ROM所设置的Width为8,仅能储存八位二进制数,而256为“ 'b100000000 ” 。若最大值为256,则会在ROM选择信号源文件时报错。

1.4、 ROM的调用

        假设系统时钟频率为50MHz,一个周期输出一个数据点,则输出波形频率为

                        Fout = 50M/256 Hz = 195312.5Hz

        若想要改变输出波形的频率,可以生成一个相位累加器,俗称加法器,同时设置一个频率控制字Fword来控制加法器累加的速度。

        虽然在固定的时钟频率下加法器每次累加的速度是一样的,但是可以调整每次累加的步长来控制其达到最大值的速度。

        频率控制字、加法器长度(位数)N、时钟频率以及输出波形频率关系如下:

                Fword * clk = Fout * 2 ^ N

        加法器控制程序如下:

  1. always@(posedge dac_clk or negedge rst )//计数器,越界归零, frequency = 50 MHz
  2. begin
  3. if(rst) begin//当 rst = 1
  4. Fcnt <= 32'd0;
  5. end
  6. else//当rst等于0时
  7. Fcnt <= Fcnt + Fword;
  8. end

                需要注意的是,由于程序中使用的频率控制字并不一定为整数,故在计算时会产生一定误差,但此误差基本可忽略不计。

        由于所用的ROM地址为8位二进制数,在一个周期内需要从0变化到255 即 11111111 一次,而累加器同样需要变到 1111 1111……一次,故可以使用累加器的前八位作为调用ROM的地址。

        assign rom_addr = Fcnt[31:24] + Pword;

1.5、 完整代码(包括拓展部分)

  1. module DDS_CHEN(
  2. input clk, //fpga clock
  3. input rst,//rst is 0 if no act
  4. input swi,
  5. input [3:0]bot,//按下时为高电平,逻辑1,控制频率以及幅度
  6. output dac_clk,
  7. output [7:0]sin_out,
  8. output [4:0] led
  9. );
  10. reg [31:0] Fword;
  11. wire [31:0] Pword;
  12. assign Pword = 8'b0;
  13. wire clk50;//标准时钟
  14. wire clk10;//没啥用
  15. wire [7:0] wave_data_sin;//读取数据
  16. wire [7:0] wave_data_sq;
  17. reg [7:0] wave_data_r;//赋值数据;
  18. reg [31:0] Fcnt;//计数器
  19. wire [7:0] rom_addr;//地址
  20. reg [4:0]led_r;//测试用
  21. //实际时钟
  22. assign dac_clk = clk50; //输出时钟
  23. //仿真时钟
  24. //assign dac_clk = clk;
  25. //控频
  26. //50M clk 858993-10k 8589933-100k 429497-5k 85899-1k 4295-50 相位累加器步长误差最大为7.6E-6
  27. //Fword = fout*(2^n)/fclk n = 32 fclk = 50MHz
  28. always@( posedge clk50)
  29. begin
  30. casex(bot)
  31. 4'b11xx: Fword = 4295;//50
  32. 4'b10xx: Fword = 85899;//1k
  33. 4'b01xx: Fword = 429497;//5k
  34. 4'b00xx: Fword = 858993;//10k
  35. default :Fword = 858993;
  36. endcase
  37. end
  38. //地址控制
  39. always@(posedge dac_clk or negedge rst )//计数器,越界归零, frequency = 50 MHz
  40. begin
  41. if(rst) begin//当 rst = 1
  42. Fcnt <= 32'd0;
  43. end
  44. else//当rst等于0时
  45. Fcnt <= Fcnt + Fword;
  46. end
  47. assign rom_addr = Fcnt[31:24] + Pword;//the first 8 bit as the address
  48. //指示灯
  49. always@(posedge dac_clk)
  50. begin
  51. if(!rst&&!bot)
  52. led_r <= 5'b1;
  53. else if(!rst&&bot[0])
  54. led_r <= 5'b00010;
  55. else if(!rst&&bot[1])
  56. led_r <= 5'b00100;
  57. else if(!rst&&bot[2])
  58. led_r <= 5'b01000;
  59. else if(!rst&&bot[3])
  60. led_r <= 5'b10000;
  61. else
  62. led_r <=0;
  63. end
  64. //赋值与控幅,控幅还可以通过外部电路改变电压来实现
  65. always@(posedge dac_clk)
  66. begin
  67. case(swi)
  68. 1'b0:
  69. casex(bot)
  70. 4'bxx00 : wave_data_r <= wave_data_sin / 4;
  71. 4'bxx01 : wave_data_r <= wave_data_sin / 2;
  72. 4'bxx10 : wave_data_r <= (wave_data_sin / 4)*3 ;
  73. 4'bxx11 : wave_data_r <= wave_data_sin ;
  74. endcase
  75. 1'b1:
  76. casex(bot)
  77. 4'bxx00 : wave_data_r <= wave_data_sq / 4;
  78. 4'bxx01 : wave_data_r <= wave_data_sq / 2;
  79. 4'bxx10 : wave_data_r <= (wave_data_sq / 4)*3 ;
  80. 4'bxx11 : wave_data_r <= wave_data_sq;
  81. default: wave_data_r <= 114;
  82. endcase
  83. endcase
  84. end
  85. assign sin_out = wave_data_r;
  86. assign led = led_r;
  87. blk_mem_gen_0 blk_mem_gen_0 (
  88. //实际
  89. .clka(clk50), // input clka
  90. //仿真
  91. //.clka(clk),
  92. .addra(rom_addr), // input [8 : 0] addra
  93. .douta(wave_data_sin) // output [7 : 0] douta
  94. );
  95. blk_mem_gen_1 sq(
  96. //仿真
  97. //.clka(clk),
  98. //实际
  99. .clka(clk50),
  100. .addra(rom_addr),
  101. .douta(wave_data_sq)
  102. );
  103. clk_wiz_0 clk_wiz_0 (
  104. .clk_in1(clk),
  105. .resetn(1'b1),
  106. .clk_out50M(clk50),
  107. .clk_out10M(clk10),
  108. .locked()
  109. );
  110. endmodule

2、数码管显示

2.1、数码管简要说明

        一般而言,数码管可以分为共阴、共阳两种。

        共阴为数码管的八个二极管共用一个阴极,此时控制不同二极管阳极的电平就可以控制其显示状态,当阳极与阴极形成一定电压差时,二极管导通发光,即高电平发光。一般而言,共极低电平表示数码管工作。

        共阳为数码管的八个二极管共用一个阳极。一般当共阳极为高电平时二极管工作,相应二极管阴极为低电平时发光。即低电平发光。

        共阳、共阴极的数码管真值表正好完全相反。  

2.2、SM410564

        本次实验所使用的是四位共阳数码管SM410564。当正视显示面时,其管脚约束如下:

 14e03f6fe5724037a4031d33714199d6.png        s1~s4为四个共阳极,控制四个显示数位,可以同时为高电平,由于共极管脚分别独立,故电平高低与否与其他管脚无关。

        a~g为显示数字的七个发光二极管的阴极管脚,dp为小数点位置发光二极管的阴极管脚。

        整个模块共由4*8共32个发光二极管组成,显然控制发光二极管的8个阴极管脚不足以同时显示四个不同的符号,但可以同时显示相同的符号。故采用交替显示的方式进行输出。

        交替显示通过对于每一时钟周期,仅让一个共极为高电平,并将相应真值赋值,在下一个周期相邻共极变为高电平,当前共极变为低电平,并重新进行赋值,如此循环。假设时钟周期为50M赫兹,则整个显示模块的刷新率即为50Mhz, 而对于单一的显示位,刷新率为50M/4 = 12.5MHz远大于人眼25Hz或是80Hz的基本要求。

        完整代码如下:(刷新了为1lHz,输出仅为6.6.6.6.)

  1. `timescale 1ns / 1ps
  2. module SMG(
  3. input clk,
  4. input rst,
  5. output [3:0] numo,
  6. output [6:0] datao,
  7. output pointo
  8. );
  9. wire clk50;
  10. reg [3:0]numr;
  11. wire [3:0]numw;
  12. reg [6:0]datar;
  13. wire[6:0]dataw;
  14. reg clk_s;
  15. reg [32:0] cnt;//微妙时钟计数器
  16. reg pointr;
  17. assign pointo = pointr;
  18. assign numo = numr;
  19. assign datao = datar;
  20. always@(posedge clk_s)//0.001s
  21. begin
  22. datar = 7'b0000_010;//6
  23. pointr = 1'b0;
  24. if(rst)
  25. numr = 4'b0001;
  26. else if(numr ==4'b1000)
  27. numr = 4'b0001;//从s4向s1刷新;
  28. else if(numr !=4'b1000)
  29. numr = numr << 1;
  30. end
  31. //时钟
  32. always@(posedge clk50 or negedge rst)
  33. begin//一个周期为25时钟周期,0.02*25*10^6=0.5us,一个周期0.5us
  34. if(rst)
  35. cnt <= 32'd0;
  36. else if(cnt == 32'd24_999)//毫秒计数器
  37. cnt <= 32'd0;
  38. else
  39. cnt <= cnt + 1'b1;
  40. end
  41. //微秒时钟2,由cnt控制
  42. always@(posedge clk50 or negedge rst)
  43. begin
  44. if(rst)
  45. clk_s <= 1'b0;
  46. else if(cnt== 32'd24_999)//将cnt-us换为cnt,此时0.5us翻转一次,clk-us一个周期为0.001s
  47. clk_s <= ~ clk_s;
  48. else
  49. clk_s <= clk_s;
  50. end
  51. clk_wiz_0 clk_wiz_0(
  52. .reset(1'b0),
  53. .clk_in1(clk),

3、基于DHT11的温湿度传感器

3.1、DHT11

        详情参考技术手册。

        采用单线双向串口通信,一次性传输四十个字节的二进制数据,高位先输出。数据每八位分为一组,第一组为温度整数;第二组为温度小数;第三组为湿度整数;第四组为湿度小数;第五组为校验码,在正常情况下第五组应为前四组数据之和。

        但对于简单的温湿度测量来说,实际上有用的只有前24位,即前三组。因为在不拓展的情况下,湿度小数的部分输出数据一直为0,即8'b 0000_0000。

        DHT11的通信串口需要并联一个5K欧姆左右的上拉电阻使其在非工作状态保持高电平,其控制主要分为6部分:

  1.         主机拉低电平18ms以上,推荐为20ms,但不要太长;
  2.         主机拉高20~40us等待辅机(DHT11)应答,推荐为30us;
  3.         从机拉低80us;
  4.         从机拉高80us,并进入数据传输阶段;
  5.         传输数据;
  6.         拉低50us后结束。

        对于数据采集中0或1的判断,通过每个字节高电平的时间来判断。每个字节由恒定的50us低电平开始,若高电平持续时间为20~28us,则为0;若高电平持续时间为70us,则为1。

        在此时需要注意两个问题:

  1. 由于0与1的高电平持续时间相差较大,故可以用一个阈值来区分0与1,如小于50us就为0,大于50us就为1;
  2. 0与1的判定一定要在高电平结束后,也就是端口的下降沿进行,负责会出现错误数据。

3.2、基本思路

        对于整个程序,将其分为三个模块进行。

  1. 首先DHT11控制模块读取数据,并将数据传输给数据转换模块;
  2. 数据转换将一组八位二进制数据转换为两个四位二进制数据,分别为各位数据与十位数据,以配合数码管输出十进制数,并将数据传输给数码管模块;
  3. 数码管模块将二进制数据转换为共阳二极管所对应的格式,并与相应的显示匹配,最终输出。

          此外,额外设置一个按钮flag_key来控制温湿度的切换显示。2fe997dd027f4c61b173f77f3b0e29f6.jpeg

 

3.3、数据分离模块(BTD)

         此模块较为简单,有四个输入:时钟clk、复位rst、整数部分输入int_in、小数部分输入dec_in;四个输出:int_out1,2和dec_out1,2,分别对应整数、小数的十位与个位。

        十位分离过程直接通过对10('b1010)整除即可以实现;个位分离过程通过原数据减去十倍的十位部分即可得到。

        代码如下:

  1. `timescale 1ns / 1ps
  2. module BTD(
  3. input clk50,
  4. input [7:0] int_in,
  5. input [7:0] dec_in,
  6. output [3:0] int_out1,
  7. output [3:0] int_out2,
  8. output [3:0] dec_out1,
  9. output [3:0] dec_out2,
  10. input rst
  11. );
  12. //定义
  13. reg [7:0]intbr;
  14. reg [3:0]in1;
  15. reg [3:0]in2;
  16. reg [3:0]de1;
  17. reg [3:0]de2;
  18. reg [7:0]decbr;
  19. //整数位
  20. always@(posedge clk50)begin
  21. intbr = int_in;
  22. in1 = intbr/4'b1010;
  23. in2 = intbr-in1*4'b1010;
  24. end
  25. //小数位
  26. always@(posedge clk50)begin
  27. decbr = dec_in;
  28. de1 = decbr/4'b1010;
  29. de2 = decbr-de1*4'b1010;
  30. end
  31. //赋值
  32. assign int_out1 = in1;
  33. assign int_out2 = in2;
  34. assign dec_out1 = de1;
  35. assign dec_out2 = de2;
  36. endmodule

3.4、数据转换模块(SMG)

        数据转换模块结构简单,主体部分即为四个数据选择器。

        需要注意的仅有共阳数码管为低电平发光以及数据赋值的顺序为从右往左为从低到高。

        代码如下:

  1. `timescale 1ns / 1ps
  2. module SMG(
  3. input clk50,
  4. input rst,
  5. input data_flag,//0-hu 1-te//没有用
  6. input [3:0] int_in1,//D
  7. input [3:0] dec_in1,//D
  8. input [3:0] int_in2,//D
  9. input [3:0] dec_in2,//D
  10. output [6:0] data_out1,
  11. output [6:0] data_out2,
  12. output [6:0] data_out3,
  13. output [6:0] data_out4
  14. );
  15. //定义
  16. reg [3:0]numr;
  17. reg [6:0]datar1;
  18. reg [6:0]datar2;
  19. reg [6:0]datar3;
  20. reg [6:0]datar4;
  21. reg [31:0]cnt;
  22. reg [3:0]temp_data_1;
  23. reg [3:0]temp_data_2;
  24. reg [3:0]temp_data_3;
  25. reg [3:0]temp_data_4;
  26. reg int1;
  27. reg int2;
  28. reg dec1;
  29. reg dec2;
  30. //输出赋值
  31. assign data_out1 = datar1;
  32. assign data_out2 = datar2;
  33. assign data_out3 = datar3;
  34. assign data_out4 = datar4;
  35. //数据选择器
  36. //小数点显示位数选择 转换数据选择
  37. always@(posedge clk50)//0.02us 一次
  38. begin
  39. temp_data_1 = int_in1;
  40. temp_data_2 = int_in2;
  41. temp_data_3 = dec_in1;
  42. temp_data_4 = dec_in2;
  43. end
  44. //数据选择器 数据转换
  45. always@(posedge clk50)
  46. begin
  47. case(temp_data_1)//gfedcba
  48. 4'b0000:datar1 = 7'b1000000;//0
  49. 4'b0001:datar1 = 7'b1111001;
  50. 4'b0010:datar1 = 7'b0100100;
  51. 4'b0011:datar1 = 7'b0110000;
  52. 4'b0100:datar1 = 7'b0011001;
  53. 4'b0101:datar1 = 7'b0010010;
  54. 4'b0110:datar1 = 7'b0000010;
  55. 4'b0111:datar1 = 7'b1111000;
  56. 4'b1000:datar1 = 7'b0000000;
  57. 4'b1001:datar1 = 7'b0010000;
  58. endcase
  59. end
  60. always@(posedge clk50)
  61. begin
  62. case(temp_data_2)//gfedcba
  63. 4'b0000:datar2 = 7'b1000000;//0
  64. 4'b0001:datar2 = 7'b1111001;
  65. 4'b0010:datar2 = 7'b0100100;
  66. 4'b0011:datar2 = 7'b0110000;
  67. 4'b0100:datar2 = 7'b0011001;
  68. 4'b0101:datar2 = 7'b0010010;
  69. 4'b0110:datar2 = 7'b0000010;
  70. 4'b0111:datar2 = 7'b1111000;
  71. 4'b1000:datar2 = 7'b0000000;
  72. 4'b1001:datar2 = 7'b0011000;
  73. endcase
  74. end
  75. always@(posedge clk50)
  76. begin
  77. case(temp_data_3)//gfedcba
  78. 4'b0000:datar3 = 7'b1000000;//0
  79. 4'b0001:datar3 = 7'b1111001;
  80. 4'b0010:datar3 = 7'b0100100;
  81. 4'b0011:datar3 = 7'b0110000;
  82. 4'b0100:datar3 = 7'b0011001;
  83. 4'b0101:datar3 = 7'b0010010;
  84. 4'b0110:datar3 = 7'b0000010;
  85. 4'b0111:datar3 = 7'b1111000;
  86. 4'b1000:datar3 = 7'b0000000;
  87. 4'b1001:datar3 = 7'b0011000;
  88. endcase
  89. end
  90. always@(posedge clk50)
  91. begin
  92. case(temp_data_4)//gfedcba
  93. 4'b0000:datar4 = 7'b1000000;//0
  94. 4'b0001:datar4 = 7'b1111001;
  95. 4'b0010:datar4 = 7'b0100100;
  96. 4'b0011:datar4 = 7'b0110000;
  97. 4'b0100:datar4 = 7'b0011001;
  98. 4'b0101:datar4 = 7'b0010010;
  99. 4'b0110:datar4 = 7'b0000010;
  100. 4'b0111:datar4 = 7'b1111000;
  101. 4'b1000:datar4 = 7'b0000000;
  102. 4'b1001:datar4 = 7'b0011000;
  103. endcase
  104. end
  105. endmodule

3.5、DHT11控制模块

        DHT11的控制主要有四个重点:

3.5.1、上升、下降沿的判定

        通过两个指示信号来实现,代码如下:

  1. //通过dht11的上升下降沿来控制步进次数,确保在输入一个字节是bitcnt等累加器只会加一次,同时控制判别器在电平下降时才进行判断
  2. always@(posedge clk_us or negedge sys_rst)
  3. begin
  4. if(sys_rst)//复位是两个都是1
  5. begin
  6. dht11_reg1 <= 1'b1;
  7. dht11_reg2 <= 1'b1;
  8. end
  9. else
  10. begin
  11. dht11_reg1 <= dht11;//读入数据,非上升下降沿时,二者相等,pos、neg均为0,
  12. //上升沿:reg1 = 1,reg2 = 0,pos = 1,neg = 0;
  13. //下降沿:reg1 = 0,reg2 = 1,pos = 0,neg = 1;
  14. dht11_reg2 <= dht11_reg1;//reg2 = reg1
  15. end
  16. end
  17. assign dht11_pos = (dht11_reg1) & (~dht11_reg2);//posedge,时钟上升沿
  18. assign dht11_neg = (~dht11_reg1) & (dht11_reg2);//negedge,时钟下降沿

3.5.2、端口IO状态控制

        主机仅在前两个阶段生成开始指示信号是控制端口,其他阶段仅作为接收端。

        IO状态的控制可以通过对dht11进行赋值来实现,当主机输出时dht11为具体值,当辅机输出时dht11为未知量1'bz

        代码如下:

  1. //数值判定 IO状态 与 输出值
  2. always@(posedge clk_us or negedge sys_rst)
  3. begin
  4. if(sys_rst)begin
  5. dht11_en <= 1'b0;
  6. dht11_out <= 1'b1;
  7. end
  8. else if(state == WAIT_1S)//等待系统稳定
  9. begin
  10. dht11_en <= 1'b1;
  11. dht11_out <= 1'b1;
  12. end
  13. else if(state == START)//wait 1s or start
  14. begin
  15. dht11_en <= 1'b1;
  16. dht11_out <= 1'b0;
  17. if(cnt_us == LOW_18MS_MAX)
  18. dht11_out <= 1'b1;
  19. end
  20. else //其他状态
  21. begin
  22. dht11_en <= 1'b0;
  23. dht11_out <=1'b0;
  24. end
  25. end
  26. //赋值
  27. assign dht11 = dht11_en ? dht11_out : 1'bz;

3.5.3、状态判断

        状态判断主要通过两个计数器进行,一个记录高电平以及非工作时间、一个记录低电平时间。状态转换的判定通过上升、下降沿,计数器取值来进行,每当状态进行转换,计数器清零一次。

        计数器控制代码:

  1. //DHT11状态变量控制
  2. always@(posedge clk_us or negedge sys_rst)
  3. begin
  4. if(sys_rst)//复位
  5. begin
  6. cnt_low = 20'd0;
  7. cnt_us = 20'd0;
  8. end
  9. else
  10. begin
  11. case(state)
  12. WAIT_1S:begin//初始状态,
  13. if(cnt_us == WAIT_1S_MAX)//若满足时间1s
  14. cnt_us = 20'd0;
  15. else //not 到1s时,每1us cnt us+1,要加1E6次
  16. cnt_us = cnt_us + 1'b1;
  17. end
  18. START :begin//主机拉低频率18ms
  19. if(cnt_us == LOW_18MS_MAX)//在清零后cnt us 重新开始计数,直到到达18ms
  20. cnt_us = 20'd0;
  21. else
  22. cnt_us = cnt_us + 1'b1;
  23. end
  24. DLY_1 :begin//等待从机发出信号,延时30us后 cntus清零
  25. if(cnt_us == 20'd29)
  26. cnt_us = 20'd0;
  27. else
  28. cnt_us = cnt_us + 1'b1;
  29. end
  30. REPLY :begin//从机发射响应信号
  31. if(dht11_pos == 1'b1 && (cnt_low > 80))//从机相应80us后,清零、进入下一状态
  32. begin
  33. cnt_low = 20'd0;
  34. cnt_us = 20'd0;
  35. end
  36. else if(dht11 == 1'b0)//DATA为低电平,但为满足状态,持续计数
  37. begin
  38. cnt_low = cnt_low + 1'b1;//低电平时间计数
  39. cnt_us = cnt_us + 1'b1;//时间计数
  40. end
  41. else if(cnt_us > 1000)//若超过1ms,归零,重新开始
  42. begin
  43. cnt_low <= 20'd0;
  44. cnt_us <= 20'd0;
  45. end
  46. else //系统不稳定,计时但是不记录低电平时间
  47. begin
  48. cnt_low = cnt_low;
  49. cnt_us = cnt_us + 1'b1;
  50. end
  51. end
  52. DLY_2 :begin//等待
  53. if(dht11_neg == 1'b1 && (cnt_us > 80))//拉高80us响应后,进入读取阶段
  54. cnt_us = 20'd0;
  55. else
  56. cnt_us = cnt_us + 1'b1;
  57. end
  58. RD_DATA:begin//传输信号数据
  59. if(dht11_neg == 1'b1 || dht11_pos == 1'b1)//df与dr均为1时归零(电平变化时)
  60. begin
  61. cnt_us = 1'b0;
  62. end
  63. else
  64. cnt_us = cnt_us + 1'b1;//计时
  65. end
  66. default://其他情况归零
  67. begin
  68. cnt_low = 20'd0;
  69. cnt_us = 20'd0;
  70. end
  71. endcase
  72. end
  73. end

        状态控制代码:

  1. //状态控制,控制变量在后续模块
  2. always@(posedge clk_us or negedge sys_rst)//微妙时钟或复位
  3. begin
  4. if(sys_rst)//rst = 0
  5. state <= WAIT_1S; //等待1s,初始状态为state = wait 1s
  6. else
  7. begin
  8. case(state)
  9. WAIT_1S:begin
  10. if(cnt_us == WAIT_1S_MAX)//1s后,state = start,同时cnt us清零
  11. state = START;
  12. else
  13. state = WAIT_1S;
  14. end
  15. START :begin//主机发出开始信号
  16. if(cnt_us == LOW_18MS_MAX)//到达18ms后cnt us清零,进入等待1状态
  17. state = DLY_1;
  18. else
  19. state = START;
  20. end
  21. DLY_1 :begin//拉高30us表示结束
  22. if(cnt_us == 20'd30)//10us后进入从机相应状态
  23. state = REPLY;
  24. else
  25. state = DLY_1;
  26. end
  27. REPLY :begin//从机回应
  28. if(dht11_pos == 1'b1 && (cnt_low > 80))//低电平计数80us以上,进入等待2状态,等待传感器数据
  29. state = DLY_2;
  30. else if(cnt_us > 1000)//表示等待了1MS,重新开始
  31. state = START;
  32. else
  33. state = REPLY;
  34. end
  35. DLY_2 :begin//拉高80us后进入数据读取
  36. if(dht11_neg == 1'b1 && cnt_us >80)
  37. state = RD_DATA;
  38. else
  39. state = DLY_2;
  40. end
  41. RD_DATA:begin
  42. if(bit_cnt == 40 && dht11_pos == 1'b1)//读取完40bit数据后重新开始
  43. state = START;//读完后立刻返回开始
  44. else//否则持续进行
  45. state = RD_DATA;
  46. end
  47. default:state = WAIT_1S;
  48. endcase
  49. end
  50. end

3.5.4、数据读入

        代码如下:

  1. //依次对data——temp写入信号数据,输出data-temp
  2. always@(posedge clk_us or negedge sys_rst)
  3. begin
  4. if(sys_rst)
  5. data_temp <= 40'd0;
  6. else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us< 50 ) //下降沿是出发,确保高点平占时完全被记录
  7. data_temp[39 - bit_cnt] <= 1'b0; //DHT11先输出高位,因此从高位向低位依次赋值
  8. else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us > 50)
  9. data_temp[39 - bit_cnt] <= 1'b1;//小于50us当成0,大于50us当成1
  10. else
  11. data_temp <= data_temp;
  12. end
  13. //数据校验和赋值,输出data
  14. always@(posedge clk_us or negedge sys_rst)
  15. begin
  16. if(sys_rst)
  17. data <= 32'd0;
  18. else if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8])
  19. data <= data_temp[39:8];//校验位无误时将数据赋值给data
  20. else
  21. data <= data;
  22. end

3.5.5、完整代码

        第一组:

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/06/11 15:49:57
  7. // Design Name:
  8. // Module Name: dht11_ctrl
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module dht11_ctrl(sys_clk,sys_rst,data_flag,dht11,data_out, test_leds);
  22. input sys_clk;//clk-50Mhz
  23. input sys_rst;//rst,触发为1
  24. input data_flag;//控制输出温度还是湿度
  25. inout dht11;//既可输入又可输出,即为DATA线
  26. output [15:0] data_out;//输出16位信号
  27. output [3:0] test_leds;
  28. //状态定义
  29. parameter WAIT_1S = 6'b000_001,//上电等待1s状态
  30. START = 6'b000_010,//主机拉低20ms,发送开始信号状态
  31. DLY_1 = 6'b000_100,//等待从机答应
  32. REPLY = 6'b001_000,//从机对主机发出发送信号
  33. DLY_2 = 6'b010_000,//等待主机回应
  34. RD_DATA = 6'b100_000;//开始传输数据
  35. parameter WAIT_1S_MAX = 32'd999_999,
  36. LOW_18MS_MAX = 20'd19_999;
  37. wire dht11_pos;
  38. wire dht11_neg;
  39. reg clk_us;
  40. reg [4:0] cnt;//微妙时钟计数器
  41. reg [5:0] state;
  42. reg [19:0] cnt_us;//微秒计时器
  43. reg [19:0] cnt_low;//低电平计时器
  44. reg dht11_reg1;//时钟沿计数器
  45. reg dht11_reg2;//时钟沿计数器
  46. reg [5:0] bit_cnt;//数据写入计数器
  47. reg [39:0] data_temp;//全部数据
  48. reg [31:0] data;//校验后数据
  49. reg dht11_en;
  50. reg dht11_out;
  51. reg [3:0] led;
  52. //微秒时钟1,一个周期0.5us
  53. always@(posedge sys_clk or negedge sys_rst)
  54. begin//一个周期为25时钟周期,0.02*25=0.5us,一个周期0.5us
  55. if(sys_rst)
  56. cnt <= 5'd0;
  57. else if(cnt == 5'd24)//微秒计数器
  58. cnt <= 5'd0;
  59. else
  60. cnt <= cnt + 1'b1;
  61. end
  62. //微秒时钟2,由cnt控制
  63. always@(posedge sys_clk or negedge sys_rst)
  64. begin
  65. if(sys_rst)
  66. clk_us <= 1'b0;
  67. else if(cnt== 5'd24)//将cnt-us换为cnt,此时一个没0.5us翻转一次,clk-us一个周期为1us
  68. clk_us <= ~ clk_us;
  69. else
  70. clk_us <= clk_us;
  71. end
  72. //状态控制,控制变量在后续模块
  73. always@(posedge clk_us or negedge sys_rst)//微妙时钟或复位
  74. begin
  75. if(sys_rst)//rst = 0
  76. state <= WAIT_1S; //等待1s,初始状态为state = wait 1s
  77. else
  78. begin
  79. case(state)
  80. WAIT_1S:begin
  81. if(cnt_us == WAIT_1S_MAX)//1s后,state = start,同时cnt us清零
  82. state = START;
  83. else
  84. state = WAIT_1S;
  85. end
  86. START :begin//主机发出开始信号
  87. if(cnt_us == LOW_18MS_MAX)//到达18ms后cnt us清零,进入等待1状态
  88. state = DLY_1;
  89. else
  90. state = START;
  91. end
  92. DLY_1 :begin//拉高30us表示结束
  93. if(cnt_us == 20'd30)//10us后进入从机相应状态
  94. state = REPLY;
  95. else
  96. state = DLY_1;
  97. end
  98. REPLY :begin//从机回应
  99. if(dht11_pos == 1'b1 && (cnt_low > 80))//低电平计数80us以上,进入等待2状态,等待传感器数据
  100. state = DLY_2;
  101. else if(cnt_us > 1000)//表示等待了1MS,重新开始
  102. state = START;
  103. else
  104. state = REPLY;
  105. end
  106. DLY_2 :begin//拉高80us后进入数据读取
  107. if(dht11_neg == 1'b1 && cnt_us >80)
  108. state = RD_DATA;
  109. else
  110. state = DLY_2;
  111. end
  112. RD_DATA:begin
  113. if(bit_cnt == 40 && dht11_pos == 1'b1)//读取完40bit数据后重新开始
  114. state = START;//读完后立刻返回开始
  115. else//否则持续进行
  116. state = RD_DATA;
  117. end
  118. default:state = WAIT_1S;
  119. endcase
  120. end
  121. end
  122. //DHT11状态变量控制
  123. always@(posedge clk_us or negedge sys_rst)
  124. begin
  125. if(sys_rst)//复位
  126. begin
  127. cnt_low = 20'd0;
  128. cnt_us = 20'd0;
  129. end
  130. else
  131. begin
  132. case(state)
  133. WAIT_1S:begin//初始状态,
  134. if(cnt_us == WAIT_1S_MAX)//若满足时间1s
  135. cnt_us = 20'd0;
  136. else //not 到1s时,每1us cnt us+1,要加1E6次
  137. cnt_us = cnt_us + 1'b1;
  138. end
  139. START :begin//主机拉低频率18ms
  140. if(cnt_us == LOW_18MS_MAX)//在清零后cnt us 重新开始计数,直到到达18ms
  141. cnt_us = 20'd0;
  142. else
  143. cnt_us = cnt_us + 1'b1;
  144. end
  145. DLY_1 :begin//等待从机发出信号,延时30us后 cntus清零
  146. if(cnt_us == 20'd29)
  147. cnt_us = 20'd0;
  148. else
  149. cnt_us = cnt_us + 1'b1;
  150. end
  151. REPLY :begin//从机发射响应信号
  152. if(dht11_pos == 1'b1 && (cnt_low > 80))//从机相应80us后,清零、进入下一状态
  153. begin
  154. cnt_low = 20'd0;
  155. cnt_us = 20'd0;
  156. end
  157. else if(dht11 == 1'b0)//DATA为低电平,但为满足状态,持续计数
  158. begin
  159. cnt_low = cnt_low + 1'b1;//低电平时间计数
  160. cnt_us = cnt_us + 1'b1;//时间计数
  161. end
  162. else if(cnt_us > 1000)//若超过1ms,归零,重新开始
  163. begin
  164. cnt_low <= 20'd0;
  165. cnt_us <= 20'd0;
  166. end
  167. else //系统不稳定,计时但是不记录低电平时间
  168. begin
  169. cnt_low = cnt_low;
  170. cnt_us = cnt_us + 1'b1;
  171. end
  172. end
  173. DLY_2 :begin//等待
  174. if(dht11_neg == 1'b1 && (cnt_us > 80))//拉高80us响应后,进入读取阶段
  175. cnt_us = 20'd0;
  176. else
  177. cnt_us = cnt_us + 1'b1;
  178. end
  179. RD_DATA:begin//传输信号数据
  180. if(dht11_neg == 1'b1 || dht11_pos == 1'b1)//df与dr均为1时归零(电平变化时)
  181. begin
  182. cnt_us = 1'b0;
  183. end
  184. else
  185. cnt_us = cnt_us + 1'b1;//计时
  186. end
  187. default://其他情况归零
  188. begin
  189. cnt_low = 20'd0;
  190. cnt_us = 20'd0;
  191. end
  192. endcase
  193. end
  194. end
  195. reg ct;
  196. always@(posedge clk_us)
  197. begin
  198. if(cnt_us == 20'b1000)
  199. ct = 1;
  200. if(ct == 1)
  201. led[0] = 1;
  202. else if(ct == 0)
  203. led[0] = 0;
  204. end
  205. //通过dht11的上升下降沿来控制步进次数,确保在输入一个字节是bitcnt等累加器只会加一次,同时控制判别器在电平下降时才进行判断
  206. always@(posedge clk_us or negedge sys_rst)
  207. begin
  208. if(sys_rst)//复位是两个都是1
  209. begin
  210. dht11_reg1 <= 1'b1;
  211. dht11_reg2 <= 1'b1;
  212. end
  213. else
  214. begin
  215. dht11_reg1 <= dht11;//读入数据,非上升下降沿时,二者相等,pos、neg均为0,
  216. //上升沿:reg1 = 1,reg2 = 0,pos = 1,neg = 0;
  217. //下降沿:reg1 = 0,reg2 = 1,pos = 0,neg = 1;
  218. dht11_reg2 <= dht11_reg1;//reg2 = reg1
  219. end
  220. end
  221. assign dht11_pos = (dht11_reg1) & (~dht11_reg2);//posedge,时钟上升沿
  222. assign dht11_neg = (~dht11_reg1) & (dht11_reg2);//negedge,时钟下降沿
  223. //数据位数控制,输出bit-cnt
  224. always@(posedge clk_us or negedge sys_rst)
  225. begin
  226. if(sys_rst)
  227. bit_cnt <= 6'd0;
  228. else if(bit_cnt == 40 && dht11_pos == 1'b1)
  229. bit_cnt <= 6'd0;
  230. else if((state == RD_DATA) && (dht11_neg == 1'b1))
  231. bit_cnt <= bit_cnt + 1'b1;//当状态为写入且dht11-neg = 1时,bit—cnt依次变化
  232. else
  233. bit_cnt <= bit_cnt;
  234. end
  235. //依次对data——temp写入信号数据,输出data-temp
  236. always@(posedge clk_us or negedge sys_rst)
  237. begin
  238. if(sys_rst)
  239. data_temp <= 40'd0;
  240. else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us< 50 ) //下降沿是出发,确保高点平占时完全被记录
  241. data_temp[39 - bit_cnt] <= 1'b0; //DHT11先输出高位,因此从高位向低位依次赋值
  242. else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us > 50)
  243. data_temp[39 - bit_cnt] <= 1'b1;//小于50us当成0,大于50us当成1
  244. else
  245. data_temp <= data_temp;
  246. end
  247. always@(posedge clk_us)
  248. begin
  249. if(data_temp == 0)
  250. led[1] = 1;
  251. end
  252. //数据校验和赋值,输出data
  253. always@(posedge clk_us or negedge sys_rst)
  254. begin
  255. if(sys_rst)
  256. data <= 32'd0;
  257. else if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8])
  258. data <= data_temp[39:8];//校验位无误时将数据赋值给data
  259. else
  260. data <= data;
  261. end
  262. //IO控制
  263. always@(posedge clk_us or negedge sys_rst)
  264. begin
  265. if(sys_rst)begin
  266. dht11_en <= 1'b0;
  267. dht11_out <= 1'b1;
  268. end
  269. else if(state == WAIT_1S)//等待系统稳定
  270. begin
  271. dht11_en <= 1'b1;
  272. dht11_out <= 1'b1;
  273. end
  274. else if(state == START)//wait 1s or start
  275. begin
  276. dht11_en <= 1'b1;
  277. dht11_out <= 1'b0;
  278. if(cnt_us == LOW_18MS_MAX)
  279. dht11_out <= 1'b1;
  280. end
  281. else //其他状态
  282. begin
  283. dht11_en <= 1'b0;
  284. dht11_out <=1'b0;
  285. end
  286. end
  287. assign dht11 = dht11_en ? dht11_out : 1'bz;
  288. //当dht11en = 1时,dht11 = dht11 out,当en = 0 时,dht11 = 未知
  289. //start时dht11 受主机控制,等于0--低电平
  290. //其他时候,dht11为未知量,由辅机控制
  291. reg [15:0] datar;
  292. always@(posedge clk_us or negedge sys_rst)//温湿度选择器,输出data-out
  293. begin
  294. if(sys_rst)
  295. datar <= 16'd0;//十六位
  296. else if(data_flag == 1'b0)//data为32位 整温-小温-整湿-小湿
  297. datar <= data[31:16];//湿度数据
  298. else if(data_flag == 1'b1)
  299. datar <= data[15:0];//湿度数据
  300. else
  301. datar <= data_out;
  302. end
  303. assign data_out = datar;
  304. always@(posedge clk_us)
  305. begin
  306. if(data == 0 /*&& data_temp != 0*/)
  307. led[2] = 1;
  308. else if(datar == 0)
  309. led[3] = 1;
  310. end
  311. endmodule

        第二组:

  1. `timescale 1ns / 1ps
  2. module dht11_2(
  3. input sys_clk , //system clock
  4. input sys_rst_n , //system reset negedge
  5. inout dht11_data , //dht11 inout port
  6. output reg [39:0] t_h_data
  7. );
  8. //tate code
  9. parameter WAIT = 6'b000_001,//wait state 2s
  10. START = 6'b000_010,//make bus low 20ms
  11. WAIT_RES = 6'b000_100,//wait respond
  12. RES_LOW = 6'b001_000,//respond low
  13. RES_HIGH = 6'b010_000,//respong high
  14. REC_DATA = 6'b100_000;//receive datas
  15. //time parameter
  16. parameter CNT_2S_MAX = 100_000_000 ,
  17. CNT_20MS_MAX = 1_000_000 ,
  18. CNT_1US_MAX = 50 ;
  19. //state define
  20. reg [5:0] state_cur;//current state
  21. reg [5:0] state_nex;//next state
  22. //lag define
  23. wire end_2s ; //wait 2s end
  24. wire end_20ms ; //wait 20ms end
  25. wire res_ok ; //respond ok
  26. wire res_no ; //no respond
  27. wire end_res_low ; //wait respond low end 83us
  28. wire end_res_high; //wait respond high end 87us
  29. wire end_rec ; //data receive end 40bits
  30. //dht11
  31. reg dht11_data_r1;
  32. reg dht11_data_r2;
  33. wire dht11_posedge;
  34. wire dht11_negedge;
  35. reg data;
  36. reg output_en;
  37. wire check;//校验
  38. reg [39:0] t_h_data_temp;//温湿度数据
  39. //计数器
  40. reg [26:0] cnt_2s;
  41. reg [19:0] cnt_20ms;
  42. reg [6:0] cnt_nus;
  43. reg [5:0] cnt_1us;
  44. reg cnt_us_rst;
  45. reg [5:0] cnt_bit;
  46. //条件判断
  47. assign end_2s = (state_cur == WAIT && cnt_2s == CNT_2S_MAX - 1'b1) ? 1'b1 : 1'b0;
  48. assign end_20ms = (state_cur == START && cnt_20ms == CNT_20MS_MAX - 1'b1) ? 1'b1 : 1'b0;
  49. assign res_ok = (state_cur == WAIT_RES && cnt_nus < 20 && dht11_negedge) ? 1'b1 : 1'b0;
  50. assign res_no = (state_cur == WAIT_RES && cnt_nus > 20) ? 1'b1 : 1'b0;
  51. assign end_res_low = (state_cur == RES_LOW && cnt_nus > 70 && dht11_posedge) ? 1'b1 : 1'b0;
  52. assign end_res_high = (state_cur == RES_HIGH && cnt_nus > 70 && dht11_negedge) ? 1'b1 : 1'b0;
  53. assign end_rec = (state_cur == REC_DATA && cnt_bit >= 40) ? 1'b1 : 1'b0;
  54. //dht11传输上升、下降延判断
  55. assign dht11_posedge = dht11_data_r1 & ~dht11_data_r2;
  56. assign dht11_negedge = ~dht11_data_r1 & dht11_data_r2;
  57. //生成
  58. always@(posedge sys_clk or negedge sys_rst_n)begin
  59. if(~sys_rst_n)begin
  60. dht11_data_r1 <= 1'b0;
  61. dht11_data_r2 <= 1'b0;
  62. end
  63. else begin
  64. dht11_data_r1 <= dht11_data;
  65. dht11_data_r2 <= dht11_data_r1;
  66. end
  67. end
  68. //信息校验
  69. assign check = (t_h_data_temp[39:32]+t_h_data_temp[31:24]+
  70. t_h_data_temp[23:16]+t_h_data_temp[15:8] == t_h_data_temp[7:0])
  71. ? 1'b1 : 1'b0;
  72. //计数器群
  73. always@(*)begin
  74. case(state_cur)
  75. WAIT : cnt_us_rst = 1'b1;
  76. START : cnt_us_rst = 1'b1;
  77. WAIT_RES : begin
  78. if(res_ok)
  79. cnt_us_rst = 1'b1;
  80. else
  81. cnt_us_rst = 1'b0;
  82. end
  83. RES_LOW : begin
  84. if(end_res_low)
  85. cnt_us_rst = 1'b1;
  86. else
  87. cnt_us_rst = 1'b0;
  88. end
  89. RES_HIGH : begin
  90. if(end_res_high)
  91. cnt_us_rst = 1'b1;
  92. else
  93. cnt_us_rst = 1'b0;
  94. end
  95. REC_DATA : begin
  96. if(dht11_posedge || dht11_negedge)
  97. cnt_us_rst = 1'b1;
  98. else
  99. cnt_us_rst = 1'b0;
  100. end
  101. default :cnt_us_rst = 1'b1;
  102. endcase
  103. end
  104. //cnt_2s
  105. always@(posedge sys_clk or negedge sys_rst_n)begin
  106. if(~sys_rst_n)begin
  107. cnt_2s <= 27'd0;
  108. end
  109. else begin
  110. if(state_cur == WAIT)begin
  111. if(cnt_2s <= CNT_2S_MAX - 1'b1)
  112. cnt_2s <= cnt_2s + 1'b1;
  113. else
  114. cnt_2s <= cnt_2s;
  115. end
  116. else if(state_cur == REC_DATA)begin
  117. cnt_2s <= 27'd0;
  118. end
  119. else begin
  120. cnt_2s <= cnt_2s;
  121. end
  122. end
  123. end
  124. //cnt_20ms
  125. always@(posedge sys_clk or negedge sys_rst_n)begin
  126. if(~sys_rst_n)begin
  127. cnt_20ms <= 20'd0;
  128. end
  129. else begin
  130. if(state_cur == START)begin
  131. if(cnt_20ms <= CNT_20MS_MAX - 1'b1)
  132. cnt_20ms <= cnt_20ms + 1'b1;
  133. else
  134. cnt_20ms <= cnt_20ms;
  135. end
  136. else if(state_cur == REC_DATA)begin
  137. cnt_20ms <= 20'd0;
  138. end
  139. else begin
  140. cnt_20ms <= cnt_20ms;
  141. end
  142. end
  143. end
  144. //cnt_1us
  145. always@(posedge sys_clk or negedge sys_rst_n)begin
  146. if(~sys_rst_n)begin
  147. cnt_1us <= 6'd0;
  148. end
  149. else begin
  150. if(cnt_1us == CNT_1US_MAX - 1'b1)
  151. cnt_1us <= 6'd0;
  152. else if(cnt_us_rst)
  153. cnt_1us <= 6'd0;
  154. else
  155. cnt_1us <= cnt_1us + 1'b1;
  156. end
  157. end
  158. //cnt_nus
  159. always@(posedge sys_clk or negedge sys_rst_n)begin
  160. if(~sys_rst_n)begin
  161. cnt_nus <= 7'd0;
  162. end
  163. else begin
  164. if(cnt_us_rst)
  165. cnt_nus <= 7'd0;
  166. else if(cnt_1us == CNT_1US_MAX - 1'b1)
  167. cnt_nus <= cnt_nus + 1'b1;
  168. else
  169. cnt_nus <= cnt_nus;
  170. end
  171. end
  172. //信号位数控制
  173. always@(posedge sys_clk or negedge sys_rst_n)begin
  174. if(~sys_rst_n)begin
  175. cnt_bit <= 6'd0;
  176. end
  177. else begin
  178. if(state_cur == REC_DATA)begin
  179. if(dht11_negedge)
  180. cnt_bit <= cnt_bit + 1'b1;
  181. else
  182. cnt_bit <= cnt_bit;
  183. end
  184. else begin
  185. cnt_bit <= 6'd0;
  186. end
  187. end
  188. end
  189. //状态控制1
  190. always@(posedge sys_clk or negedge sys_rst_n)begin
  191. if(~sys_rst_n)
  192. state_cur <= WAIT;
  193. else
  194. state_cur <= state_nex;
  195. end
  196. //状态控制2
  197. always@(*)begin
  198. case(state_cur)
  199. WAIT :begin
  200. if(end_2s)
  201. state_nex = START; //count 2s finish
  202. else
  203. state_nex = WAIT;
  204. end
  205. START :begin
  206. if(end_20ms)
  207. state_nex = WAIT_RES;//count 20ms finish
  208. else
  209. state_nex = START;
  210. end
  211. WAIT_RES:begin
  212. if(res_ok) //respond
  213. state_nex = RES_LOW;
  214. else if(res_no) //no respond
  215. state_nex = WAIT;
  216. else
  217. state_nex = WAIT_RES;
  218. end
  219. RES_LOW :begin
  220. if(end_res_low)
  221. state_nex = RES_HIGH;
  222. else
  223. state_nex = RES_LOW;
  224. end
  225. RES_HIGH:begin
  226. if(end_res_high)
  227. state_nex = REC_DATA;
  228. else
  229. state_nex = RES_HIGH;
  230. end
  231. REC_DATA:begin
  232. if(end_rec)
  233. state_nex = WAIT;
  234. else
  235. state_nex = REC_DATA;
  236. end
  237. default :begin
  238. state_nex = WAIT;
  239. end
  240. endcase
  241. end
  242. //IO控制
  243. assign dht11_data = output_en ? data : 1'bz;
  244. always@(posedge sys_clk or negedge sys_rst_n)begin
  245. if(~sys_rst_n)begin
  246. output_en <= 1'b0;
  247. data <= 1'b0;
  248. end
  249. else begin
  250. case(state_cur)
  251. WAIT :begin
  252. output_en <= 1'b1;//output
  253. data <= 1'b1;
  254. end
  255. START :begin
  256. output_en <= 1'b1;//output
  257. data <= 1'b0;
  258. if(end_20ms)
  259. data <= 1'b1;
  260. end
  261. WAIT_RES :begin
  262. output_en <= 1'b0;//input
  263. data <= 1'b0;
  264. end
  265. RES_LOW :begin
  266. output_en <= 1'b0;//input
  267. data <= 1'b0;
  268. end
  269. RES_HIGH :begin
  270. output_en <= 1'b0;//input
  271. data <= 1'b0;
  272. end
  273. REC_DATA :begin
  274. output_en <= 1'b0;//input
  275. data <= 1'b0;
  276. end
  277. default :begin
  278. output_en <= 1'b0;//input
  279. data <= 1'b0;
  280. end
  281. endcase
  282. end
  283. end
  284. //写入1
  285. always@(posedge sys_clk or negedge sys_rst_n)begin
  286. if(~sys_rst_n)begin
  287. t_h_data_temp <= 40'd0;
  288. end
  289. else begin
  290. if(state_cur == REC_DATA)begin
  291. if(cnt_nus > 50 && dht11_negedge)
  292. t_h_data_temp[39 - cnt_bit] <= 1'b1;
  293. else if(cnt_nus < 50 && dht11_negedge)
  294. t_h_data_temp[39 - cnt_bit] <= 1'b0;
  295. else
  296. t_h_data_temp <= t_h_data_temp;
  297. end
  298. else begin
  299. t_h_data_temp <= t_h_data_temp;
  300. end
  301. end
  302. end
  303. //写入2
  304. always@(posedge sys_clk or negedge sys_rst_n)begin
  305. if(~sys_rst_n)begin
  306. t_h_data <= 40'd0;
  307. end
  308. else begin
  309. if(state_cur == REC_DATA)begin
  310. if(end_rec && check)
  311. t_h_data <= t_h_data_temp;
  312. else
  313. t_h_data <= t_h_data;
  314. end
  315. else begin
  316. t_h_data <= t_h_data;
  317. end
  318. end
  319. end
  320. endmodule

3.6、TOP

        顶层模块需要注意两点:

        1、若数据只是“经过”顶层模块或是作为赋值对象而不对其改变,则需要用wire型变量,否则将会报错;

        2、最好保持输出位置与输出数据的控制时序相同,千万不要让输出数据的改变快于输出位置的改变,否则数码管最终会显示非理想的输出。而非理想输出的原因可能在于:数据转换模块有问题;DHT11控制模块有问题;数码管模块有问题。本人就因此将所有代码从头到尾检查了一遍。

        代码如下:

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/06/11 14:45:14
  7. // Design Name:
  8. // Module Name: DHT_TOP
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module DHT_TOP(
  22. input sys_clk,
  23. input sys_rst,//触发为1 D20
  24. input tap8421,
  25. input key, //D19
  26. inout dht11, //V17 AR8
  27. output point, //AR7
  28. output [3:0]num, //A0-5
  29. output [6:0]abcdefg, //AR0-6
  30. output [3:0]leds
  31. );
  32. reg data_flag;
  33. //显示切换 1--温度 0--湿度
  34. wire [15:0] data_ctrl;
  35. wire [39:0] thdata;
  36. //不同模块之间传输而不在顶层改变的变量用wire型
  37. //应用时钟 自用 输出给dht11——ctrl 输出给——SMG
  38. wire clk50;
  39. //数据选择器所用变量
  40. reg [7:0] data_hu_int;//十位湿度整数
  41. reg [7:0] data_te_int;//十位温度整数
  42. reg [7:0] data_hu_dec;//十位湿度小数
  43. reg [7:0] data_te_dec;//十位温度小数
  44. //SMG 4个数据信号
  45. //BTD 小数点内设,不需要输入
  46. reg [7:0] data_smg_int;
  47. reg [7:0] data_smg_dec;
  48. //SMG的输入 BTD的输出
  49. wire [3:0] int_d1;//四位十进制
  50. wire [3:0] int_d2;//四位十进制
  51. wire [3:0] dec_d1;//四位十进制
  52. wire [3:0] dec_d2;//四位十进制
  53. reg [3:0] numr;
  54. reg pointr;
  55. //test
  56. wire [6:0] a1;
  57. wire [6:0] a2;
  58. wire [6:0] a3;
  59. wire [6:0] a4;
  60. reg [6:0] ar;
  61. assign abcdefg = ar;
  62. assign num = numr;
  63. assign point = pointr;
  64. //dht11控制程序
  65. /*
  66. dht11_ctrl dht11_ctrl
  67. (
  68. .sys_clk(clk50), //input
  69. .sys_rst(sys_rst),//input
  70. .data_flag(data_flag), //input 温湿度选择
  71. .dht11(dht11), //DHT11数据 input
  72. .data_out(data_ctrl),//输出数据 output
  73. .test_leds(leds)
  74. );
  75. */
  76. //输出控制 8421BCD TO ABCDEFG
  77. dht11_2 dht11_2
  78. (
  79. .sys_clk(clk50),
  80. .sys_rst_n(!sys_rst),
  81. .dht11_data(dht11),
  82. .t_h_data(thdata)
  83. );
  84. assign leds = int_d1;
  85. //assign leds = dec_d1;
  86. SMG SMG
  87. (
  88. .clk50(clk50),//in
  89. .rst(sys_rst),//in normal = 0
  90. .data_flag(data_flag),//in 0h 1t
  91. .int_in1(int_d1),//in d
  92. .dec_in1(dec_d1),//in d
  93. .int_in2(int_d2),//in d
  94. .dec_in2(dec_d2),//in d
  95. .data_out1(a1),
  96. .data_out2(a2),
  97. .data_out3(a3),
  98. .data_out4(a4)
  99. //out 8421bcd to abcdefg
  100. );
  101. //BTD SMG TO 4 8421 BCD
  102. BTD BTD
  103. (
  104. .clk50(clk50),
  105. .int_in(data_smg_int[7:0]),
  106. .dec_in(data_smg_dec[7:0]),
  107. //.int_in('b01010100),//
  108. //.dec_in('b00010101),//
  109. //DHT11有效测试范围不过100,因此仅前7位信号有效
  110. .int_out1(int_d1),
  111. .dec_out1(dec_d1),
  112. .int_out2(int_d2),
  113. .dec_out2(dec_d2),
  114. .rst(sys_rst)
  115. );
  116. //温湿度切换
  117. //通过按键的key信号,转换data_out输出的是湿度还是温度,没按一次按钮,转换一次
  118. always@(posedge clk50 or negedge sys_rst)
  119. begin
  120. if(sys_rst)
  121. data_flag <= 1'b0;
  122. else if(key == 1'b1)
  123. data_flag <= ~data_flag;
  124. else
  125. data_flag <= data_flag;
  126. end
  127. //温湿度选择器,输出data-out
  128. always@(posedge clk50 or negedge sys_rst)
  129. begin
  130. if(sys_rst)
  131. begin
  132. end
  133. else if(data_flag == 1'b0)//data为32位 整温-小温-整湿-小湿
  134. //data_flag为0时data_crtl为湿度数据
  135. begin
  136. data_hu_int <= thdata[39:32];//湿度数据
  137. data_hu_dec <= thdata[31:24];//湿度数据
  138. end
  139. else if(data_flag == 1'b1)//te
  140. begin
  141. data_te_int <= thdata[23:16];//温度数据
  142. data_te_dec <= thdata[15:8];//温度数据
  143. end
  144. end
  145. //数据选择器1
  146. always@(posedge clk50)
  147. begin
  148. if(sys_rst)begin
  149. numr = 4'b1111;
  150. end
  151. else if(numr == 4'b1000)
  152. numr = 4'b0001;
  153. else
  154. numr = numr <<1;
  155. end
  156. always@(posedge clk50)
  157. begin
  158. case(numr)
  159. 4'b0001:begin ar = a1;
  160. end
  161. 4'b0010:begin ar = a2;
  162. end
  163. 4'b0100:begin ar = a3;
  164. end
  165. 4'b1000:begin ar = a4;
  166. end
  167. 4'b1111:begin ar = 7'b0111111;
  168. end
  169. endcase
  170. end
  171. always@(posedge clk50)
  172. begin
  173. if(numr == 4'b0010)
  174. pointr = 1'b0;
  175. else
  176. pointr = 1'b1;
  177. end
  178. //smg赋值
  179. always@(posedge clk50 or negedge sys_rst)
  180. begin
  181. if(sys_rst)
  182. begin
  183. data_smg_int <= 8'b0000_0000;
  184. data_smg_dec <= 8'b0000_0000;
  185. end
  186. else if (tap8421)
  187. begin
  188. data_smg_int <= 8'b0101_0100;//84
  189. data_smg_dec <= 8'b0001_0101;//21
  190. end
  191. else if(data_flag == 1'b1 && !tap8421)//温度
  192. begin
  193. data_smg_int <= data_te_int;
  194. data_smg_dec <= data_te_dec;
  195. end
  196. else if(data_flag == 1'b0 && !tap8421)//湿度
  197. begin
  198. data_smg_int <= data_hu_int;
  199. data_smg_dec <= data_hu_dec;
  200. end
  201. end
  202. //时钟
  203. clk_wiz_0 clk0(
  204. .reset(1'b0),
  205. .clk_in1(sys_clk),
  206. .clk50(clk50)
  207. );
  208. endmodule

3.7、结果展示

        湿度:37%

55d0ce5b3a894b699c2dd050171b3c65.jpeg        

         温度:28.09

d7bc7f3b4fa04702b3b1de168b9364e9.jpeg

         城市参考值:

87da2cc6b2c54f3a9f04a6f85b128c06.png

         可以看到实际数据与城市参考值基本上差不多,同时证明在旁边放一桶水确实能够提高空气湿度。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/432224
推荐阅读
相关标签
  

闽ICP备14008679号