目錄
1、 FPGA的DDS信號(hào)發(fā)生器
1.1、DDS簡(jiǎn)介
1.2、ROM IP核的生成
1.3、波形數(shù)據(jù)的生成
1.4、 ROM的調(diào)用
1.5、 完整代碼(包括拓展部分)
2、數(shù)碼管顯示
2.1、數(shù)碼管簡(jiǎn)要說(shuō)明
2.2、SM410564
3、基于DHT11的溫濕度傳感器
3.1、DHT11
3.2、基本思路
3.3、數(shù)據(jù)分離模塊(BTD)
3.4、數(shù)據(jù)轉(zhuǎn)換模塊(SMG)
3.5、DHT11控制模塊
3.5.1、上升、下降沿的判定
3.5.2、端口IO狀態(tài)控制
3.5.3、狀態(tài)判斷
3.5.4、數(shù)據(jù)讀入
3.5.5、完整代碼
3.6、TOP
3.7、結(jié)果展示
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-764084.html
1、 FPGA的DDS信號(hào)發(fā)生器
1.1、DDS簡(jiǎn)介
? ? ? ? DSS全稱為“直接數(shù)字式頻率合成”,通過(guò)邏輯高低電平的相加合成數(shù)字信號(hào),在將數(shù)信信號(hào)轉(zhuǎn)換,生成模擬信號(hào)。
? ? ? ? DSS信號(hào)發(fā)生器的實(shí)現(xiàn)主要分為三個(gè)步驟:
- ? ? ? ? 生成所需波形數(shù)據(jù)并儲(chǔ)存到IP核“ROM”中;
- ? ? ? ? 讀取波形數(shù)據(jù)并輸出數(shù)字信號(hào);
- ? ? ? ? 將數(shù)字信號(hào)通過(guò)DA轉(zhuǎn)換模塊轉(zhuǎn)換成模擬信號(hào)。
1.2、ROM IP核的生成
? ? ? ? 調(diào)用ROM,首先需要生成一個(gè)ROM。步驟如下:
????????1.? ? ? ? ?PROJECT MANAGER -- IP Catalog;
????????2.? ? ? ? 在IP Catalog界面搜索ROM,選擇RAMs & ROMs & BRAM -- Block Memory Generator;
????????3.? ? ? ? 選擇所需要的Memory Type ,這里我們選擇Single Port ROM;?
????????4.? ? ? 設(shè)定Memory Size,根據(jù)所需要的Width 與 Depth,這里我們由于所用的波形數(shù)據(jù)為256個(gè)采樣點(diǎn),故設(shè)定Width = 8;Depth = 256;
? ? ? ????????5.????????設(shè)定波形文件,在Memory Initialization中勾選 Load Init File,然后在Coe File一欄選擇對(duì)于的文件,隨后點(diǎn)擊OK,這樣ROM就生成完畢了。
? ? ? ? ? ? ? 調(diào)用端口如下:
rom_name rom_name(
.clka( ),
//input ck
.addra( ),
//input address
.douta( )
//output data
);
? ? ? ? ?????????需要注意,rom的地址從0開(kāi)始,依次向上加1。如0地址的下一位地址為1。
1.3、波形數(shù)據(jù)的生成
? ? ? ? 波形數(shù)據(jù)生成較為簡(jiǎn)單,MATLAB代碼如下:
clc;
clear all;
N=2^8;
A=2^8;%振幅
f=1;9
F1=0;%相位
F2=256;
n=[0:N-1];
s1=round((A/2)*sin(2*pi*n/N+pi*F1)+F2/2);%256/2為0
fild=fopen('wave_dds_sin.coe','wt');
fprintf(fild,'memory_initialization_radix=10;\n');
fprintf(fild,'memory_initialization_vector=\n');
for i=1:N
if(s1(i)==256)
s1(i)=255;
end
fprintf(fild,'%d,\n',s1(i));
end
fclose(fild);
plot(s1)
? ? ? ? 通過(guò)程序生成單周期有256個(gè)數(shù)據(jù)點(diǎn)的正弦波,A為峰峰值。
? ? ? ? 需要注意最小值為0,而最大值并非256,而是255,因?yàn)镽OM所設(shè)置的Width為8,僅能儲(chǔ)存八位二進(jìn)制數(shù),而256為“ 'b100000000 ” 。若最大值為256,則會(huì)在ROM選擇信號(hào)源文件時(shí)報(bào)錯(cuò)。
1.4、 ROM的調(diào)用
? ? ? ? 假設(shè)系統(tǒng)時(shí)鐘頻率為50MHz,一個(gè)周期輸出一個(gè)數(shù)據(jù)點(diǎn),則輸出波形頻率為
? ? ? ? ? ? ? ? ? ? ? ? Fout = 50M/256 Hz = 195312.5Hz
? ? ? ? 若想要改變輸出波形的頻率,可以生成一個(gè)相位累加器,俗稱加法器,同時(shí)設(shè)置一個(gè)頻率控制字Fword來(lái)控制加法器累加的速度。
? ? ? ? 雖然在固定的時(shí)鐘頻率下加法器每次累加的速度是一樣的,但是可以調(diào)整每次累加的步長(zhǎng)來(lái)控制其達(dá)到最大值的速度。
? ? ? ? 頻率控制字、加法器長(zhǎng)度(位數(shù))N、時(shí)鐘頻率以及輸出波形頻率關(guān)系如下:
? ? ? ? ? ? ? ? Fword * clk = Fout * 2 ^ N
? ? ? ? 加法器控制程序如下:
always@(posedge dac_clk or negedge rst )//計(jì)數(shù)器,越界歸零, frequency = 50 MHz
begin
if(rst) begin//當(dāng) rst = 1
Fcnt <= 32'd0;
end
else//當(dāng)rst等于0時(shí)
Fcnt <= Fcnt + Fword;
end
? ? ? ? ? ? ? ? 需要注意的是,由于程序中使用的頻率控制字并不一定為整數(shù),故在計(jì)算時(shí)會(huì)產(chǎn)生一定誤差,但此誤差基本可忽略不計(jì)。
? ? ? ? 由于所用的ROM地址為8位二進(jìn)制數(shù),在一個(gè)周期內(nèi)需要從0變化到255 即 11111111 一次,而累加器同樣需要變到 1111 1111……一次,故可以使用累加器的前八位作為調(diào)用ROM的地址。
assign rom_addr = Fcnt[31:24] + Pword;
1.5、 完整代碼(包括拓展部分)
module DDS_CHEN(
input clk, //fpga clock
input rst,//rst is 0 if no act
input swi,
input [3:0]bot,//按下時(shí)為高電平,邏輯1,控制頻率以及幅度
output dac_clk,
output [7:0]sin_out,
output [4:0] led
);
reg [31:0] Fword;
wire [31:0] Pword;
assign Pword = 8'b0;
wire clk50;//標(biāo)準(zhǔn)時(shí)鐘
wire clk10;//沒(méi)啥用
wire [7:0] wave_data_sin;//讀取數(shù)據(jù)
wire [7:0] wave_data_sq;
reg [7:0] wave_data_r;//賦值數(shù)據(jù);
reg [31:0] Fcnt;//計(jì)數(shù)器
wire [7:0] rom_addr;//地址
reg [4:0]led_r;//測(cè)試用
//實(shí)際時(shí)鐘
assign dac_clk = clk50; //輸出時(shí)鐘
//仿真時(shí)鐘
//assign dac_clk = clk;
//控頻
//50M clk 858993-10k 8589933-100k 429497-5k 85899-1k 4295-50 相位累加器步長(zhǎng)誤差最大為7.6E-6
//Fword = fout*(2^n)/fclk n = 32 fclk = 50MHz
always@( posedge clk50)
begin
casex(bot)
4'b11xx: Fword = 4295;//50
4'b10xx: Fword = 85899;//1k
4'b01xx: Fword = 429497;//5k
4'b00xx: Fword = 858993;//10k
default :Fword = 858993;
endcase
end
//地址控制
always@(posedge dac_clk or negedge rst )//計(jì)數(shù)器,越界歸零, frequency = 50 MHz
begin
if(rst) begin//當(dāng) rst = 1
Fcnt <= 32'd0;
end
else//當(dāng)rst等于0時(shí)
Fcnt <= Fcnt + Fword;
end
assign rom_addr = Fcnt[31:24] + Pword;//the first 8 bit as the address
//指示燈
always@(posedge dac_clk)
begin
if(!rst&&!bot)
led_r <= 5'b1;
else if(!rst&&bot[0])
led_r <= 5'b00010;
else if(!rst&&bot[1])
led_r <= 5'b00100;
else if(!rst&&bot[2])
led_r <= 5'b01000;
else if(!rst&&bot[3])
led_r <= 5'b10000;
else
led_r <=0;
end
//賦值與控幅,控幅還可以通過(guò)外部電路改變電壓來(lái)實(shí)現(xiàn)
always@(posedge dac_clk)
begin
case(swi)
1'b0:
casex(bot)
4'bxx00 : wave_data_r <= wave_data_sin / 4;
4'bxx01 : wave_data_r <= wave_data_sin / 2;
4'bxx10 : wave_data_r <= (wave_data_sin / 4)*3 ;
4'bxx11 : wave_data_r <= wave_data_sin ;
endcase
1'b1:
casex(bot)
4'bxx00 : wave_data_r <= wave_data_sq / 4;
4'bxx01 : wave_data_r <= wave_data_sq / 2;
4'bxx10 : wave_data_r <= (wave_data_sq / 4)*3 ;
4'bxx11 : wave_data_r <= wave_data_sq;
default: wave_data_r <= 114;
endcase
endcase
end
assign sin_out = wave_data_r;
assign led = led_r;
blk_mem_gen_0 blk_mem_gen_0 (
//實(shí)際
.clka(clk50), // input clka
//仿真
//.clka(clk),
.addra(rom_addr), // input [8 : 0] addra
.douta(wave_data_sin) // output [7 : 0] douta
);
blk_mem_gen_1 sq(
//仿真
//.clka(clk),
//實(shí)際
.clka(clk50),
.addra(rom_addr),
.douta(wave_data_sq)
);
clk_wiz_0 clk_wiz_0 (
.clk_in1(clk),
.resetn(1'b1),
.clk_out50M(clk50),
.clk_out10M(clk10),
.locked()
);
endmodule
2、數(shù)碼管顯示
2.1、數(shù)碼管簡(jiǎn)要說(shuō)明
? ? ? ? 一般而言,數(shù)碼管可以分為共陰、共陽(yáng)兩種。
????????共陰為數(shù)碼管的八個(gè)二極管共用一個(gè)陰極,此時(shí)控制不同二極管陽(yáng)極的電平就可以控制其顯示狀態(tài),當(dāng)陽(yáng)極與陰極形成一定電壓差時(shí),二極管導(dǎo)通發(fā)光,即高電平發(fā)光。一般而言,共極低電平表示數(shù)碼管工作。
? ? ? ? 共陽(yáng)為數(shù)碼管的八個(gè)二極管共用一個(gè)陽(yáng)極。一般當(dāng)共陽(yáng)極為高電平時(shí)二極管工作,相應(yīng)二極管陰極為低電平時(shí)發(fā)光。即低電平發(fā)光。
? ? ? ? 共陽(yáng)、共陰極的數(shù)碼管真值表正好完全相反。??
2.2、SM410564
? ? ? ? 本次實(shí)驗(yàn)所使用的是四位共陽(yáng)數(shù)碼管SM410564。當(dāng)正視顯示面時(shí),其管腳約束如下:
?? ? ? ? s1~s4為四個(gè)共陽(yáng)極,控制四個(gè)顯示數(shù)位,可以同時(shí)為高電平,由于共極管腳分別獨(dú)立,故電平高低與否與其他管腳無(wú)關(guān)。
? ? ? ? a~g為顯示數(shù)字的七個(gè)發(fā)光二極管的陰極管腳,dp為小數(shù)點(diǎn)位置發(fā)光二極管的陰極管腳。
? ? ? ? 整個(gè)模塊共由4*8共32個(gè)發(fā)光二極管組成,顯然控制發(fā)光二極管的8個(gè)陰極管腳不足以同時(shí)顯示四個(gè)不同的符號(hào),但可以同時(shí)顯示相同的符號(hào)。故采用交替顯示的方式進(jìn)行輸出。
? ? ? ? 交替顯示通過(guò)對(duì)于每一時(shí)鐘周期,僅讓一個(gè)共極為高電平,并將相應(yīng)真值賦值,在下一個(gè)周期相鄰共極變?yōu)楦唠娖?,?dāng)前共極變?yōu)榈碗娖?,并重新進(jìn)行賦值,如此循環(huán)。假設(shè)時(shí)鐘周期為50M赫茲,則整個(gè)顯示模塊的刷新率即為50Mhz, 而對(duì)于單一的顯示位,刷新率為50M/4 = 12.5MHz遠(yuǎn)大于人眼25Hz或是80Hz的基本要求。
? ? ? ? 完整代碼如下:(刷新了為1lHz,輸出僅為6.6.6.6.)
`timescale 1ns / 1ps
module SMG(
input clk,
input rst,
output [3:0] numo,
output [6:0] datao,
output pointo
);
wire clk50;
reg [3:0]numr;
wire [3:0]numw;
reg [6:0]datar;
wire[6:0]dataw;
reg clk_s;
reg [32:0] cnt;//微妙時(shí)鐘計(jì)數(shù)器
reg pointr;
assign pointo = pointr;
assign numo = numr;
assign datao = datar;
always@(posedge clk_s)//0.001s
begin
datar = 7'b0000_010;//6
pointr = 1'b0;
if(rst)
numr = 4'b0001;
else if(numr ==4'b1000)
numr = 4'b0001;//從s4向s1刷新;
else if(numr !=4'b1000)
numr = numr << 1;
end
//時(shí)鐘
always@(posedge clk50 or negedge rst)
begin//一個(gè)周期為25時(shí)鐘周期,0.02*25*10^6=0.5us,一個(gè)周期0.5us
if(rst)
cnt <= 32'd0;
else if(cnt == 32'd24_999)//毫秒計(jì)數(shù)器
cnt <= 32'd0;
else
cnt <= cnt + 1'b1;
end
//微秒時(shí)鐘2,由cnt控制
always@(posedge clk50 or negedge rst)
begin
if(rst)
clk_s <= 1'b0;
else if(cnt== 32'd24_999)//將cnt-us換為cnt,此時(shí)0.5us翻轉(zhuǎn)一次,clk-us一個(gè)周期為0.001s
clk_s <= ~ clk_s;
else
clk_s <= clk_s;
end
clk_wiz_0 clk_wiz_0(
.reset(1'b0),
.clk_in1(clk),
3、基于DHT11的溫濕度傳感器
3.1、DHT11
? ? ? ? 詳情參考技術(shù)手冊(cè)。
? ? ? ? 采用單線雙向串口通信,一次性傳輸四十個(gè)字節(jié)的二進(jìn)制數(shù)據(jù),高位先輸出。數(shù)據(jù)每八位分為一組,第一組為溫度整數(shù);第二組為溫度小數(shù);第三組為濕度整數(shù);第四組為濕度小數(shù);第五組為校驗(yàn)碼,在正常情況下第五組應(yīng)為前四組數(shù)據(jù)之和。
? ? ? ? 但對(duì)于簡(jiǎn)單的溫濕度測(cè)量來(lái)說(shuō),實(shí)際上有用的只有前24位,即前三組。因?yàn)樵诓煌卣沟那闆r下,濕度小數(shù)的部分輸出數(shù)據(jù)一直為0,即8'b 0000_0000。
? ? ? ? DHT11的通信串口需要并聯(lián)一個(gè)5K歐姆左右的上拉電阻使其在非工作狀態(tài)保持高電平,其控制主要分為6部分:
- ? ? ? ? 主機(jī)拉低電平18ms以上,推薦為20ms,但不要太長(zhǎng);
- ? ? ? ? 主機(jī)拉高20~40us等待輔機(jī)(DHT11)應(yīng)答,推薦為30us;
- ? ? ? ? 從機(jī)拉低80us;
- ? ? ? ? 從機(jī)拉高80us,并進(jìn)入數(shù)據(jù)傳輸階段;
- ? ? ? ? 傳輸數(shù)據(jù);
- ? ? ? ? 拉低50us后結(jié)束。
? ? ? ? 對(duì)于數(shù)據(jù)采集中0或1的判斷,通過(guò)每個(gè)字節(jié)高電平的時(shí)間來(lái)判斷。每個(gè)字節(jié)由恒定的50us低電平開(kāi)始,若高電平持續(xù)時(shí)間為20~28us,則為0;若高電平持續(xù)時(shí)間為70us,則為1。
? ? ? ? 在此時(shí)需要注意兩個(gè)問(wèn)題:
- 由于0與1的高電平持續(xù)時(shí)間相差較大,故可以用一個(gè)閾值來(lái)區(qū)分0與1,如小于50us就為0,大于50us就為1;
- 0與1的判定一定要在高電平結(jié)束后,也就是端口的下降沿進(jìn)行,負(fù)責(zé)會(huì)出現(xiàn)錯(cuò)誤數(shù)據(jù)。
3.2、基本思路
? ? ? ? 對(duì)于整個(gè)程序,將其分為三個(gè)模塊進(jìn)行。
- 首先DHT11控制模塊讀取數(shù)據(jù),并將數(shù)據(jù)傳輸給數(shù)據(jù)轉(zhuǎn)換模塊;
- 數(shù)據(jù)轉(zhuǎn)換將一組八位二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為兩個(gè)四位二進(jìn)制數(shù)據(jù),分別為各位數(shù)據(jù)與十位數(shù)據(jù),以配合數(shù)碼管輸出十進(jìn)制數(shù),并將數(shù)據(jù)傳輸給數(shù)碼管模塊;
- 數(shù)碼管模塊將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為共陽(yáng)二極管所對(duì)應(yīng)的格式,并與相應(yīng)的顯示匹配,最終輸出。
??????????此外,額外設(shè)置一個(gè)按鈕flag_key來(lái)控制溫濕度的切換顯示。
?
3.3、數(shù)據(jù)分離模塊(BTD)
? ? ? ? ?此模塊較為簡(jiǎn)單,有四個(gè)輸入:時(shí)鐘clk、復(fù)位rst、整數(shù)部分輸入int_in、小數(shù)部分輸入dec_in;四個(gè)輸出:int_out1,2和dec_out1,2,分別對(duì)應(yīng)整數(shù)、小數(shù)的十位與個(gè)位。
? ? ? ? 十位分離過(guò)程直接通過(guò)對(duì)10('b1010)整除即可以實(shí)現(xiàn);個(gè)位分離過(guò)程通過(guò)原數(shù)據(jù)減去十倍的十位部分即可得到。
? ? ? ? 代碼如下:
`timescale 1ns / 1ps
module BTD(
input clk50,
input [7:0] int_in,
input [7:0] dec_in,
output [3:0] int_out1,
output [3:0] int_out2,
output [3:0] dec_out1,
output [3:0] dec_out2,
input rst
);
//定義
reg [7:0]intbr;
reg [3:0]in1;
reg [3:0]in2;
reg [3:0]de1;
reg [3:0]de2;
reg [7:0]decbr;
//整數(shù)位
always@(posedge clk50)begin
intbr = int_in;
in1 = intbr/4'b1010;
in2 = intbr-in1*4'b1010;
end
//小數(shù)位
always@(posedge clk50)begin
decbr = dec_in;
de1 = decbr/4'b1010;
de2 = decbr-de1*4'b1010;
end
//賦值
assign int_out1 = in1;
assign int_out2 = in2;
assign dec_out1 = de1;
assign dec_out2 = de2;
endmodule
3.4、數(shù)據(jù)轉(zhuǎn)換模塊(SMG)
? ? ? ? 數(shù)據(jù)轉(zhuǎn)換模塊結(jié)構(gòu)簡(jiǎn)單,主體部分即為四個(gè)數(shù)據(jù)選擇器。
? ? ? ? 需要注意的僅有共陽(yáng)數(shù)碼管為低電平發(fā)光以及數(shù)據(jù)賦值的順序?yàn)閺挠彝鬄閺牡偷礁摺?/p>
? ? ? ? 代碼如下:
`timescale 1ns / 1ps
module SMG(
input clk50,
input rst,
input data_flag,//0-hu 1-te//沒(méi)有用
input [3:0] int_in1,//D
input [3:0] dec_in1,//D
input [3:0] int_in2,//D
input [3:0] dec_in2,//D
output [6:0] data_out1,
output [6:0] data_out2,
output [6:0] data_out3,
output [6:0] data_out4
);
//定義
reg [3:0]numr;
reg [6:0]datar1;
reg [6:0]datar2;
reg [6:0]datar3;
reg [6:0]datar4;
reg [31:0]cnt;
reg [3:0]temp_data_1;
reg [3:0]temp_data_2;
reg [3:0]temp_data_3;
reg [3:0]temp_data_4;
reg int1;
reg int2;
reg dec1;
reg dec2;
//輸出賦值
assign data_out1 = datar1;
assign data_out2 = datar2;
assign data_out3 = datar3;
assign data_out4 = datar4;
//數(shù)據(jù)選擇器
//小數(shù)點(diǎn)顯示位數(shù)選擇 轉(zhuǎn)換數(shù)據(jù)選擇
always@(posedge clk50)//0.02us 一次
begin
temp_data_1 = int_in1;
temp_data_2 = int_in2;
temp_data_3 = dec_in1;
temp_data_4 = dec_in2;
end
//數(shù)據(jù)選擇器 數(shù)據(jù)轉(zhuǎn)換
always@(posedge clk50)
begin
case(temp_data_1)//gfedcba
4'b0000:datar1 = 7'b1000000;//0
4'b0001:datar1 = 7'b1111001;
4'b0010:datar1 = 7'b0100100;
4'b0011:datar1 = 7'b0110000;
4'b0100:datar1 = 7'b0011001;
4'b0101:datar1 = 7'b0010010;
4'b0110:datar1 = 7'b0000010;
4'b0111:datar1 = 7'b1111000;
4'b1000:datar1 = 7'b0000000;
4'b1001:datar1 = 7'b0010000;
endcase
end
always@(posedge clk50)
begin
case(temp_data_2)//gfedcba
4'b0000:datar2 = 7'b1000000;//0
4'b0001:datar2 = 7'b1111001;
4'b0010:datar2 = 7'b0100100;
4'b0011:datar2 = 7'b0110000;
4'b0100:datar2 = 7'b0011001;
4'b0101:datar2 = 7'b0010010;
4'b0110:datar2 = 7'b0000010;
4'b0111:datar2 = 7'b1111000;
4'b1000:datar2 = 7'b0000000;
4'b1001:datar2 = 7'b0011000;
endcase
end
always@(posedge clk50)
begin
case(temp_data_3)//gfedcba
4'b0000:datar3 = 7'b1000000;//0
4'b0001:datar3 = 7'b1111001;
4'b0010:datar3 = 7'b0100100;
4'b0011:datar3 = 7'b0110000;
4'b0100:datar3 = 7'b0011001;
4'b0101:datar3 = 7'b0010010;
4'b0110:datar3 = 7'b0000010;
4'b0111:datar3 = 7'b1111000;
4'b1000:datar3 = 7'b0000000;
4'b1001:datar3 = 7'b0011000;
endcase
end
always@(posedge clk50)
begin
case(temp_data_4)//gfedcba
4'b0000:datar4 = 7'b1000000;//0
4'b0001:datar4 = 7'b1111001;
4'b0010:datar4 = 7'b0100100;
4'b0011:datar4 = 7'b0110000;
4'b0100:datar4 = 7'b0011001;
4'b0101:datar4 = 7'b0010010;
4'b0110:datar4 = 7'b0000010;
4'b0111:datar4 = 7'b1111000;
4'b1000:datar4 = 7'b0000000;
4'b1001:datar4 = 7'b0011000;
endcase
end
endmodule
3.5、DHT11控制模塊
? ? ? ? DHT11的控制主要有四個(gè)重點(diǎn):
3.5.1、上升、下降沿的判定
? ? ? ? 通過(guò)兩個(gè)指示信號(hào)來(lái)實(shí)現(xiàn),代碼如下:
//通過(guò)dht11的上升下降沿來(lái)控制步進(jìn)次數(shù),確保在輸入一個(gè)字節(jié)是bitcnt等累加器只會(huì)加一次,同時(shí)控制判別器在電平下降時(shí)才進(jìn)行判斷
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)//復(fù)位是兩個(gè)都是1
begin
dht11_reg1 <= 1'b1;
dht11_reg2 <= 1'b1;
end
else
begin
dht11_reg1 <= dht11;//讀入數(shù)據(jù),非上升下降沿時(shí),二者相等,pos、neg均為0,
//上升沿:reg1 = 1,reg2 = 0,pos = 1,neg = 0;
//下降沿:reg1 = 0,reg2 = 1,pos = 0,neg = 1;
dht11_reg2 <= dht11_reg1;//reg2 = reg1
end
end
assign dht11_pos = (dht11_reg1) & (~dht11_reg2);//posedge,時(shí)鐘上升沿
assign dht11_neg = (~dht11_reg1) & (dht11_reg2);//negedge,時(shí)鐘下降沿
3.5.2、端口IO狀態(tài)控制
? ? ? ? 主機(jī)僅在前兩個(gè)階段生成開(kāi)始指示信號(hào)是控制端口,其他階段僅作為接收端。
????????IO狀態(tài)的控制可以通過(guò)對(duì)dht11進(jìn)行賦值來(lái)實(shí)現(xiàn),當(dāng)主機(jī)輸出時(shí)dht11為具體值,當(dāng)輔機(jī)輸出時(shí)dht11為未知量1'bz。
? ? ? ? 代碼如下:
//數(shù)值判定 IO狀態(tài) 與 輸出值
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)begin
dht11_en <= 1'b0;
dht11_out <= 1'b1;
end
else if(state == WAIT_1S)//等待系統(tǒng)穩(wěn)定
begin
dht11_en <= 1'b1;
dht11_out <= 1'b1;
end
else if(state == START)//wait 1s or start
begin
dht11_en <= 1'b1;
dht11_out <= 1'b0;
if(cnt_us == LOW_18MS_MAX)
dht11_out <= 1'b1;
end
else //其他狀態(tài)
begin
dht11_en <= 1'b0;
dht11_out <=1'b0;
end
end
//賦值
assign dht11 = dht11_en ? dht11_out : 1'bz;
3.5.3、狀態(tài)判斷
? ? ? ? 狀態(tài)判斷主要通過(guò)兩個(gè)計(jì)數(shù)器進(jìn)行,一個(gè)記錄高電平以及非工作時(shí)間、一個(gè)記錄低電平時(shí)間。狀態(tài)轉(zhuǎn)換的判定通過(guò)上升、下降沿,計(jì)數(shù)器取值來(lái)進(jìn)行,每當(dāng)狀態(tài)進(jìn)行轉(zhuǎn)換,計(jì)數(shù)器清零一次。
? ? ? ? 計(jì)數(shù)器控制代碼:
//DHT11狀態(tài)變量控制
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)//復(fù)位
begin
cnt_low = 20'd0;
cnt_us = 20'd0;
end
else
begin
case(state)
WAIT_1S:begin//初始狀態(tài),
if(cnt_us == WAIT_1S_MAX)//若滿足時(shí)間1s
cnt_us = 20'd0;
else //not 到1s時(shí),每1us cnt us+1,要加1E6次
cnt_us = cnt_us + 1'b1;
end
START :begin//主機(jī)拉低頻率18ms
if(cnt_us == LOW_18MS_MAX)//在清零后cnt us 重新開(kāi)始計(jì)數(shù),直到到達(dá)18ms
cnt_us = 20'd0;
else
cnt_us = cnt_us + 1'b1;
end
DLY_1 :begin//等待從機(jī)發(fā)出信號(hào),延時(shí)30us后 cntus清零
if(cnt_us == 20'd29)
cnt_us = 20'd0;
else
cnt_us = cnt_us + 1'b1;
end
REPLY :begin//從機(jī)發(fā)射響應(yīng)信號(hào)
if(dht11_pos == 1'b1 && (cnt_low > 80))//從機(jī)相應(yīng)80us后,清零、進(jìn)入下一狀態(tài)
begin
cnt_low = 20'd0;
cnt_us = 20'd0;
end
else if(dht11 == 1'b0)//DATA為低電平,但為滿足狀態(tài),持續(xù)計(jì)數(shù)
begin
cnt_low = cnt_low + 1'b1;//低電平時(shí)間計(jì)數(shù)
cnt_us = cnt_us + 1'b1;//時(shí)間計(jì)數(shù)
end
else if(cnt_us > 1000)//若超過(guò)1ms,歸零,重新開(kāi)始
begin
cnt_low <= 20'd0;
cnt_us <= 20'd0;
end
else //系統(tǒng)不穩(wěn)定,計(jì)時(shí)但是不記錄低電平時(shí)間
begin
cnt_low = cnt_low;
cnt_us = cnt_us + 1'b1;
end
end
DLY_2 :begin//等待
if(dht11_neg == 1'b1 && (cnt_us > 80))//拉高80us響應(yīng)后,進(jìn)入讀取階段
cnt_us = 20'd0;
else
cnt_us = cnt_us + 1'b1;
end
RD_DATA:begin//傳輸信號(hào)數(shù)據(jù)
if(dht11_neg == 1'b1 || dht11_pos == 1'b1)//df與dr均為1時(shí)歸零(電平變化時(shí))
begin
cnt_us = 1'b0;
end
else
cnt_us = cnt_us + 1'b1;//計(jì)時(shí)
end
default://其他情況歸零
begin
cnt_low = 20'd0;
cnt_us = 20'd0;
end
endcase
end
end
????????狀態(tài)控制代碼:
//狀態(tài)控制,控制變量在后續(xù)模塊
always@(posedge clk_us or negedge sys_rst)//微妙時(shí)鐘或復(fù)位
begin
if(sys_rst)//rst = 0
state <= WAIT_1S; //等待1s,初始狀態(tài)為state = wait 1s
else
begin
case(state)
WAIT_1S:begin
if(cnt_us == WAIT_1S_MAX)//1s后,state = start,同時(shí)cnt us清零
state = START;
else
state = WAIT_1S;
end
START :begin//主機(jī)發(fā)出開(kāi)始信號(hào)
if(cnt_us == LOW_18MS_MAX)//到達(dá)18ms后cnt us清零,進(jìn)入等待1狀態(tài)
state = DLY_1;
else
state = START;
end
DLY_1 :begin//拉高30us表示結(jié)束
if(cnt_us == 20'd30)//10us后進(jìn)入從機(jī)相應(yīng)狀態(tài)
state = REPLY;
else
state = DLY_1;
end
REPLY :begin//從機(jī)回應(yīng)
if(dht11_pos == 1'b1 && (cnt_low > 80))//低電平計(jì)數(shù)80us以上,進(jìn)入等待2狀態(tài),等待傳感器數(shù)據(jù)
state = DLY_2;
else if(cnt_us > 1000)//表示等待了1MS,重新開(kāi)始
state = START;
else
state = REPLY;
end
DLY_2 :begin//拉高80us后進(jìn)入數(shù)據(jù)讀取
if(dht11_neg == 1'b1 && cnt_us >80)
state = RD_DATA;
else
state = DLY_2;
end
RD_DATA:begin
if(bit_cnt == 40 && dht11_pos == 1'b1)//讀取完40bit數(shù)據(jù)后重新開(kāi)始
state = START;//讀完后立刻返回開(kāi)始
else//否則持續(xù)進(jìn)行
state = RD_DATA;
end
default:state = WAIT_1S;
endcase
end
end
3.5.4、數(shù)據(jù)讀入
? ? ? ? 代碼如下:
//依次對(duì)data——temp寫(xiě)入信號(hào)數(shù)據(jù),輸出data-temp
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)
data_temp <= 40'd0;
else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us< 50 ) //下降沿是出發(fā),確保高點(diǎn)平占時(shí)完全被記錄
data_temp[39 - bit_cnt] <= 1'b0; //DHT11先輸出高位,因此從高位向低位依次賦值
else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us > 50)
data_temp[39 - bit_cnt] <= 1'b1;//小于50us當(dāng)成0,大于50us當(dāng)成1
else
data_temp <= data_temp;
end
//數(shù)據(jù)校驗(yàn)和賦值,輸出data
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)
data <= 32'd0;
else if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8])
data <= data_temp[39:8];//校驗(yàn)位無(wú)誤時(shí)將數(shù)據(jù)賦值給data
else
data <= data;
end
3.5.5、完整代碼
? ? ? ? 第一組:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/06/11 15:49:57
// Design Name:
// Module Name: dht11_ctrl
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module dht11_ctrl(sys_clk,sys_rst,data_flag,dht11,data_out, test_leds);
input sys_clk;//clk-50Mhz
input sys_rst;//rst,觸發(fā)為1
input data_flag;//控制輸出溫度還是濕度
inout dht11;//既可輸入又可輸出,即為DATA線
output [15:0] data_out;//輸出16位信號(hào)
output [3:0] test_leds;
//狀態(tài)定義
parameter WAIT_1S = 6'b000_001,//上電等待1s狀態(tài)
START = 6'b000_010,//主機(jī)拉低20ms,發(fā)送開(kāi)始信號(hào)狀態(tài)
DLY_1 = 6'b000_100,//等待從機(jī)答應(yīng)
REPLY = 6'b001_000,//從機(jī)對(duì)主機(jī)發(fā)出發(fā)送信號(hào)
DLY_2 = 6'b010_000,//等待主機(jī)回應(yīng)
RD_DATA = 6'b100_000;//開(kāi)始傳輸數(shù)據(jù)
parameter WAIT_1S_MAX = 32'd999_999,
LOW_18MS_MAX = 20'd19_999;
wire dht11_pos;
wire dht11_neg;
reg clk_us;
reg [4:0] cnt;//微妙時(shí)鐘計(jì)數(shù)器
reg [5:0] state;
reg [19:0] cnt_us;//微秒計(jì)時(shí)器
reg [19:0] cnt_low;//低電平計(jì)時(shí)器
reg dht11_reg1;//時(shí)鐘沿計(jì)數(shù)器
reg dht11_reg2;//時(shí)鐘沿計(jì)數(shù)器
reg [5:0] bit_cnt;//數(shù)據(jù)寫(xiě)入計(jì)數(shù)器
reg [39:0] data_temp;//全部數(shù)據(jù)
reg [31:0] data;//校驗(yàn)后數(shù)據(jù)
reg dht11_en;
reg dht11_out;
reg [3:0] led;
//微秒時(shí)鐘1,一個(gè)周期0.5us
always@(posedge sys_clk or negedge sys_rst)
begin//一個(gè)周期為25時(shí)鐘周期,0.02*25=0.5us,一個(gè)周期0.5us
if(sys_rst)
cnt <= 5'd0;
else if(cnt == 5'd24)//微秒計(jì)數(shù)器
cnt <= 5'd0;
else
cnt <= cnt + 1'b1;
end
//微秒時(shí)鐘2,由cnt控制
always@(posedge sys_clk or negedge sys_rst)
begin
if(sys_rst)
clk_us <= 1'b0;
else if(cnt== 5'd24)//將cnt-us換為cnt,此時(shí)一個(gè)沒(méi)0.5us翻轉(zhuǎn)一次,clk-us一個(gè)周期為1us
clk_us <= ~ clk_us;
else
clk_us <= clk_us;
end
//狀態(tài)控制,控制變量在后續(xù)模塊
always@(posedge clk_us or negedge sys_rst)//微妙時(shí)鐘或復(fù)位
begin
if(sys_rst)//rst = 0
state <= WAIT_1S; //等待1s,初始狀態(tài)為state = wait 1s
else
begin
case(state)
WAIT_1S:begin
if(cnt_us == WAIT_1S_MAX)//1s后,state = start,同時(shí)cnt us清零
state = START;
else
state = WAIT_1S;
end
START :begin//主機(jī)發(fā)出開(kāi)始信號(hào)
if(cnt_us == LOW_18MS_MAX)//到達(dá)18ms后cnt us清零,進(jìn)入等待1狀態(tài)
state = DLY_1;
else
state = START;
end
DLY_1 :begin//拉高30us表示結(jié)束
if(cnt_us == 20'd30)//10us后進(jìn)入從機(jī)相應(yīng)狀態(tài)
state = REPLY;
else
state = DLY_1;
end
REPLY :begin//從機(jī)回應(yīng)
if(dht11_pos == 1'b1 && (cnt_low > 80))//低電平計(jì)數(shù)80us以上,進(jìn)入等待2狀態(tài),等待傳感器數(shù)據(jù)
state = DLY_2;
else if(cnt_us > 1000)//表示等待了1MS,重新開(kāi)始
state = START;
else
state = REPLY;
end
DLY_2 :begin//拉高80us后進(jìn)入數(shù)據(jù)讀取
if(dht11_neg == 1'b1 && cnt_us >80)
state = RD_DATA;
else
state = DLY_2;
end
RD_DATA:begin
if(bit_cnt == 40 && dht11_pos == 1'b1)//讀取完40bit數(shù)據(jù)后重新開(kāi)始
state = START;//讀完后立刻返回開(kāi)始
else//否則持續(xù)進(jìn)行
state = RD_DATA;
end
default:state = WAIT_1S;
endcase
end
end
//DHT11狀態(tài)變量控制
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)//復(fù)位
begin
cnt_low = 20'd0;
cnt_us = 20'd0;
end
else
begin
case(state)
WAIT_1S:begin//初始狀態(tài),
if(cnt_us == WAIT_1S_MAX)//若滿足時(shí)間1s
cnt_us = 20'd0;
else //not 到1s時(shí),每1us cnt us+1,要加1E6次
cnt_us = cnt_us + 1'b1;
end
START :begin//主機(jī)拉低頻率18ms
if(cnt_us == LOW_18MS_MAX)//在清零后cnt us 重新開(kāi)始計(jì)數(shù),直到到達(dá)18ms
cnt_us = 20'd0;
else
cnt_us = cnt_us + 1'b1;
end
DLY_1 :begin//等待從機(jī)發(fā)出信號(hào),延時(shí)30us后 cntus清零
if(cnt_us == 20'd29)
cnt_us = 20'd0;
else
cnt_us = cnt_us + 1'b1;
end
REPLY :begin//從機(jī)發(fā)射響應(yīng)信號(hào)
if(dht11_pos == 1'b1 && (cnt_low > 80))//從機(jī)相應(yīng)80us后,清零、進(jìn)入下一狀態(tài)
begin
cnt_low = 20'd0;
cnt_us = 20'd0;
end
else if(dht11 == 1'b0)//DATA為低電平,但為滿足狀態(tài),持續(xù)計(jì)數(shù)
begin
cnt_low = cnt_low + 1'b1;//低電平時(shí)間計(jì)數(shù)
cnt_us = cnt_us + 1'b1;//時(shí)間計(jì)數(shù)
end
else if(cnt_us > 1000)//若超過(guò)1ms,歸零,重新開(kāi)始
begin
cnt_low <= 20'd0;
cnt_us <= 20'd0;
end
else //系統(tǒng)不穩(wěn)定,計(jì)時(shí)但是不記錄低電平時(shí)間
begin
cnt_low = cnt_low;
cnt_us = cnt_us + 1'b1;
end
end
DLY_2 :begin//等待
if(dht11_neg == 1'b1 && (cnt_us > 80))//拉高80us響應(yīng)后,進(jìn)入讀取階段
cnt_us = 20'd0;
else
cnt_us = cnt_us + 1'b1;
end
RD_DATA:begin//傳輸信號(hào)數(shù)據(jù)
if(dht11_neg == 1'b1 || dht11_pos == 1'b1)//df與dr均為1時(shí)歸零(電平變化時(shí))
begin
cnt_us = 1'b0;
end
else
cnt_us = cnt_us + 1'b1;//計(jì)時(shí)
end
default://其他情況歸零
begin
cnt_low = 20'd0;
cnt_us = 20'd0;
end
endcase
end
end
reg ct;
always@(posedge clk_us)
begin
if(cnt_us == 20'b1000)
ct = 1;
if(ct == 1)
led[0] = 1;
else if(ct == 0)
led[0] = 0;
end
//通過(guò)dht11的上升下降沿來(lái)控制步進(jìn)次數(shù),確保在輸入一個(gè)字節(jié)是bitcnt等累加器只會(huì)加一次,同時(shí)控制判別器在電平下降時(shí)才進(jìn)行判斷
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)//復(fù)位是兩個(gè)都是1
begin
dht11_reg1 <= 1'b1;
dht11_reg2 <= 1'b1;
end
else
begin
dht11_reg1 <= dht11;//讀入數(shù)據(jù),非上升下降沿時(shí),二者相等,pos、neg均為0,
//上升沿:reg1 = 1,reg2 = 0,pos = 1,neg = 0;
//下降沿:reg1 = 0,reg2 = 1,pos = 0,neg = 1;
dht11_reg2 <= dht11_reg1;//reg2 = reg1
end
end
assign dht11_pos = (dht11_reg1) & (~dht11_reg2);//posedge,時(shí)鐘上升沿
assign dht11_neg = (~dht11_reg1) & (dht11_reg2);//negedge,時(shí)鐘下降沿
//數(shù)據(jù)位數(shù)控制,輸出bit-cnt
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)
bit_cnt <= 6'd0;
else if(bit_cnt == 40 && dht11_pos == 1'b1)
bit_cnt <= 6'd0;
else if((state == RD_DATA) && (dht11_neg == 1'b1))
bit_cnt <= bit_cnt + 1'b1;//當(dāng)狀態(tài)為寫(xiě)入且dht11-neg = 1時(shí),bit—cnt依次變化
else
bit_cnt <= bit_cnt;
end
//依次對(duì)data——temp寫(xiě)入信號(hào)數(shù)據(jù),輸出data-temp
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)
data_temp <= 40'd0;
else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us< 50 ) //下降沿是出發(fā),確保高點(diǎn)平占時(shí)完全被記錄
data_temp[39 - bit_cnt] <= 1'b0; //DHT11先輸出高位,因此從高位向低位依次賦值
else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us > 50)
data_temp[39 - bit_cnt] <= 1'b1;//小于50us當(dāng)成0,大于50us當(dāng)成1
else
data_temp <= data_temp;
end
always@(posedge clk_us)
begin
if(data_temp == 0)
led[1] = 1;
end
//數(shù)據(jù)校驗(yàn)和賦值,輸出data
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)
data <= 32'd0;
else if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8])
data <= data_temp[39:8];//校驗(yàn)位無(wú)誤時(shí)將數(shù)據(jù)賦值給data
else
data <= data;
end
//IO控制
always@(posedge clk_us or negedge sys_rst)
begin
if(sys_rst)begin
dht11_en <= 1'b0;
dht11_out <= 1'b1;
end
else if(state == WAIT_1S)//等待系統(tǒng)穩(wěn)定
begin
dht11_en <= 1'b1;
dht11_out <= 1'b1;
end
else if(state == START)//wait 1s or start
begin
dht11_en <= 1'b1;
dht11_out <= 1'b0;
if(cnt_us == LOW_18MS_MAX)
dht11_out <= 1'b1;
end
else //其他狀態(tài)
begin
dht11_en <= 1'b0;
dht11_out <=1'b0;
end
end
assign dht11 = dht11_en ? dht11_out : 1'bz;
//當(dāng)dht11en = 1時(shí),dht11 = dht11 out,當(dāng)en = 0 時(shí),dht11 = 未知
//start時(shí)dht11 受主機(jī)控制,等于0--低電平
//其他時(shí)候,dht11為未知量,由輔機(jī)控制
reg [15:0] datar;
always@(posedge clk_us or negedge sys_rst)//溫濕度選擇器,輸出data-out
begin
if(sys_rst)
datar <= 16'd0;//十六位
else if(data_flag == 1'b0)//data為32位 整溫-小溫-整濕-小濕
datar <= data[31:16];//濕度數(shù)據(jù)
else if(data_flag == 1'b1)
datar <= data[15:0];//濕度數(shù)據(jù)
else
datar <= data_out;
end
assign data_out = datar;
always@(posedge clk_us)
begin
if(data == 0 /*&& data_temp != 0*/)
led[2] = 1;
else if(datar == 0)
led[3] = 1;
end
endmodule
? ? ? ? 第二組:
`timescale 1ns / 1ps
module dht11_2(
input sys_clk , //system clock
input sys_rst_n , //system reset negedge
inout dht11_data , //dht11 inout port
output reg [39:0] t_h_data
);
//tate code
parameter WAIT = 6'b000_001,//wait state 2s
START = 6'b000_010,//make bus low 20ms
WAIT_RES = 6'b000_100,//wait respond
RES_LOW = 6'b001_000,//respond low
RES_HIGH = 6'b010_000,//respong high
REC_DATA = 6'b100_000;//receive datas
//time parameter
parameter CNT_2S_MAX = 100_000_000 ,
CNT_20MS_MAX = 1_000_000 ,
CNT_1US_MAX = 50 ;
//state define
reg [5:0] state_cur;//current state
reg [5:0] state_nex;//next state
//lag define
wire end_2s ; //wait 2s end
wire end_20ms ; //wait 20ms end
wire res_ok ; //respond ok
wire res_no ; //no respond
wire end_res_low ; //wait respond low end 83us
wire end_res_high; //wait respond high end 87us
wire end_rec ; //data receive end 40bits
//dht11
reg dht11_data_r1;
reg dht11_data_r2;
wire dht11_posedge;
wire dht11_negedge;
reg data;
reg output_en;
wire check;//校驗(yàn)
reg [39:0] t_h_data_temp;//溫濕度數(shù)據(jù)
//計(jì)數(shù)器
reg [26:0] cnt_2s;
reg [19:0] cnt_20ms;
reg [6:0] cnt_nus;
reg [5:0] cnt_1us;
reg cnt_us_rst;
reg [5:0] cnt_bit;
//條件判斷
assign end_2s = (state_cur == WAIT && cnt_2s == CNT_2S_MAX - 1'b1) ? 1'b1 : 1'b0;
assign end_20ms = (state_cur == START && cnt_20ms == CNT_20MS_MAX - 1'b1) ? 1'b1 : 1'b0;
assign res_ok = (state_cur == WAIT_RES && cnt_nus < 20 && dht11_negedge) ? 1'b1 : 1'b0;
assign res_no = (state_cur == WAIT_RES && cnt_nus > 20) ? 1'b1 : 1'b0;
assign end_res_low = (state_cur == RES_LOW && cnt_nus > 70 && dht11_posedge) ? 1'b1 : 1'b0;
assign end_res_high = (state_cur == RES_HIGH && cnt_nus > 70 && dht11_negedge) ? 1'b1 : 1'b0;
assign end_rec = (state_cur == REC_DATA && cnt_bit >= 40) ? 1'b1 : 1'b0;
//dht11傳輸上升、下降延判斷
assign dht11_posedge = dht11_data_r1 & ~dht11_data_r2;
assign dht11_negedge = ~dht11_data_r1 & dht11_data_r2;
//生成
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
dht11_data_r1 <= 1'b0;
dht11_data_r2 <= 1'b0;
end
else begin
dht11_data_r1 <= dht11_data;
dht11_data_r2 <= dht11_data_r1;
end
end
//信息校驗(yàn)
assign check = (t_h_data_temp[39:32]+t_h_data_temp[31:24]+
t_h_data_temp[23:16]+t_h_data_temp[15:8] == t_h_data_temp[7:0])
? 1'b1 : 1'b0;
//計(jì)數(shù)器群
always@(*)begin
case(state_cur)
WAIT : cnt_us_rst = 1'b1;
START : cnt_us_rst = 1'b1;
WAIT_RES : begin
if(res_ok)
cnt_us_rst = 1'b1;
else
cnt_us_rst = 1'b0;
end
RES_LOW : begin
if(end_res_low)
cnt_us_rst = 1'b1;
else
cnt_us_rst = 1'b0;
end
RES_HIGH : begin
if(end_res_high)
cnt_us_rst = 1'b1;
else
cnt_us_rst = 1'b0;
end
REC_DATA : begin
if(dht11_posedge || dht11_negedge)
cnt_us_rst = 1'b1;
else
cnt_us_rst = 1'b0;
end
default :cnt_us_rst = 1'b1;
endcase
end
//cnt_2s
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_2s <= 27'd0;
end
else begin
if(state_cur == WAIT)begin
if(cnt_2s <= CNT_2S_MAX - 1'b1)
cnt_2s <= cnt_2s + 1'b1;
else
cnt_2s <= cnt_2s;
end
else if(state_cur == REC_DATA)begin
cnt_2s <= 27'd0;
end
else begin
cnt_2s <= cnt_2s;
end
end
end
//cnt_20ms
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_20ms <= 20'd0;
end
else begin
if(state_cur == START)begin
if(cnt_20ms <= CNT_20MS_MAX - 1'b1)
cnt_20ms <= cnt_20ms + 1'b1;
else
cnt_20ms <= cnt_20ms;
end
else if(state_cur == REC_DATA)begin
cnt_20ms <= 20'd0;
end
else begin
cnt_20ms <= cnt_20ms;
end
end
end
//cnt_1us
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_1us <= 6'd0;
end
else begin
if(cnt_1us == CNT_1US_MAX - 1'b1)
cnt_1us <= 6'd0;
else if(cnt_us_rst)
cnt_1us <= 6'd0;
else
cnt_1us <= cnt_1us + 1'b1;
end
end
//cnt_nus
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_nus <= 7'd0;
end
else begin
if(cnt_us_rst)
cnt_nus <= 7'd0;
else if(cnt_1us == CNT_1US_MAX - 1'b1)
cnt_nus <= cnt_nus + 1'b1;
else
cnt_nus <= cnt_nus;
end
end
//信號(hào)位數(shù)控制
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_bit <= 6'd0;
end
else begin
if(state_cur == REC_DATA)begin
if(dht11_negedge)
cnt_bit <= cnt_bit + 1'b1;
else
cnt_bit <= cnt_bit;
end
else begin
cnt_bit <= 6'd0;
end
end
end
//狀態(tài)控制1
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)
state_cur <= WAIT;
else
state_cur <= state_nex;
end
//狀態(tài)控制2
always@(*)begin
case(state_cur)
WAIT :begin
if(end_2s)
state_nex = START; //count 2s finish
else
state_nex = WAIT;
end
START :begin
if(end_20ms)
state_nex = WAIT_RES;//count 20ms finish
else
state_nex = START;
end
WAIT_RES:begin
if(res_ok) //respond
state_nex = RES_LOW;
else if(res_no) //no respond
state_nex = WAIT;
else
state_nex = WAIT_RES;
end
RES_LOW :begin
if(end_res_low)
state_nex = RES_HIGH;
else
state_nex = RES_LOW;
end
RES_HIGH:begin
if(end_res_high)
state_nex = REC_DATA;
else
state_nex = RES_HIGH;
end
REC_DATA:begin
if(end_rec)
state_nex = WAIT;
else
state_nex = REC_DATA;
end
default :begin
state_nex = WAIT;
end
endcase
end
//IO控制
assign dht11_data = output_en ? data : 1'bz;
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
output_en <= 1'b0;
data <= 1'b0;
end
else begin
case(state_cur)
WAIT :begin
output_en <= 1'b1;//output
data <= 1'b1;
end
START :begin
output_en <= 1'b1;//output
data <= 1'b0;
if(end_20ms)
data <= 1'b1;
end
WAIT_RES :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
RES_LOW :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
RES_HIGH :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
REC_DATA :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
default :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
endcase
end
end
//寫(xiě)入1
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
t_h_data_temp <= 40'd0;
end
else begin
if(state_cur == REC_DATA)begin
if(cnt_nus > 50 && dht11_negedge)
t_h_data_temp[39 - cnt_bit] <= 1'b1;
else if(cnt_nus < 50 && dht11_negedge)
t_h_data_temp[39 - cnt_bit] <= 1'b0;
else
t_h_data_temp <= t_h_data_temp;
end
else begin
t_h_data_temp <= t_h_data_temp;
end
end
end
//寫(xiě)入2
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
t_h_data <= 40'd0;
end
else begin
if(state_cur == REC_DATA)begin
if(end_rec && check)
t_h_data <= t_h_data_temp;
else
t_h_data <= t_h_data;
end
else begin
t_h_data <= t_h_data;
end
end
end
endmodule
3.6、TOP
? ? ? ? 頂層模塊需要注意兩點(diǎn):
? ? ? ? 1、若數(shù)據(jù)只是“經(jīng)過(guò)”頂層模塊或是作為賦值對(duì)象而不對(duì)其改變,則需要用wire型變量,否則將會(huì)報(bào)錯(cuò);
? ? ? ? 2、最好保持輸出位置與輸出數(shù)據(jù)的控制時(shí)序相同,千萬(wàn)不要讓輸出數(shù)據(jù)的改變快于輸出位置的改變,否則數(shù)碼管最終會(huì)顯示非理想的輸出。而非理想輸出的原因可能在于:數(shù)據(jù)轉(zhuǎn)換模塊有問(wèn)題;DHT11控制模塊有問(wèn)題;數(shù)碼管模塊有問(wèn)題。本人就因此將所有代碼從頭到尾檢查了一遍。
????????代碼如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/06/11 14:45:14
// Design Name:
// Module Name: DHT_TOP
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module DHT_TOP(
input sys_clk,
input sys_rst,//觸發(fā)為1 D20
input tap8421,
input key, //D19
inout dht11, //V17 AR8
output point, //AR7
output [3:0]num, //A0-5
output [6:0]abcdefg, //AR0-6
output [3:0]leds
);
reg data_flag;
//顯示切換 1--溫度 0--濕度
wire [15:0] data_ctrl;
wire [39:0] thdata;
//不同模塊之間傳輸而不在頂層改變的變量用wire型
//應(yīng)用時(shí)鐘 自用 輸出給dht11——ctrl 輸出給——SMG
wire clk50;
//數(shù)據(jù)選擇器所用變量
reg [7:0] data_hu_int;//十位濕度整數(shù)
reg [7:0] data_te_int;//十位溫度整數(shù)
reg [7:0] data_hu_dec;//十位濕度小數(shù)
reg [7:0] data_te_dec;//十位溫度小數(shù)
//SMG 4個(gè)數(shù)據(jù)信號(hào)
//BTD 小數(shù)點(diǎn)內(nèi)設(shè),不需要輸入
reg [7:0] data_smg_int;
reg [7:0] data_smg_dec;
//SMG的輸入 BTD的輸出
wire [3:0] int_d1;//四位十進(jìn)制
wire [3:0] int_d2;//四位十進(jìn)制
wire [3:0] dec_d1;//四位十進(jìn)制
wire [3:0] dec_d2;//四位十進(jìn)制
reg [3:0] numr;
reg pointr;
//test
wire [6:0] a1;
wire [6:0] a2;
wire [6:0] a3;
wire [6:0] a4;
reg [6:0] ar;
assign abcdefg = ar;
assign num = numr;
assign point = pointr;
//dht11控制程序
/*
dht11_ctrl dht11_ctrl
(
.sys_clk(clk50), //input
.sys_rst(sys_rst),//input
.data_flag(data_flag), //input 溫濕度選擇
.dht11(dht11), //DHT11數(shù)據(jù) input
.data_out(data_ctrl),//輸出數(shù)據(jù) output
.test_leds(leds)
);
*/
//輸出控制 8421BCD TO ABCDEFG
dht11_2 dht11_2
(
.sys_clk(clk50),
.sys_rst_n(!sys_rst),
.dht11_data(dht11),
.t_h_data(thdata)
);
assign leds = int_d1;
//assign leds = dec_d1;
SMG SMG
(
.clk50(clk50),//in
.rst(sys_rst),//in normal = 0
.data_flag(data_flag),//in 0h 1t
.int_in1(int_d1),//in d
.dec_in1(dec_d1),//in d
.int_in2(int_d2),//in d
.dec_in2(dec_d2),//in d
.data_out1(a1),
.data_out2(a2),
.data_out3(a3),
.data_out4(a4)
//out 8421bcd to abcdefg
);
//BTD SMG TO 4 8421 BCD
BTD BTD
(
.clk50(clk50),
.int_in(data_smg_int[7:0]),
.dec_in(data_smg_dec[7:0]),
//.int_in('b01010100),//
//.dec_in('b00010101),//
//DHT11有效測(cè)試范圍不過(guò)100,因此僅前7位信號(hào)有效
.int_out1(int_d1),
.dec_out1(dec_d1),
.int_out2(int_d2),
.dec_out2(dec_d2),
.rst(sys_rst)
);
//溫濕度切換
//通過(guò)按鍵的key信號(hào),轉(zhuǎn)換data_out輸出的是濕度還是溫度,沒(méi)按一次按鈕,轉(zhuǎn)換一次
always@(posedge clk50 or negedge sys_rst)
begin
if(sys_rst)
data_flag <= 1'b0;
else if(key == 1'b1)
data_flag <= ~data_flag;
else
data_flag <= data_flag;
end
//溫濕度選擇器,輸出data-out
always@(posedge clk50 or negedge sys_rst)
begin
if(sys_rst)
begin
end
else if(data_flag == 1'b0)//data為32位 整溫-小溫-整濕-小濕
//data_flag為0時(shí)data_crtl為濕度數(shù)據(jù)
begin
data_hu_int <= thdata[39:32];//濕度數(shù)據(jù)
data_hu_dec <= thdata[31:24];//濕度數(shù)據(jù)
end
else if(data_flag == 1'b1)//te
begin
data_te_int <= thdata[23:16];//溫度數(shù)據(jù)
data_te_dec <= thdata[15:8];//溫度數(shù)據(jù)
end
end
//數(shù)據(jù)選擇器1
always@(posedge clk50)
begin
if(sys_rst)begin
numr = 4'b1111;
end
else if(numr == 4'b1000)
numr = 4'b0001;
else
numr = numr <<1;
end
always@(posedge clk50)
begin
case(numr)
4'b0001:begin ar = a1;
end
4'b0010:begin ar = a2;
end
4'b0100:begin ar = a3;
end
4'b1000:begin ar = a4;
end
4'b1111:begin ar = 7'b0111111;
end
endcase
end
always@(posedge clk50)
begin
if(numr == 4'b0010)
pointr = 1'b0;
else
pointr = 1'b1;
end
//smg賦值
always@(posedge clk50 or negedge sys_rst)
begin
if(sys_rst)
begin
data_smg_int <= 8'b0000_0000;
data_smg_dec <= 8'b0000_0000;
end
else if (tap8421)
begin
data_smg_int <= 8'b0101_0100;//84
data_smg_dec <= 8'b0001_0101;//21
end
else if(data_flag == 1'b1 && !tap8421)//溫度
begin
data_smg_int <= data_te_int;
data_smg_dec <= data_te_dec;
end
else if(data_flag == 1'b0 && !tap8421)//濕度
begin
data_smg_int <= data_hu_int;
data_smg_dec <= data_hu_dec;
end
end
//時(shí)鐘
clk_wiz_0 clk0(
.reset(1'b0),
.clk_in1(sys_clk),
.clk50(clk50)
);
endmodule
3.7、結(jié)果展示
? ? ? ? 濕度:37%
????????
? ? ? ? ?溫度:28.09
?????????城市參考值:
? ? ? ? ?可以看到實(shí)際數(shù)據(jù)與城市參考值基本上差不多,同時(shí)證明在旁邊放一桶水確實(shí)能夠提高空氣濕度。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-764084.html
?
到了這里,關(guān)于FPGA實(shí)驗(yàn)筆記_Vivado:DDS信號(hào)發(fā)生器;數(shù)碼管;基于DHT11的溫濕度傳感器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!