当前位置:   article > 正文

温湿度测量模块DHT11使用方法(FPGA)_dht11温湿度传感器使用手册

dht11温湿度传感器使用手册

1.DHT11工作流程

        DHT11采用了简化的单总线通讯。当DHT11在上电一秒后收到来自控制器(FPGA)发出的起始信号后,会向控制器发送一个响应信号,随后便会发送40位的数据。

        起始信号:一个时长大于18ms小于30ms的低电平

        响应信号:

        数据格式:

        工作时序图:

        数据格式:

        校验位=湿度高8位+湿度低8位+温度高8位+温度低8位;

        湿度高8位对应湿度的整数部分,湿度低8位对应湿度的小数部分;

        温度高8位对应温度的整数部分,温度低8位对应温度的小数部分(当温度的低8位的最高位为1时表示此时测量到的温度为0下);

      !!!  主机从DHT11读取的温湿度数据总是前一次的测量值,如两次测间隔时间很长,请连 续读两次以第二次获得的值为实时温湿度值。

2.代码详解

2.1起始信号的发送

1.对使能信号en进行两次缓存,data_reg在输入时钟的上升沿对来自DHT11的数据进行采样;

  1. always @(posedge clk or negedge rstn )
  2. begin
  3. if (!rstn)
  4. begin
  5. en_reg0<=1'b0;
  6. en_reg1<=1'b0;
  7. end
  8. else
  9. begin
  10. en_reg0<=en;
  11. en_reg1<=en_reg0;
  12. end
  13. end
  14. always @(posedge clk )
  15. begin
  16. data_reg<=data;
  17. end

2.当起始信号发送标志位send_flag有效时,对输入的50Mhz时钟clk进行分频产生一个周期为1ms的时钟clk_1ms,在clk时钟的上升沿对clk_1ms进行采样,采样结果放在寄存器clk_reg0和clk_reg1中(clk_reg1滞后于clk_reg0一个clk周期)。当~clk_reg1&clk_reg0(clk_1ms出现一个上升沿)时cnt_19ms+1;

        为什么不直接用产生的周期为1ms的时钟clk_1ms直接进行计数,而是用对clk_reg0和clk_reg1来判断clk_1ms是否出现上升沿从而进行计数?

        这是因为send_flag是在一个以clk为时钟的状态机中产生的,如果用clk_1ms直接进行计数,当cnt_19ms计到特定时,状态机在clk的时钟控制下跳转到下一个状态使send_flag无效,此时clk_1ms为低电平且不会在发生向高电平的跳变,此时就会导致寄存器cnt_19ms无法被正确的置0,导致下次测量使能en信号来临时模块无法正常工作;

  1. always @(posedge clk or negedge rstn)
  2. begin
  3. if(!rstn)
  4. begin
  5. clk_1ms<=1'b0;
  6. cnt_1ms<=15'd0;
  7. end
  8. else if (send_flag)
  9. begin
  10. cnt_1ms<=cnt_1ms==num_1ms ? 15'd0 : cnt_1ms+15'd1;
  11. clk_1ms<=cnt_1ms==num_1ms ? ~clk_1ms : clk_1ms;
  12. end
  13. else
  14. begin
  15. clk_1ms<=1'b0;
  16. cnt_1ms<=15'd0;
  17. end
  18. end
  19. reg clk_reg0,clk_reg1;
  20. always @(posedge clk or negedge rstn)
  21. begin
  22. if (!rstn)
  23. begin
  24. clk_reg0<=0;
  25. clk_reg1<=0;
  26. end
  27. else if (send_flag)
  28. begin
  29. clk_reg0<=clk_1ms;
  30. clk_reg1<=clk_reg0;
  31. end
  32. else
  33. begin
  34. clk_reg0<=0;
  35. clk_reg1<=0;
  36. end
  37. end
  38. wire cnt_en=~clk_reg1&clk_reg0;
  39. always@(posedge clk or negedge rstn)
  40. begin
  41. if (!rstn)
  42. begin
  43. cnt_19ms<=5'd0;
  44. end
  45. else if (cnt_en)
  46. begin
  47. cnt_19ms<=cnt_19ms+5'd1;
  48. end
  49. else if (!send_flag)
  50. begin
  51. cnt_19ms<=5'd0;
  52. end
  53. else
  54. begin
  55. cnt_19ms<=cnt_19ms;
  56. end
  57. end

