我是靠谱客的博主 强健胡萝卜,这篇文章主要介绍FPGA实现数字QAM调制系统前言一、项目设计要求二、各模块及仿真三、例化仿真验证功能总结,现在分享给大家,希望可以做个参考。

目录

前言

一、项目设计要求

二、各模块及仿真

1.m序列发生器

2.串并转换电路

3.电平映射电路

4.载波发生器

5.乘法器

6.加法器

三、例化仿真验证功能

总结


前言

QAM是Quadrature Amplitude Modulation的缩写,中文译名为“正交振幅调制”,其幅度和相位同时变化,属于非恒包络二维调制。本次设计使用环境为Quartus II与Modelsim Altera,项目设计原理图如下:

  

一、项目设计要求

设计任务各模块要求具体如下:

(1)模块时钟生成电路

设计必要的模块时钟生成电路,输出满足电路各模块工作需求的时钟信号。对生成的时钟信号预留仿真输出端口。

(2)m序列发生器

m序列的特征方程为f(x)=1+x+x^{3} ,采用线性移位寄存器来产生,输出数字序列信号m的码速率为4kbps。电路在适当的时钟信号控制下工作,上升沿触发。reset信号高有效时电路异步复位,其复位状态为:全1信号。移位寄存器状态信号A_reg需预留仿真输出端口。

(3)串并转换电路

串并转换模块将串行输入的m序列,逐位依次交替送入I路和Q路,I、Q两路信号分别以2位为一组,生成输出信号I、Q,先输入的串行数据位于并行输出数据的高位。这样,每4位串行输入的二进制序列中,第1bit和第3bit组合成并行2位宽I信号输出;第2bit和第4bit组合成并行2位宽Q信号输出。电路在适当的时钟信号控制下工作,上升沿触发。reset信号高有效时电路异步清零。

(4)电平映射电路

分别将I、Q两信号进行电平映射,得到两路3位宽数据流a、b,映射规则如表1所示,其中a/b使用映射电平补码输出。电路在适当的时钟信号控制下工作,上升沿触发。reset信号高有效时电路异步清零。

表1 I/Q输入信号与映射电平a/b关系表

I/Q信号

映射电平

a/b

00

+3

011

01

+1

001

11

-1

111

10

-3

101

(5)载波信号发生电路

载波信号发生器输出同频正交载波信号c_cos和c_sin,分别表示为c_cos=cos2πf0t和c_sin=sin2πf0t,其中f0=10kHz一个周期内采样200个样值。采样数据存储可以选择使用IP核实现。电路在适当的时钟信号控制下工作,上升沿触发。reset信号高有效时电路异步清零。

6ASK幅度调制电路

分别对a、b两路信号进行ASK幅度调制,得到互为正交的调幅信号I_mod和Q_mod,分别表示为:I_mod=acos2πf0t和Q_mod= bsin2πf0t。电路在适当的时钟信号控制下工作,上升沿触发。reset信号高有效时电路异步清零。

(7)加法电路

设计加法器电路实现输出调制信号qam=acos2πf0t-bsin2πf0t。电路在适当的时钟信号控制下工作,上升沿触发。reset信号高有效时电路异步清零。

二、各模块及仿真

1.m序列发生器

由于m序列的特征方程为f(x)=1+x+x^{3}可知,C3C2C1C0=1011,则使用3级移位寄存器实现,Q[0]与Q[2]异或作为反馈。

代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module mcode(clk, rst, out); input clk, rst; //输入端口 output out; //输出端口 reg[2:0] Q; //中间节点 wire C0; assign C0 = Q[2] ^ Q[0] ; //反馈 assign out = Q[2]; //输出信号 always@(posedge clk or posedge rst) begin if(rst ) Q[2:0] <= 3'b111; //异步清零,全1 else Q[2:0] <= {Q[1:0],C0}; //移位 end endmodule

仿真结果:

由要求可知,M序列的码元速率为4kbps,则所需时钟为4KHz,周期为25000ns,由图可知仿真结果正确,且M序列为1101001的7位码。

2.串并转换电路

串并转换电路原理比较简单,只需要在每个时钟脉冲到来的时候给I和Q的高低位进行赋值,采用计数的方式完成,此处需注意输出I和Q应当在分完一组后再进行输出,不然每个时钟脉冲到来的时候I和Q的数据会变。

