基于FPGA通原第二天(1)
📅 2026/7/9 6:52:27
👁️ 阅读次数
📝 编程学习
在数字通信、软件无线电(SDR)中,BPSK(二进制相移键控)是最基础、最经典的数字调制方式。它用 0° 和 180° 两个相位的正弦载波,分别表示二进制 0 和 1,抗噪性能强、实现简单。
本文就基于一段可直接综合、可仿真运行的 Verilog 代码,从零讲透:数字 BPSK 到底怎么实现?代码每一行在干什么?背后原理是什么?
一、BPSK 概念
一句话:用载波相位表示数字信息
- 发送
0→ 输出正常正弦波(相位 0°) - 发送
1→ 输出反转正弦波(相位 180°)
数学上:
- 0 对应:\(s_0(t) = \sin(\omega t)\)
- 1 对应:\(s_1(t) = -\sin(\omega t) = \sin(\omega t+\pi)\)
在数字电路里,我们不用复杂的相位计算,直接对正弦波采样值取反,就完成了 180° 相移 —— 这也是这份代码最巧妙的地方。
二、DDS 载波 + BPSK 调制
这份代码只有一个模块,完成两件核心事:
- DDS(直接数字频率合成):生成稳定的正弦载波
- BPSK 调制:根据输入比特,输出原波 / 反相波
整体信号流:输入比特 data_in→相位判断(0/1)→DDS 生成正弦→原波/取反→输出调制波形 bpsk_out
三、逐行拆解 Verilog 代码
1. 模块端口定义
verilog
module bpsk_modulator( input wire clk, // 系统时钟 input wire rst_n, // 低电平复位 input wire data_in, // 基带比特流(0/1) input wire [31:0] fcw, // 频率控制字(控制载波频率) output reg signed [7:0] bpsk_out // BPSK调制输出(8位有符号) );fcw:频率控制字,决定载波频率,改它就能改频bpsk_out:8 位有符号数,直接送给 DAC 就能出模拟波形
2. DDS 核心:相位累加器
verilog
reg [31:0] phase_acc; always @(posedge clk or negedge rst_n) begin if (!rst_n) phase_acc <= 32'd0; else phase_acc <= phase_acc + fcw; end- 每来一个时钟,相位就 +
fcw - 32 位寄存器不断累加,相当于数字角度旋转器
- 频率公式:
越大,载波频率越高。
3. 正弦波查找表(LUT)
verilog
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 assign sin_out = sin_lut[phase_acc[31:24]];- 相位高 8 位作为地址(0~255),刚好对应一个完整正弦周期
- 从 ROM 里读出预存的正弦采样值
- 不用乘法器,纯查表,速度快、省资源
4. BPSK 调制:最关键一行逻辑
verilog
always @(posedge clk or negedge rst_n) begin if(!rst_n) bpsk_out <= 8'sd0; else if(data_in) bpsk_out <= -sin_out; // 1 → 反相 else bpsk_out <= sin_out; // 0 → 原相 enddata_in = 0:直接输出正弦data_in = 1:正弦值取反(= 180° 相移)- 这就是数字 BPSK 的最简实现
仿真代码和结果
`timescale 1ns/1ps module tb_bpsk_modulator(); reg clk ; reg rst_n ; reg data_in ; reg [31:0] fcw ; wire [7:0] bpsk_out ; bpsk_modulator u_bps_modulator( .clk (clk ), .rst_n (rst_n ), .fcw (fcw ), .data_in (data_in ), .bpsk_out (bpsk_out) ); initial begin clk = 0 ; forever begin #5 clk = ~clk; end end initial begin rst_n = 0; fcw = 32'd429496730; #100 rst_n = 1; data_in = 0; #100 data_in = 1; #100 data_in = 1; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 rst_n = 1; data_in = 0; #100 data_in = 1; #100 data_in = 1; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 rst_n = 1; data_in = 0; #100 data_in = 1; #100 data_in = 1; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 rst_n = 1; data_in = 0; #100 data_in = 1; #100 data_in = 1; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 rst_n = 1; data_in = 0; #100 data_in = 1; #100 data_in = 1; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 rst_n = 1; data_in = 0; #100 data_in = 1; #100 data_in = 1; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 1; #100 data_in = 0; #100 data_in = 0; #100 data_in = 0; #100 data_in = 1; #5000 $finish; end endmodule设定系统时钟 100MHz、频率控制字 FCW=429496730 完成波形仿真,对比可见输入基带比特 0、1 对应的两路载波相位区分清晰,调制波形规整理想。
编程学习
技术分享
实战经验