基于FPGA的QPSK调制(VIVADO)

网上关于QPSK调制原理的介绍比较多,这里我就不再赘述了,直接上内容吧。

本工程基于XC7Z010CLG400-1构建,使用相位调制法进行调制,基本框图如下:

由FPGA将输入信号两两一组转变为并行信号然后进行相位选择 ,然后根据相位对地址累加器中的值进行调节并输入存储正弦波形的ROM中获取波形值,并输出给DAC模块从而实现QPSK调制。

工程中实际框图如图所示

接下来上代码,ROM初始化数据由MATLAB脚本生成:

脚本:

clear,clc;
N = 256 ;%数据深度

y = zeros(N , 1) ;%生成100行*1列的矩阵
y_integer = zeros(N , 1) ;%生成100行*1列的矩阵
y_hex = zeros(N , 1) ;%生成100行*1列的矩阵,十六进制
for i = 1:1:N %循环1~N,累加1
    x = i-1 ;
    y(i,1) = ceil( 127*sin(x*2*pi/N) ) ;%ceil为四舍五入函数,输出范围为-127~127的正弦波数据
end 
subplot(211);
plot(y);%画图预览
title("原始波形");
axis([-10,260,-130,130]);
fid = fopen('cos_256point_new.coe','wt');    %创建一个名为cos_100point.coe的文件
%- COE 文件前置格式
fprintf( fid, 'MEMORY_INITIALIZATION_RADIX = 16;\n'); %coe文件数据为16进制                    
fprintf( fid, 'MEMORY_INITIALIZATION_VECTOR =\n');
%- 写数据
 

y_integer=y+127;
y_hex= dec2hex(y_integer);
subplot(212);
plot(y_integer);
title("存入数据");
axis([-10,260,-10,260]);
for i = 1:1:N
    if(i == N)
        %最后一个点时,标点为分号,其余时候为逗号
        fprintf(fid,'%c%c;',y_hex(i,1),y_hex(i,2));  %输出16进制数据
    else
        fprintf(fid,'%c%c,\n',y_hex(i,1),y_hex(i,2));  %输出16进制数据
    end
end
fclose(fid);%关闭文件

该脚本会将将一个周期内的正弦波形采样256次,每次采样结果为8bit的16进制数,采样结果存储于文件cos_256point_new.coe中,VIVADO初始化ROM时选中此文件即可。

QPSK调制模块

module QPSK(
    input sys_clk,
    input sys_rst_n,
    input data_clk,
    input data_in,
    output reg sw,
    output [7:0]data_out,
    output  data_out_clk
    );
    
