FPGA實現2fsk數字調製解調(verilog)
前言:
最近做調製解調系統,在網上找了不少教程,雖然確實有很大的參考意義,但是問題也很明顯:
過於簡單,無法實用。例如一些教程直接用方波作為載波,代替正弦波。
功能單一,不夠系統。大部分解調教程只完成了波形復現,沒有完全復現數字訊號。
解調方面的資料少。
已經實現了最簡單的基礎功能,把思路分享出來供大家參考。原始碼暫時只提供頂層檔案,子模組可以根據功能描述自行設計,感謝理解。以後會開源。
非通訊科班出身,難免會出現不少錯誤,歡迎大佬批評指正。
未經允許請勿轉載。
發射端電路:
電路原理:
l_clk
產生100Hz訊號,其週期10ms是天線發射一個碼元對應的時間(取的比較長是方便用示波器直觀地看出來)。
jsq5
模5計數器,即50ms為一幀。後續電路會根據cnt的值完成不同工作。
read
cnt=0時讀取info_s,cnt=1-4時發射相應位的二進位制數。
code
編碼,其實就是最簡單的並串轉換。
clk_wiz_0
ip核,產生讀取rom所需的時鐘,這裡頻率分別是1024×11k和1024×22k。
rom
ip核,儲存正弦訊號。
da_wave_send
讀取rom。
行為級模擬結果:
55ms-105ms是完整的一幀
55ms時讀取info_s,即要發射的資料。55-65ms部分不發射任何訊號,這裡是方便接收端進行同步(根據這段空白識別舊一幀的結束和新一幀的開始)。
65-105ms部分為調製後的載波訊號。22kHz表示“0”、11kHz表示“1”。0110正好對應55ms時info_s的值“6”。
頂層程式碼:
module
num_4_communication
(
input
s_clk
,
//系統時鐘
input
s_rst
,
//系統復位,低電平有效
input
[
4
-
1
:
0
]
info_s
,
output
da_clk
,
//DA 取樣時鐘
output
[
9
:
0
]
da_data
//DA 取樣資料
);
wire
[
10
-
1
:
0
]
rd_addr
;
wire
[
10
-
1
:
0
]
rd_data
;
wire
[
4
-
1
:
0
]
info
;
wire
[
3
-
1
:
0
]
cnt
;
wire
l_clk_10
;
wire
modu
;
wire
en
;
wire
clk_out1
;
wire
clk_out2
;
//時鐘模組(ip核)
clk_wiz_0
u_clk_wiz_0
(
。
clk_in1
(
s_clk
),
。
clk_out1
(
clk_out1
),
。
clk_out2
(
clk_out2
)
);
//時鐘模組(low)
l_clk
u_l_clk
(
。
s_clk
(
s_clk
),
。
s_rst
(
s_rst
),
。
l_clk_10
(
l_clk_10
)
);
//資料讀取
read
u_read
(
。
l_clk_10
(
l_clk_10
),
。
s_rst
(
s_rst
),
。
info_s
(
info_s
),
。
cnt
(
cnt
),
。
info
(
info
)
);
//JSQ
jsq_5
u_jsq_5
(
。
l_clk_10
(
l_clk_10
),
。
s_rst
(
s_rst
),
。
cnt
(
cnt
)
);
//編碼
code
u_code
(
。
l_clk_10
(
l_clk_10
),
。
s_rst
(
s_rst
),
。
cnt
(
cnt
),
。
info
(
info
),
。
modu
(
modu
),
。
en
(
en
)
);
//調製
modulation
u_modulation
(
。
modu
(
modu
),
。
clk_out1
(
clk_out1
),
。
clk_out2
(
clk_out2
),
。
clk
(
clk
)
);
//DA傳送1
da_wave_send
u1_da_wave_send
(
。
clk
(
clk
),
。
s_rst
(
s_rst
),
。
en
(
en
),
。
rd_data
(
rd_data
),
。
rd_addr
(
rd_addr
),
。
da_data
(
da_data
),
。
da_clk
(
da_clk
)
);
//rom
rom
u_rom
(
。
addra
(
rd_addr
),
。
clka
(
da_clk
),
。
douta
(
rd_data
)
);
endmodule
接收端電路
解調採用比較簡單的非相干解調,包絡檢波法。使用兩個通帶頻率分別為11k、22k的帶通濾波器,透過簡單的
帶通濾波-整流-低通濾波
即可檢測接收訊號中相應頻率訊號的功率。
對兩個低通濾波器的輸出做減法運算,求出11k和22k分量的差值。當該差值足夠大時,即可認為接收到了可信的“0”或“1”,否則認為此時沒有訊號。
如果接收端的狀態從“認為此時沒有訊號”變為“認為此時接收到了可到的訊號”,則對應發射端的狀態為一幀載波訊號的開始。signal_en的上升沿可以表述這一變化,利用這一特性可以實現同步。
電路原理:
fir_11k、fir_22k、lowpass
數字濾波器ip核,實現帶通濾波和低通濾波。
rectify
整流,就是取絕對值。
dem
根據差值的正負判斷接收到的訊號是“0”或“1”。
bit_judge
差值位寬判斷。
avg
求每32個取樣週期得到的差值位寬的平均值。
signal_enable
根據上述均值,判斷該段時間內的訊號是否可信。
judgment
同步、判決、串並轉換、輸出資料。
行為級模擬結果:
info_judge即接收端最終還原出的4bit資料。
其中wave_s和modu由testbench產生。
wave_11k_dc、wave22kdc 、differencebitavg、signal_en都是電路內部訊號,實際不需輸出。
sum_over下降沿輸出訊號穩定,可以作為讀取指令。
info_prob為“1”時表示該位資料雖然有輸出,但不可靠。
頂層程式碼:
module
num_4_receive
(
input
clk
,
//200kHz系統時鐘
input
rst
,
input
[
9
:
0
]
wave_s
,
//AD訊號輸入
output
[
3
:
0
]
info_judge
,
//復原訊號
output
[
3
:
0
]
info_prob
,
//可靠性判斷
output
sum_over
//並行資料接收指示,上升沿處理資料,下降沿時info_judge穩定
);
// 10位輸入轉為16位
wire
[
15
:
0
]
fir_in
;
assign
fir_in
[
15
:
6
]
=
wave_s
;
assign
fir_in
[
5
:
0
]
=
6
‘d0000
;
//濾波器35位輸出擷取為16位
wire
signed
[
39
:
0
]
data1
;
wire
signed
[
39
:
0
]
data2
;
wire
signed
[
15
:
0
]
wave_11k
;
wire
signed
[
15
:
0
]
wave_22k
;
assign
wave_11k
=
data1
[
34
:
19
];
assign
wave_22k
=
data2
[
34
:
19
];
//整流後的波形
wire
[
15
:
0
]
wave_11k_abs
;
wire
[
15
:
0
]
wave_22k_abs
;
//低通濾波器輸出取16位
wire
signed
[
39
:
0
]
data1_dc40
;
wire
signed
[
39
:
0
]
data2_dc40
;
wire
signed
[
15
:
0
]
wave_11k_dc
;
wire
signed
[
15
:
0
]
wave_22k_dc
;
assign
wave_11k_dc
=
data1_dc40
[
35
:
20
];
assign
wave_22k_dc
=
data2_dc40
[
35
:
20
];
wire
info
;
//差值估值 判斷訊號有無
wire
signed
[
15
:
0
]
demode
;
wire
[
15
:
0
]
difference_abs
;
wire
[
3
:
0
]
difference_bit
;
wire
[
3
:
0
]
noise_bit
=
4
’b0000
;
wire
clk_avg
;
//64倍時鐘 每計算完32次取樣均值變化一次
wire
clk_avg_1
;
//比clk_avg略有延遲
wire
[
4
:
0
]
difference_bit_avg
;
//訊號差值估算
wire
signal_en
;
//接收到的訊號有效
//wire [3:0] info_judge; //最終並行資料
//wire [3:0] info_prob; //問題報警訊號
//wire sum_over; //並行資料接收指示
//過帶通濾波器
fir_11k
u_fir_11k
(
。
aclk
(
clk
),
。
s_axis_data_tvalid
(
1
‘b1
),
。
s_axis_data_tready
(),
。
s_axis_data_tdata
(
fir_in
),
。
m_axis_data_tvalid
(),
。
m_axis_data_tdata
(
data1
)
);
fir_22k
u_fir_22k
(
。
aclk
(
clk
),
。
s_axis_data_tvalid
(
1
’b1
),
。
s_axis_data_tready
(),
。
s_axis_data_tdata
(
fir_in
),
。
m_axis_data_tvalid
(),
。
m_axis_data_tdata
(
data2
)
);
//整流1
rectify
u1_rectify
(
。
wave
(
wave_11k
),
。
wave_abs
(
wave_11k_abs
)
);
rectify
u2_rectify
(
。
wave
(
wave_22k
),
。
wave_abs
(
wave_22k_abs
)
);
//低通濾波器
lowpass
u1_lowpass
(
。
aclk
(
clk
),
。
s_axis_data_tvalid
(
1
‘b1
),
。
s_axis_data_tready
(),
。
s_axis_data_tdata
(
wave_11k_abs
),
。
m_axis_data_tvalid
(),
。
m_axis_data_tdata
(
data1_dc40
)
);
lowpass
u2_lowpass
(
。
aclk
(
clk
),
。
s_axis_data_tvalid
(
1
’b1
),
。
s_axis_data_tready
(),
。
s_axis_data_tdata
(
wave_22k_abs
),
。
m_axis_data_tvalid
(),
。
m_axis_data_tdata
(
data2_dc40
)
);
//解調
dem
u_dem
(
。
clk
(
clk
),
。
rst
(
rst
),
。
wave_11k_dc
(
wave_11k_dc
),
。
wave_22k_dc
(
wave_22k_dc
),
。
info
(
info
),
。
demode
(
demode
)
);
//差值訊號過整流
rectify
u3_rectify
(
。
wave
(
demode
),
。
wave_abs
(
difference_abs
)
);
//差值位寬判斷
bit_judge
bit_judge
(
。
clk
(
clk
),
。
rst
(
rst
),
。
difference_abs
(
difference_abs
),
。
difference_bit
(
difference_bit
)
);
//每32位取樣資料取平均值
avg
avg
(
。
clk
(
clk
),
。
rst
(
rst
),
。
difference_bit
(
difference_bit
),
。
clk_avg
(
clk_avg
),
。
clk_avg_1
(
clk_avg_1
),
。
difference_bit_avg
(
difference_bit_avg
)
);
//判斷是否接收到訊號
signal_enable
signal_enable
(
。
clk_avg_1
(
clk_avg_1
),
。
rst
(
rst
),
。
difference_bit_avg
(
difference_bit_avg
),
。
noise_bit
(
noise_bit
),
。
signal_en
(
signal_en
)
);
//同步 判決 輸出訊號
judgment
judgment
(
。
clk
(
clk
),
。
rst
(
rst
),
。
info
(
info
),
。
signal_en
(
signal_en
),
// 訊號有效性判斷
。
clk_avg
(
clk_avg
),
//
。
info_judge
(
info_judge
),
//復原訊號
。
info_prob
(
info_prob
),
//可靠性判斷
。
sum_over
(
sum_over
)
);
endmodule
總結
幼兒玩具水平的系統,不過幫助理解了一些基礎知識。
後續會對速率、抗噪聲等進行改進,也會做一些其他調製方法的。最終實現實用的OFDM。
任何問題可以私信交流、共同學習。感謝!