3.由于DHT11采用的是单总线控制,只有一条数据线,来自控制器(FPGA)和DHT11的数据都在这条数据线上传输。所以在FPGA中需要设置一个三态门来应对这种情况,在verlog中三态门对于的端口类型为inout。

 当dir_reg为1时三态门为输出模式,此时FPGA可以通过该端口向外发送数据,当dir_reg为0时三态门为输入模式,此时FPGA可以通过该端口接收来自DHT11的数据。 

状态机:当检测到en_reg1滞后en_reg0一个周期,当检测到测量控制en出现下降沿后send_flag为1,三态门设置为输出模式并向外输出电平直至cnt_19ms为20(定时19.5ms)。当cnt_19ms为20时三态门三态门配置为输入模式,flag_rce置1准备开始接收来自DHT11的40位数据。当完成数据的接收后状态机回到初始状态准备接收下一次的测量开始信号(en出现下降沿)

        为什么要在接收完数据后才跳转到初始状态?

为了避免在数据接收的过程中,en有效从而打断一次正常的数据接收。

  1. always @(posedge clk or negedge rstn)
  2. begin
  3. if (!rstn)
  4. begin
  5. st0<=s0;
  6. send_flag<=1'b0;
  7. dir_reg<=1'b0;
  8. out_2_dht<=1'b1;
  9. flag_rce<=1'b0;
  10. end
  11. else
  12. begin
  13. case(st0)
  14. s0:begin
  15. st0<=(en_reg1&~en_reg0) ? s1 : s0;
  16. end
  17. s1:begin
  18. send_flag<=1'b1;
  19. st0<= cnt_19ms==20?s2:s1;
  20. dir_reg<=1'b1;
  21. out_2_dht<=1'b0;
  22. end
  23. s2:
  24. begin
  25. dir_reg<=1'b0;
  26. out_2_dht<=1'b1;
  27. send_flag<=1'b0;
  28. flag_rce<=1'b1;
  29. st0<=s3;
  30. end
  31. s3:
  32. begin
  33. flag_rce<=1'b0;
  34. st0<= done_reg==1'b1 ? s0 : s3;
  35. end
  36. endcase
  37. end
  38. end
  39. assign io_dir=dir_reg;
  40. assign data_2_dht11=out_2_dht;

2.2数据的接收

