基于FPGA通原第二天(1)

在数字通信、软件无线电(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 调制

这份代码只有一个模块,完成两件核心事:

  1. DDS(直接数字频率合成):生成稳定的正弦载波
  2. 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 → 原相 end
  • data_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 对应的两路载波相位区分清晰,调制波形规整理想。