reg [1:0]data_buffer=0;
//reg sw=1'd0;
reg [3:0]thera=0;
reg [3:0]now_thera=0;   
wire carry_signal_clk;//载波频率
always @(negedge data_clk or negedge sys_rst_n)//串并转换
begin
    if(sys_rst_n == 1'b0)
        begin
            data_buffer <= 2'd0;
            sw<=1'd0;
        end
    else 
        begin
            data_buffer[sw]=data_in; 
            sw<=~sw; 
        end        
end
reg thera_cnt=0;
always @(posedge data_clk or negedge sys_rst_n)
begin
    if(sys_rst_n == 1'b0)
        thera<=4'd0;
    else if(sw==1'd0)
      begin
        case (data_buffer)
            2'b00:
                 thera<={thera[1:0],2'd0};   
            2'b01:      
                 thera<={thera[1:0],2'd1};  
            2'b10:      
                 thera<={thera[1:0],2'd2};  
            2'b11:      
                 thera<={thera[1:0],2'd3};  
        endcase
      end
end



//assign thera_edg=((thera[3:2]==thera[1:0]) ? 0:1)&(!sw);//在thera改变时产生上升沿信号
assign thera_edg=((thera[3:2]==thera[1:0]) ? 0:1)&sw;
reg [7:0]rd_addr=0;
reg [7:0]addr_pi_4=0;
reg [7:0]addr_3pi_4=0;
reg [7:0]addr_5pi_4=0;
reg [7:0]addr_7pi_4=0;
always @(posedge data_out_clk or negedge sys_rst_n)//地址选择
begin
    if(sys_rst_n == 1'b0)
        begin
            rd_addr<=8'd32;
            addr_pi_4 =8'd32;
            addr_3pi_4=8'd160;
            addr_5pi_4=8'd224;
            addr_7pi_4=8'd96;
        end
    else 
        begin
          //rd_addr<=rd_addr+1'b1;
          addr_pi_4<=addr_pi_4+1'b1;
          addr_3pi_4<=addr_3pi_4+1'b1;
          addr_5pi_4<=addr_5pi_4+1'b1;
          addr_7pi_4<=addr_7pi_4+1'b1;
              case(thera[1:0])
                2'b00://pi/4
                    rd_addr<=addr_pi_4;
                2'b10://3pi/4
                    rd_addr<=addr_3pi_4;
                2'b01://5pi/4
                    rd_addr<=addr_5pi_4;
                2'b11://7pi/4
                    rd_addr<=addr_7pi_4;
              endcase
        end        
end
data_clock u_data_clock 
 (
  // Clock out ports
  .clk_out1(data_out_clk),
  // Status and control signals
  .reset   (~sys_rst_n),
  .locked  (),
 // Clock in ports
  .clk_in1 (sys_clk)
 );

//ROM存储波形
blk_mem_gen_0  u_rom_256x8b(
    .addra    (rd_addr),
    .clka     (data_out_clk),
    .douta    (data_out)
    );  
   
endmodule

代码中根据寄存器thera的值来切换地址,由于相邻两相位(上图两红线)之间地址差值固定为64,因此实际上此处可省略四个并行的寄存器

reg [7:0]addr_pi_4=0;
reg [7:0]addr_3pi_4=0;
reg [7:0]addr_5pi_4=0;
reg [7:0]addr_7pi_4=0;

改为根据当前相位和目标相位作差,差值乘上64再与rd_addr进行相加减以节省资源。

顶层模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/07/20 21:23:57
// Design Name: 
// Module Name: TOP
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module TOP(
    input sys_clk,
    input sys_rst_n,
    input data_ena_key,
    output ena_led,
    output [7:0] data_out,
    output data_out_clk
    );
wire data_in;
wire data_clock;  
wire  sys_clk1,sys_clk2;

//assign ena_led=~data_ena_key;
QPSK QPSK_modulation(
    .sys_clk    (sys_clk1),
    .sys_rst_n  (sys_rst_n),
    .data_clk   (data_clock),
    .data_in    (data_in),
    .data_out   (data_out),
    .data_out_clk(data_out_clk)
    );

BUFG BUFG_inst(
    .O  (  sys_clk1   ),
    .I  (  sys_clk    )
);
BUFG BUFG_inst2(
    .O  (  sys_clk2   ),
    .I  (  sys_clk    )
);
data_generate baseband_signal(
    .sys_clk   (sys_clk2),
    .sys_rst_n (sys_rst_n),
    .data_ena  (~data_ena_key),
    .data_out  (data_in),
    .data_clk  (data_clock)
    ); 
endmodule

信源码发生模块

module data_generate(
    input sys_clk,
    input sys_rst_n,
    input data_ena,
    output reg data_out,
    output data_clk
    );
 
 reg data_clk_buf=1'b0,data_clk_cnt=1'b0;   
 reg [0:15]data={
                1'b1,
                1'b0,
                1'b1,
                1'b1,
                1'b0,
                1'b1,
                1'b1,
                1'b1,
                1'b0,
                1'b1,
                1'b1,
                1'b1,
                1'b1,
                1'b0,
                1'b0,
                1'b0
                };   
 reg [3:0]counter;
 always@(posedge data_clk_buf or negedge sys_rst_n)
 begin
     if(sys_rst_n == 1'b0|data_ena==1'b0)
        begin
            counter<=4'd0;
            data_out<=1'b1;
            data_clk_cnt<=1'b0;
        end
     else
        begin
            counter<=counter+1'b1;   
            data_out<=data[counter];
            data_clk_cnt<=data_ena;//将数据使能同步至时钟上升沿
        end
 end
 assign data_clk=data_clk_cnt & data_clk_buf;
 
reg [11:0]clk_counter=0;
always@(posedge sys_clk or negedge sys_rst_n)
begin
    if(sys_rst_n==1'b0)
    begin
        clk_counter<=12'd0;
        data_clk_buf<=1'd0;
    end
    else
    begin
        clk_counter<=clk_counter+1'b1;
        if(clk_counter==12'd2500)
        begin
            clk_counter<=12'd0;
            data_clk_buf<=~data_clk_buf;
            
        end
    end
end
endmodule

整体工程简单易懂,直接赋值粘贴即可使用,想偷懒的也可以直接下载原始工程

优化方向:1.省略并行寄存器addr_pi_4,addr_3pi_4,addr_5pi_4,addr_7pi_4改为对rd_addr的加减。

2.缩减ROM空间,只存储正弦波形的前1/4波形。然后将根据条件对rd_addr的值进行处理后输入ROM:<64:rd_addr输入ROM;64≤rd_addr<128:(128-rd_addr)输入ROM;128≤rd_addr<192:(rd_addr-128)输入ROM、ROM输出乘-1;192≤rd_addr<256:(256-rd_addr)输入ROM、ROM输出乘-1

2024/9/13优化后的工程:文章---CSDN

工程:https://download.csdn.net/download/glassca/89632853

基于FPGAQPSK调制是一种利用现场可编程门阵列(FPGA)实现的四相移键控(QPSK)信号调制技术。FPGA是一种可编程逻辑器件,可以通过编程实现各种数字电路和信号处理功能。 QPSK调制是一种数字调制技术,将两个独立的数据比特组合成一个复合的符号,并映射到特定的相位点上。相比较二进制调制QPSK调制可以传输两倍的数据率。基于FPGAQPSK调制系统可以实现高效的信号处理和可靠的通信。 在基于FPGAQPSK调制系统中,首先需要进行数据的生成和调制。数据可以通过FPGA内部的逻辑运算单元生成或外部输入。然后,通过相位偏移键控器(PSK)模块将数据映射到相应的相位点上。FPGA的高度可编程性可以实现不同的相位映射方式,如Grey码映射和传统QPSK映射等。 接下来是将调制后的信号进行滤波和选择性放大。FPGA可以实现数字滤波器的功能,用于去除调制后信号中的噪声和干扰,并对信号进行频率选择性放大。这样可以提高信号质量和抗干扰能力。 最后,通过FPGA上的数字到模拟转换器(DAC)将数字信号转换为模拟信号,并通过发射天线发送到接收端。接收端使用QPSK解调器将接收到的信号转换为数字信号,并通过FPGA进行解调和恢复原始数据。 基于FPGAQPSK调制技术具有灵活性、可编程性和高性能的优势。它可以根据不同的应用需求进行灵活的配置和优化,适用于无线通信、卫星通信、无线电传输等领域。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

睿智の男孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值