当检测到数据接收有效信号flag_rce为1时跳转到下一个状态等待数据总线被释放,当数据总线被释放后开始接收来自DHT11的响应信号(一个83us的低电平和一个87us的高电平),若接收到的响应信号不满足用户手册中给出的最小值则认为数据传输有误跳转到s_erro,结束数据的接收并输出一个clk时钟高电平的error信号。若接收到的响应信号符合要求则进行来自DHT11的40位数据的接收。通过观察DHT11工作流程中的数据格式我们可以发现,数据“0”和数据“1”只有高电平的时候是有差异的,所以我采用了一个比较偷懒的办法,只对数据的高电平进行计数,如果计数结果小于用户手册数据“0”的最大时间,则认为该数据为0反之为1。

 在接收完40个数据后,根据DHT11工作流程中检验位的产生方法,生成校验位与接收到的数据的低8位进行比对,若相等则此次数据有效,进行数据的输出并结束,若不相等则跳转到s_erro.

  1. always @(posedge clk or negedge rstn)
  2. begin
  3. if (!rstn)
  4. begin
  5. bit_cnt<=6'd39;
  6. st<=s0;
  7. cnt<=13'd0;
  8. error_reg<=1'b0;
  9. d_reg<=39'd0;
  10. done_reg<=1'b0;
  11. parity_bit<=8'd0;
  12. end
  13. else
  14. begin
  15. case(st)
  16. s0: begin
  17. st<= flag_rce ? s1: s0;
  18. end
  19. s1: begin
  20. st<= data_reg==1'b1 ? s2 :s1;
  21. end
  22. s2:begin
  23. st<= data_reg==1'b0 ? s3: s2;
  24. cnt<=13'd1;
  25. end
  26. s3:begin
  27. st<= data_reg==1'b1 ? s4 :s3;
  28. cnt<=cnt+13'd1;
  29. end
  30. s4:begin
  31. cnt<=13'd2;
  32. st<= cnt>13'd4000 ? s5 : s_erro;
  33. end
  34. s5:begin
  35. st<= data_reg==1'b0 ? s6 :s5;
  36. cnt<=cnt+13'd1;
  37. end
  38. s6:begin
  39. cnt<=13'd1;
  40. st<= cnt>13'd4200 ? s7 : s_erro;
  41. end
  42. s7:begin
  43. st<= data_reg==1'b1 ? s8 :s7;
  44. cnt<=13'd1;
  45. end
  46. s8:begin
  47. cnt<=cnt+13'd1;
  48. st<= data_reg==1'b0 ? s9 : s8;
  49. end
  50. s9:begin
  51. d_reg[bit_cnt]<=cnt<13'd1400 ? 1'b0:1'b1;
  52. bit_cnt<= bit_cnt==6'd0 ? 6'd0 :bit_cnt-6'd1;
  53. st<= bit_cnt==6'd0 ? s10 : s7;
  54. end
  55. s10:begin
  56. parity_bit=d_reg[39:32]+d_reg[31:24]+d_reg[23:16]+d_reg[15:8];
  57. st<=s11;
  58. end
  59. s11:begin
  60. done_reg<=parity_bit==d_reg[7:0] ? 1'b1: 1'b0;
  61. d0<=parity_bit==d_reg[7:0] ? d_reg: 40'd0;
  62. st<=parity_bit==d_reg[7:0] ? s12:s_erro;
  63. end
  64. s12:begin
  65. bit_cnt<=6'd39;
  66. st<=s0;
  67. cnt<=13'd0;
  68. error_reg<=1'b0;
  69. d_reg<=39'd0;
  70. done_reg<=1'b0;
  71. end
  72. s_erro:begin
  73. done_reg<=1'b1;
  74. error_reg<=1'b1;
  75. d0<=40'd0;
  76. st<=s12;
  77. end
  78. endcase
  79. end
  80. end

3.整体代码