代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
module serial_2_parallel(clk, rst, data_in, I, Q); input clk; input rst; input data_in; //序列输入 output reg[1:0] I; output reg[1:0] Q; reg [2:0]cnt;//计数 reg[1:0] data_I; reg[1:0] data_Q; always @(posedge clk or posedge rst) //时序问题,第一次计数不需要进行分配。 begin if(rst) begin I <= 2'b00; Q <= 2'b00; cnt <= 3'b000; end else if(cnt==3'b100) //4次才可以分完一组I和Q,因此分完才刷新I和Q的数据。 begin I <= data_I; Q <= data_Q; cnt <= 3'b001; end else cnt <= cnt + 1'b1; end always @(*) //串并转换 begin case(cnt) 3'b001: data_I [1]<=data_in; 3'b010: data_Q [1]<=data_in; 3'b011: data_I [0]<=data_in; 3'b100: data_Q [0]<=data_in; default: begin data_I=2'b00; data_Q=2'b00; end endcase end endmodule

此处不单独仿真验证,后面将前三个模块联合仿真。

3.电平映射电路

代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module mapping (clk ,rst ,data_I ,a); input clk ,rst; input [1:0] data_I; output reg [2:0] a; always@(*) begin case(data_I) 2'b00: a<=3'b011; 2'b01: a<=3'b001; 2'b11: a<=3'b111; 2'b10: a<=3'b101; default: a<=3'b000; endcase end endmodule

仿真结果:

选择前4组M序列作为参考,即

1101001 1101001 1101001 1101001

    I:10 01 11 01 00 11 10

   Q:10 01 00 11 10 10 01

由上图可知,10为-3、11为-1、01为1、00为3,且串并转换结果正确。

4.载波发生器

载波发生器采用IP核实现,利用matlab对正弦信号进行采样,并生成后缀为mif的文件,将采样数据存入IP中,再利用查表法对IP核中数据进行周期性遍历即可得到正弦载波,余弦波只需改变初始相位即可。后续会出一篇关于DDS信号发生器的文章。

matlab代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
clc; %清除命令行命令 clear all; %清除工作区变量,释放内存空间 F1=1; %信号频率 Fs=199; %采样频率 P1=0; %信号初始相位 N=200; %采样点数 t=[0:1/Fs:(N-1)/Fs]; %采样时刻 ADC=0; %直流分量 A=2^7; %信号幅度 %生成正弦信号 s=A*sin(2*pi*F1*t + pi*P1/180) + ADC; plot(s); %绘制图形 %创建 mif 文件 fild = fopen('sin_wave_255x8.mif','wt'); %写入 mif 文件,格式不可更改% fprintf(fild, '%sn','WIDTH=8;'); %位宽 fprintf(fild, '%snn','DEPTH=512;'); %深度 fprintf(fild, '%sn','ADDRESS_RADIX=UNS;'); %地址格式 fprintf(fild, '%snn','DATA_RADIX=UNS;'); %数据格式 fprintf(fild, '%st','CONTENT'); %地址 fprintf(fild, '%sn','BEGIN'); %开始 for i = 1:N s0(i) = fix(s(i)); %对小数四舍五入以取整 if s0(i) <0 %负数转化为补码 s0(i) = s0(i)+2^8; end fprintf(fild, 't%gt',i-1); %地址编码 fprintf(fild, '%st',':'); %冒号 fprintf(fild, '%d',s0(i)); %数据写入 fprintf(fild, '%sn',';'); %分号,换行 end fprintf(fild, '%sn','END;'); %结束 fclose(fild);

Verilog代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
module wave( input wire clk, input wire rst, output wire [7:0] sin_wave, output wire [7:0] cos_wave ); wire [7:0] addr_1; wire [7:0] addr_2; reg [31:0] F_cnt1; reg [31:0] F_cnt2; parameter F_WODR=667733;//频率控制字=clk*10000/(2^24*200) parameter P_WODR=50;//相位控制字 always@(posedge clk or posedge rst) //生成正弦波 begin if(rst) F_cnt1 <= 32'd0; else if(F_cnt1[31:24]==8'd199) F_cnt1 <= 32'd0; else F_cnt1 <= F_cnt1 + F_WODR; //频率控制字 end always @(posedge clk or posedge rst) //生成余弦波 begin if(rst) F_cnt2 <= {P_WODR, 24'd0}; else if(F_cnt2[31:24]==8'd199) F_cnt2 <= 32'd0; else F_cnt2 <= F_cnt2 + F_WODR; //频率控制 end assign addr_1 = F_cnt1[31:24]; assign addr_2 = F_cnt2[31:24]; wave_rom wave_rom_inst( //调用IP核的数据 .address (addr_1), .clock (clk), .q (sin_wave) ); wave_rom wave_rom_inst2( .address (addr_2), .clock (clk), .q (cos_wave) ); endmodule

