1. 引言BPSK二进制相移键控是数字通信中最基本的调制方式之一它用载波的两种相位0° 和 180°表示二进制信息具有抗噪声能力强、实现简单的优点。在 FPGA 中我们可以利用直接数字频率合成DDS技术生成高精度的正弦载波并结合简单的逻辑运算完成调制与解调整个过程均可在数字域内实现非常适合在低成本 FPGA 上部署。本文将分享一个完整的 BPSK 调制解调系统包含以下模块bpsk_modulatorBPSK 调制器根据输入比特翻转 DDS 载波的符号。bpsk_demodulatorBPSK 解调器使用相干解调恢复数据。bpsk_top顶层模块将调制器和解调器级联构成自环测试系统。所有代码均用 Verilog 编写并在 ModelSim 下完成功能仿真。2. 系统总体架构下图展示了系统的连接关系实际为自环测试拓扑--------------- ----------------- data_in -| bpsk_modulator|---------| bpsk_demodulator|-- data_out --------------- ----------------- ^ ^ | | fcw,clk,rst_n fcw,clk,rst_n发送端和接收端使用完全相同的fcw频率控制字和时钟因此本地载波与接收信号的载波同频同相实现了理想的相干解调条件。这种结构主要用于功能验证在真实无线通信中还需要增加载波同步和位同步电路。3. BPSK 调制器设计3.1 DDS 载波发生器调制器的核心是一个基于相位累加器的 DDS其框图如下fcw - [32位相位累加器] - 高8位作为地址 - [256×8bit正弦LUT] - sin_out相位累加器每个时钟周期累加fcw溢出自动循环相当于模 2^32 的计数器。取累加器高 8 位phase_acc[31:24]作为查找表地址对应相位分辨率 360°/256 1.40625°。正弦查找表sin_lut中预存一个完整周期的正弦波采样值有符号 8 位范围 -128~127。输出正弦波的频率由下式决定fcarrierfcw232×fclkfcarrier​232fcw​×fclk​例如系统时钟 100 MHz、fcw 429496730 时载波频率约为 10 MHz。3.2 BPSK 调制实现调制逻辑非常简单根据输入data_in选择输出sin_out或-sin_out对应相位 0° 和 180°。always (posedge clk or negedge rst_n) begin if(!rst_n) bpsk_out 8sd0; else if(data_in) bpsk_out -sin_out; // 比特1 → 载波反相 else bpsk_out sin_out; // 比特0 → 载波同相 end这样当data_in在符号速率下变化时bpsk_out便是一个相位连续或仅在比特边界跳变的 BPSK 已调信号。完整调制器代码bpsk_modulator.vmodule bpsk_modulator( input wire clk, input wire rst_n, input wire data_in, input wire [31:0] fcw, output reg signed [7:0] bpsk_out ); reg [31:0] phase_acc; always (posedge clk or negedge rst_n) begin if (!rst_n) phase_acc 32d0; else phase_acc phase_acc fcw; end wire [7:0] addr phase_acc[31:24]; reg signed [7:0] sin_lut [0:255]; initial begin $readmemh(sin_lut_one.hex, sin_lut); end wire signed [7:0] sin_out; assign sin_out sin_lut[addr]; always (posedge clk or negedge rst_n) begin if(!rst_n) bpsk_out 8sd0; else if(data_in) bpsk_out -sin_out; else bpsk_out sin_out; end endmodule4. BPSK 解调器设计解调器采用相干解调结构包含本地载波产生、乘法混频、低通滤波和判决四个部分。4.1 本地载波与混频解调器内部复用了与调制器完全相同的 DDS产生同频同相的本地正弦信号local_sin。接收信号bpsk_in与local_sin相乘若发送比特为 0bpsk_in sin(ωt)相乘后得sin²(ωt) 0.5 - 0.5cos(2ωt)。若发送比特为 1bpsk_in -sin(ωt)相乘后得-sin²(ωt) -0.5 0.5cos(2ωt)。因此乘积中包含一个直流分量±0.5和一个高频分量2倍载波频率。通过低通滤波器滤除高频后即可根据直流分量的极性恢复原始数据。wire signed [7:0] local_sin; assign local_sin sin_lut[phase_acc[31:24]]; // 相位累加器与调制端相同 reg signed [15:0] mixed_signal; always (posedge clk or negedge rst_n) begin if (!rst_n) mixed_signal 16sd0; else mixed_signal bpsk_in * local_sin; // 有符号乘法位宽扩展 end4.2 低通滤波器设计1. 数学原理从公式到代码一阶 IIR 低通滤波器的标准差分方程如下其中是当前的滤波输出对应代码中的 lpf_out 的新值。是上一拍的滤波输出对应代码中等号右边的 lpf_out。是当前的输入信号对应代码中的 mixed_signal。是滤波系数介于 $0$ 到 $1$ 之间决定了滤波的强度。我们对这个公式进行简单的代数变换代码中用移位操作实现除以 16 的运算避免了乘法器reg signed [15:0] lpf_out; always (posedge clk or negedge rst_n) begin if (!rst_n) lpf_out 16sd0; else lpf_out lpf_out (mixed_signal 4) - (lpf_out 4); end仔细观察最后这个公式你会发现它和我们的 Verilog 代码结构完全一致注意有符号数的算术右移会保留符号位确保了滤波器的正确性。4.3 判决输出当lpf_out 0时输出比特1否则输出0。这正好与调制器映射规则对应。always (posedge clk or negedge rst_n) begin if (!rst_n) data_out 1b0; else begin if (lpf_out 16sd0) data_out 1b1; else data_out 1b0; end end完整解调器代码bpsk_demodulator.vmodule bpsk_demodulator( input wire clk, input wire rst_n, input wire signed [7:0] bpsk_in, input wire [31:0] fcw, output reg data_out ); reg [31:0] phase_acc; always (posedge clk or negedge rst_n) begin if (!rst_n) phase_acc 32d0; else phase_acc phase_acc fcw; end reg signed [7:0] sin_lut [0:255]; initial begin $readmemh(sin_lut_one.hex, sin_lut); end wire signed [7:0] local_sin; assign local_sin sin_lut[phase_acc[31:24]]; reg signed [15:0] mixed_signal; always (posedge clk or negedge rst_n) begin if (!rst_n) mixed_signal 16sd0; else mixed_signal bpsk_in * local_sin; end reg signed [15:0] lpf_out; always (posedge clk or negedge rst_n) begin if (!rst_n) lpf_out 16sd0; else lpf_out lpf_out (mixed_signal 4) - (lpf_out 4); end always (posedge clk or negedge rst_n) begin if (!rst_n) data_out 1b0; else begin if (lpf_out 16sd0) data_out 1b1; else data_out 1b0; end end endmodule5. 顶层模块与自环测试顶层模块bpsk_top将调制器和解调器直接连接形成自环测试通路。这里需要特别注意fcw的位宽声明为[31:0]避免默认 1 位导致的不匹配。module bpsk_top( input wire clk, input wire rst_n, input wire data_in, input wire [31:0] fcw, output wire data_out ); wire [7:0] bpsk_data; bpsk_modulator u_bpsk_modulator( .clk (clk), .rst_n (rst_n), .data_in (data_in), .fcw (fcw), .bpsk_out (bpsk_data) ); bpsk_demodulator u_bpsk_demodulator( .clk (clk), .rst_n (rst_n), .bpsk_in (bpsk_data), .fcw (fcw), .data_out (data_out) ); endmodule测试仿真BPSK调制部分基本正常解调输出data_out正确恢复。