3.1DHT11完整驱动代码

  1. module DHT11(
  2. input clk,rstn,
  3. data,en,
  4. output reg [39:0] d0,
  5. output done0,erro,
  6. output io_dir,data_2_dht11
  7. );
  8. parameter num_1ms=15'd24_999;
  9. localparam s0=4'b0000,s1=4'b0001,s2=4'b0011,s3=4'b0010,s4=4'b0110,s5=4'b0111,s6=4'b0101,s7=4'b0100,s8=4'b1100,s9=4'b1101,s10=4'b1111,s11=4'b1110,s12=4'b1010, s_erro=4'b1000;
  10. reg en_reg0,en_reg1;
  11. reg send_flag,flag_rce,done_reg,data_reg;
  12. reg [3:0]st0;
  13. reg [4:0]cnt_19ms;
  14. reg dir_reg,out_2_dht;
  15. always @(posedge clk or negedge rstn )
  16. begin
  17. if (!rstn)
  18. begin
  19. en_reg0<=1'b0;
  20. en_reg1<=1'b0;
  21. end
  22. else
  23. begin
  24. en_reg0<=en;
  25. en_reg1<=en_reg0;
  26. end
  27. end
  28. always @(posedge clk )
  29. begin
  30. data_reg<=data;
  31. end
  32. always @(posedge clk or negedge rstn)
  33. begin
  34. if (!rstn)
  35. begin
  36. st0<=s0;
  37. send_flag<=1'b0;
  38. dir_reg<=1'b0;
  39. out_2_dht<=1'b1;
  40. flag_rce<=1'b0;
  41. end
  42. else
  43. begin
  44. case(st0)
  45. s0:begin
  46. st0<=(en_reg1&~en_reg0) ? s1 : s0;
  47. end
  48. s1:begin
  49. send_flag<=1'b1;
  50. st0<= cnt_19ms==20?s2:s1;
  51. dir_reg<=1'b1;
  52. out_2_dht<=1'b0;
  53. end
  54. s2:
  55. begin
  56. dir_reg<=1'b0;
  57. out_2_dht<=1'b1;
  58. send_flag<=1'b0;
  59. flag_rce<=1'b1;
  60. st0<=s3;
  61. end
  62. s3:
  63. begin
  64. flag_rce<=1'b0;
  65. st0<= done_reg==1'b1 ? s0 : s3;
  66. end
  67. endcase
  68. end
  69. end
  70. assign io_dir=dir_reg;
  71. assign data_2_dht11=out_2_dht;
  72. reg [14:0]cnt_1ms;
  73. reg clk_1ms;
  74. always @(posedge clk or negedge rstn)
  75. begin
  76. if(!rstn)
  77. begin
  78. clk_1ms<=1'b0;
  79. cnt_1ms<=15'd0;
  80. end
  81. else if (send_flag)
  82. begin
  83. cnt_1ms<=cnt_1ms==num_1ms ? 15'd0 : cnt_1ms+15'd1;
  84. clk_1ms<=cnt_1ms==num_1ms ? ~clk_1ms : clk_1ms;
  85. end
  86. else
  87. begin
  88. clk_1ms<=1'b0;
  89. cnt_1ms<=15'd0;
  90. end
  91. end
  92. reg clk_reg0,clk_reg1;
  93. always @(posedge clk or negedge rstn)
  94. begin
  95. if (!rstn)
  96. begin
  97. clk_reg0<=0;
  98. clk_reg1<=0;
  99. end
  100. else if (send_flag)
  101. begin
  102. clk_reg0<=clk_1ms;
  103. clk_reg1<=clk_reg0;
  104. end
  105. else
  106. begin
  107. clk_reg0<=0;
  108. clk_reg1<=0;
  109. end
  110. end
  111. wire cnt_en=~clk_reg1&clk_reg0;
  112. always@(posedge clk or negedge rstn)
  113. begin
  114. if (!rstn)
  115. begin
  116. cnt_19ms<=5'd0;
  117. end
  118. else if (cnt_en)
  119. begin
  120. cnt_19ms<=cnt_19ms+5'd1;
  121. end
  122. else if (!send_flag)
  123. begin
  124. cnt_19ms<=5'd0;
  125. end
  126. else
  127. begin
  128. cnt_19ms<=cnt_19ms;
  129. end
  130. end
  131. reg [39:0]d_reg;
  132. reg [12:0]cnt;
  133. reg [3:0]st;
  134. reg error_reg;
  135. reg [5:0] bit_cnt;
  136. reg [7:0] parity_bit;
  137. always @(posedge clk or negedge rstn)
  138. begin
  139. if (!rstn)
  140. begin
  141. bit_cnt<=6'd39;
  142. st<=s0;
  143. cnt<=13'd0;
  144. error_reg<=1'b0;
  145. d_reg<=39'd0;
  146. done_reg<=1'b0;
  147. parity_bit<=8'd0;
  148. end
  149. else
  150. begin
  151. case(st)
  152. s0: begin
  153. st<= flag_rce ? s1: s0;
  154. end
  155. s1: begin
  156. st<= data_reg==1'b1 ? s2 :s1;
  157. end
  158. s2:begin
  159. st<= data_reg==1'b0 ? s3: s2;
  160. cnt<=13'd1;
  161. end
  162. s3:begin
  163. st<= data_reg==1'b1 ? s4 :s3;
  164. cnt<=cnt+13'd1;
  165. end
  166. s4:begin
  167. cnt<=13'd2;
  168. st<= cnt>13'd4000 ? s5 : s_erro;
  169. end
  170. s5:begin
  171. st<= data_reg==1'b0 ? s6 :s5;
  172. cnt<=cnt+13'd1;
  173. end
  174. s6:begin
  175. cnt<=13'd1;
  176. st<= cnt>13'd4200 ? s7 : s_erro;
  177. end
  178. s7:begin
  179. st<= data_reg==1'b1 ? s8 :s7;
  180. cnt<=13'd1;
  181. end
  182. s8:begin
  183. cnt<=cnt+13'd1;
  184. st<= data_reg==1'b0 ? s9 : s8;
  185. end
  186. s9:begin
  187. d_reg[bit_cnt]<=cnt<13'd1400 ? 1'b0:1'b1;
  188. bit_cnt<= bit_cnt==6'd0 ? 6'd0 :bit_cnt-6'd1;
  189. st<= bit_cnt==6'd0 ? s10 : s7;
  190. end
  191. s10:begin
  192. parity_bit=d_reg[39:32]+d_reg[31:24]+d_reg[23:16]+d_reg[15:8];
  193. st<=s11;
  194. end
  195. s11:begin
  196. done_reg<=parity_bit==d_reg[7:0] ? 1'b1: 1'b0;
  197. d0<=parity_bit==d_reg[7:0] ? d_reg: 40'd0;
  198. st<=parity_bit==d_reg[7:0] ? s12:s_erro;
  199. end
  200. s12:begin
  201. bit_cnt<=6'd39;
  202. st<=s0;
  203. cnt<=13'd0;
  204. error_reg<=1'b0;
  205. d_reg<=39'd0;
  206. done_reg<=1'b0;
  207. end
  208. s_erro:begin
  209. done_reg<=1'b1;
  210. error_reg<=1'b1;
  211. d0<=40'd0;
  212. st<=s12;
  213. end
  214. endcase
  215. end
  216. end
  217. assign done0=done_reg;
  218. assign erro=error_reg;
  219. endmodule