仿真结果:

 由图测得正余弦波的周期T=100127ns,则f=1/T≈9987Hz,有点误差,这是由于频率控制字和系统频率不是成整数倍的关系。

5.乘法器

乘法器没什么好说的,注意是带符号的两个数相乘。

代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module ask( input [7:0] wave, input [2:0] data_in, output [10:0] data_out ); wire sign,sign1;//两个操作数的符号位 wire [2:0] abs_a; wire [7:0] abs_I;//a和I的绝对值 assign sign=data_in[2]; assign sign1=wave[7]; wire [10:0] out1; assign abs_a=sign?(~data_in+1):data_in; assign abs_I=sign1?(~wave+1):wave; assign out1=abs_a*abs_I; assign data_out=(sign+sign1)?(~out1+1):out1; endmodule

6.加法器

带符号数的加法器

代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module add( input [10:0] Im, input [10:0] Qm, output [12:0] Qam ); wire [11:0] Im_e;//Im扩展后的补码 wire [11:0] Qm_e;//负Qm扩展后的补码 wire [10:0] Qm_p;//负Qm补码 assign Qm_p = ~(Qm - 1); assign Qm_e = {Qm_p[10] , Qm_p}; assign Im_e = {Im[10] , Im}; assign Qam = Im_e + Qm_e; endmodule

三、例化仿真验证功能

顶层代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
module qam( input clk, input rst, output m, output [2:0] a, output [2:0] b, output [7:0] sin_wave, output [7:0] cos_wave, output [10:0] Im, output [10:0] Qm, output [11:0] Qam ); wire div_clk; divclk U0(clk,rst,div_clk); wire out; mcode U1(div_clk, rst, out); assign m=out; wire [1:0] I,Q; serial_2_parallel U2(div_clk, rst, out, I, Q); wire [2:0] A,B; mapping U3(clk, rst, I, A); mapping U4(clk, rst, Q, B); assign a=A; assign b=B; wave U5(clk, rst, sin_wave, cos_wave); wire [10:0]IM,QM; ask U6(sin_wave, A, IM); ask U7(cos_wave, B, QM); assign Im=IM; assign Qm=QM; add U8(IM, QM, Qam); endmodule

分频代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module divclk(clk,rst,div_clk); input clk,rst; output reg div_clk; reg [31:0] counter; always@(posedge rst or posedge clk)//计数时钟分频模块 begin if(rst) begin counter<=32'h00000000; div_clk<=0; end else if(counter==32'h00001869)// 4KHz计数到6249翻转counter=(clk/div_clk)/2-1 begin counter<=32'h00000000; div_clk <= ~div_clk; end else counter<=counter + 1; end endmodule

测试代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
`timescale 1 ns/ 1 ns module qam_vlg_tst(); reg clk, rst; wire m; wire [2:0] a; wire [2:0] b; wire [7:0] sin_wave; wire [7:0] cos_wave; wire [10:0] Im; wire [10:0] Qm; wire [11:0] Qam; qam U(clk, rst, m, a, b, sin_wave, cos_wave, Im, Qm, Qam); initial begin clk=0; rst=1; #100 rst=0; end always #10 clk=~clk; endmodule

仿真结果:


 由结果可知,乘法器与加法器也没有问题。

总结

以上就是今天要讲的内容,本文仅仅简单介绍了如何使用FPGA实现数字QAM调制,可见FPGA与通信领域的联系比较紧密。需要工程文件的小伙伴评论区留言~

最后

以上就是强健胡萝卜最近收集整理的关于FPGA实现数字QAM调制系统前言一、项目设计要求二、各模块及仿真三、例化仿真验证功能总结的全部内容,更多相关FPGA实现数字QAM调制系统前言一、项目设计要求二、各模块及仿真三、例化仿真验证功能总结内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部