3.2调用示例

当复位后1s,led灯亮起此时按下按键en开始进行一次温湿度数据采集,若采集到的数据有误则会重新进行一次采集直至采集到正确数据。采集到的数据通过sender模块由通过串口发送到上位机。

  1. module DHT11_code (
  2. input clk ,rstn,en,
  3. inout data_dht11,
  4. output tx,
  5. output led,test
  6. );
  7. reg[26:0] cnt;
  8. reg ready;
  9. always @ (posedge clk or negedge rstn)
  10. begin
  11. if (!rstn)
  12. begin
  13. cnt<=27'd0;
  14. ready<=1'b0;
  15. end
  16. else
  17. begin
  18. cnt<=cnt==27'd50_000_000 ? 27'd50_000_000:cnt+27'd1;
  19. ready<= cnt==27'd50_000_000 ? 1'b1:1'b0;
  20. end
  21. end
  22. assign led=~ready;
  23. reg clk_us;
  24. reg [4:0] cnt_num;
  25. always @(posedge clk or negedge rstn)
  26. begin
  27. if (!rstn)
  28. begin
  29. clk_us<=1'b0;
  30. cnt_num<=5'd0;
  31. end
  32. else
  33. begin
  34. cnt_num<=cnt_num==5'd25 ? 5'd0:cnt_num+5'd1;
  35. clk_us<=cnt_num==5'd25 ? ~clk_us:clk_us;
  36. end
  37. end
  38. reg key_buff0,key_buff1;
  39. always @(posedge clk_us or negedge rstn)
  40. begin
  41. if(!rstn)
  42. begin
  43. key_buff0<=1'b1;
  44. key_buff1<=1'b1;
  45. end
  46. else
  47. begin
  48. key_buff0<=en;
  49. key_buff1<=key_buff0;
  50. end
  51. end
  52. wire en_flag,erro;
  53. assign en_flag=key_buff1&~key_buff0&ready|erro;
  54. wire out_2_dht11;
  55. wire io_ctrl;
  56. assign data_dht11 = io_ctrl==1'b1 ? out_2_dht11 : 1'bz;
  57. wire [39:0]dht11_data;
  58. wire done_2_uart;
  59. DHT11 U0(
  60. .clk(clk),.rstn(rstn),
  61. .data(data_dht11),.en(en_flag),
  62. .d0(dht11_data),
  63. .done0(done_2_uart),.erro(erro),
  64. .io_dir(io_ctrl),.data_2_dht11(out_2_dht11)
  65. );
  66. wire send_done;
  67. sender U1(.clk(clk),.rstn(rstn),.data_flag(done_2_uart),
  68. .data(data_dht11),
  69. .done(send_done),.tx_d(tx)
  70. );
  71. endmodule

用signaltap II抓到的DHT11 模块的输出结果。

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

闽ICP备14008679号