网易首页 > 网易号 > 正文 申请入驻

基于FPGA的直接扩频通信系统设计(附代码)

0
分享至

来源:FPGA技术江湖

导读

本篇适用于有一定通信基础的大侠,本篇使用的理论不仅仅是扩频通信。为了便于学习,本章将会以实战的方式,对整个工程的仿真。并对一些关键的仿真结果进行说明。各位大侠可依据自己的需要进行阅读,参考学习。

理论基础

一、扩频通信

香农(E.Shannon)在 1945 年、1948 年和 1949 年连续发表了有关信息论和通信加密以及系统安全性等 3 篇论文。最后给出信道容量的数学计算公式:

( C:信道容量;B:带宽大小;S:信号能量;N:噪声能量 )

根据香农最后给出的信道容量公式(C=B·log2(1+S/N))可知,信道容量与带宽大小正好成正比,不论信噪比(S/N)有多小(但不会为零),只要带宽足够大,信道容量就足够大。根据这个结论,引出了扩频通信技术。

扩频通信,即扩展频谱通信技术(Spread Spectrum Communication),通过扩频调制用一个更高频率的伪随机码将基带信号扩展到一个更宽的频带内,使发射信号的能量被扩展到一个更宽的频带内,从而看来如同噪声一样,使该系统更具隐藏性和抗干扰性。接收端则采用相同的伪随机码进行解扩,从而恢复出原始信息数据。按照频谱扩展的方式的不同,现有的扩频通信系统可以分为直接序列扩频(Direct Sequence Spectrum)工作方式(简称直接扩频方式)、跳变频率(Frequency Hopping)方式(简称跳频方式)和混合方式四种[1]。本文所设计的使用直接序列扩频方式。

直接序列扩频通信是将带传输的二进制信息数据用高速的伪随机码(PN 码)直接调制,实现频谱扩展后传输,在接收端使用相逆方式进行解扩,从而可以恢复信源的信息。最能体现扩频通信的特点就是它具有优异的抗干扰能力。所以它常常被运用于一些干扰性很强的通信领域中。比如无线通信。

二、M序列

2.1 伪随机码概述

伪随机码也称为伪随机序列。是模仿随机序列的随机特性而产生的一种码字,也称为伪噪声序列或者伪噪声吗。直接扩频通信的性能取决于其伪随机序列的性能,伪随机码序列是一种规律难以发现、具有类似白噪声统计特性的编码信号。所以,伪随机序列通常有以下要求:

  • a. ‘0’和’1’的个数基本相等,具有良好的随机性(由于数字通信通常以二进制位多,所以要‘0’的概率和’1’的概率基本相等);

  • b. 具有尖锐的自相关特性,以保证通过同步伪随机序列完成扩频信号的解扩;

  • c. 不同的 PN 序列具有很小的互相关特性,以防止通过不同的 PN 序列扩频后的信号被此干扰;

  • d. 不同的 PN 序列具有很小的互相关特性,以防止通过不同的 PN 序列扩频后的信号被此干扰;

  • e. PN 序列总量大,以满足多用户需求

2.2 伪随机码选型

根据上述要求,常用的序列有包括:m 序列、gold 序列和 Walsh 序列等,m 序列通常容易硬件直接硬件实现;gold 序列自相关性差;Walsh 序列一般使用写入双口 RAM 中,然后启动读取逻辑序列产生,但耗费大量的硬件逻辑单元。故本设计选用了 m 序列作为系统的伪随机码。

2.3 m 序列产生

m 序列是最长线性反馈移位寄存器序列的简称,它是最常用的一种伪随机序列。由 n 级串联寄存器组成,通过反馈逻辑的移位寄存器设定初始状态后,在时钟的触发下,每次移位后各级寄存器状态会发生变化。从任何一个寄存器输出得到的一串序列,该序列称为移位寄存器。其框图如图 1 所示为一个时钟触发下的时序电路。

图1

图中使用 n 个寄存器,通常将 a0 作为输出信号产生 m 序列。从上图也可以看出,一个完成的 n 级 m 序列是由一个相应的线性反馈逻辑表达式,即为:

(其中, ‘⊕’代表异或运算或叫模 2 加运算,Cn ∈{0,1} )

由上式可知,只有当 Cn=1 时,对应的多项式才有效。为了便于表示,通常将上式与本原多项式对应。本原多项式的数学表达式如下:

仅当该多项式为本原多项式时才能产生 m 序列,以下列出部分本原多项式表 1。

表1 2-10 阶本原多项式

其中,n 阶 m 序列具有如下特点:

a. 序列长度为 2n-1;

b.‘0’和‘1’个数相当,即‘1’的个数比‘0’的个数多且仅多 1 个。

本原多项式是由多位科学家及其科学工作者最终得来,关于它们的具体得来,这里不作多解释。

本文设计采用的是 5 阶 m 序列作为系统的伪随机码发生器,其对应的硬件框图如图 2。由于级联的寄存器初始状态不能全为 0。

本设计中规定初始状态为:a4 a3 a2 a1 a0 = 5’b10000。

a0 输出的得到的 m 序列为:{0000101011101100011111001101001}。从左到右顺序输出。

图2

根据以上 m 序列的拓扑结构图,我们就很容易使用 FPGA 的资源来设计5 阶的 m 序列,只要 5 个触发器和 1 个异或门就可以完成该设计。而 Verilog HDL 语言更容易完成设计。具体内容,参考 coder 模块。

三、汉明码

数字信号在传输过程中常常因干扰而发生损坏。接收端接收到数据后可能错误的判决。乘性干扰引起的码间串扰可以采用均衡的办法纠正。而加性干扰的影响则需要其他办法解决。对于加性干扰,本文考虑使用差错控制措施。

差错控制措施,即在数据中间添加必要的监督位,达到可以对错误数据的监督和纠错能力。对于差错控制措施,前辈科学家和科学工作者也设计出多种方法,各有各的优劣。本设计使用的是汉明码(7,4),其中 7 为码组的总长度,4 为原始信息位数,则监督位为 3 位。故每发送 4 比特信息需要添加 3 比特的监督位,监督位是根据信息位既定约束关系得到。汉明码是一种能纠错 1 比特错误的特殊的线性分组码。由于它的编译码简单,在数据通信和计算机存储系统中广泛应用,如蓝牙通信技术和硬盘阵列等。

本设计所使用的汉明码的最小码距为 3,可以纠正 1 为错误,检测 2 位错误。但对 2 位错误码并不能正确的纠错。尽管发生 1 位错的概率相对最高,但在一些比较高的应用中汉明码不能满足要求。码距是指两个不同码组间对应位不同的个数,例如 1000111 和 10001100 的码距为 3。

对于以上介绍比较乏味,以下使用另一种角度来对(7,4)码进行介绍汉明码的原理与设计过程。

我们可以把添加纠错码作为一个系统,即输入 4 比特原始信息位(a6,a5,a4,a3)而输出带有 3 比特监督位(a2,a1,a0)的码组。

对于 3 个监督位,有以下规则:

S1. 监督位 a2 作为 a6、a5 和 a4 的偶校验码,即 a2^a6^a5^a4=0;

S2. 监督位 a1 作为 a6、a5 和 a3 的偶校验码,即 a2^a6^a5^a3=0;

S3. 监督位 a0 作为 a6、a4 和 a3 的偶校验码,即 a2^a6^a4^a3=0;(‘^’表示异或或者表示模 2 加)对应以上 3 个监督位的规则,可以列出其对应的全部码组,如表 2。

表 2

从上表中,不难看出,纠错码产生系统输出由 a6a5a4a3a2a1a0 构成,而每发送一个码组,只发送 4 比特的原始信息。从上表中,也不能直观的说明该编码方式可以纠错 1 位码元,而不能纠错 2位,而图 3 正好可以解释。

图3

图中 3 个大圆圈对应 3 个监督位的三个规则,可以这么理解,如下:

1. 如果接收到的信息只不符合规则“S1”,则对应图中 a2;

2. 如果接收到的信息只不符合规则“S2”,则对应图中 a1;

3. 如果接收到的信息只不符合规则“S3”,则对应图中 a0;

4. 如果接收到的信息不符合规则“S1”和“S2”,则对应图中 a2a1;

5. 如果接收到的信息不符合规则“S1”和“S3”,则对应图中 a2a0;

6. 如果接收到的信息不符合规则“S2”和“S3”,则对应图中 a1a0;

7. 如果接收到的信息不符合规则“S1”、“S2”和“S3”,则对应图中的a2a1a0。

从图 3 中,可以给出一个结论,只要错误码只有 1 位,系统就可以纠正错误;而如果错误码达到 2 位,就无法纠正错误。

根据以上两表对应关系可以推出以下错误规则和误码位置关系的结论,列出如表 3 所示。

表 3

(注明: 对应的 1 表示错误,例如 S1 S2 S3 等于 001,表示接收到的数据违反规则 S3)

从以上对汉明码的原理,到设计使用(7,4)码的设计,设计中的 3 个监督位都可以使用异或操作完成。具体内容将在后面介绍。

四、系统结构

对于该系统,我们最注重的是原始码元汉明码编码、扩频、信道编码、频解码和纠错码系统。当然,由于设计仿真需要模拟一些关于加性干扰,不得不在模拟发送过程中添加干扰源。加上测试平台的模块构成了整个系统的通信方式,便于读者理解。整个系统的拓扑结构图如图 4 所示。

图4

图中包括整个设计的构架,也是数字信号传输的基本模型。包括信源、汉明码编码、m 序列发生器、解扩器、m 序列同步器、汉明码解码器和信宿等。Testbench 平台会把信源和信宿进行比对,输出传输的结果,并且打印到屏幕上供查看。其中,发送端和接收端才可综合,其它模块均用于测试,不可综合。噪声发送器为模拟信道传输过程中的干扰。加法器表示加性干扰。各个模块的代码对应如表 4 所示。

表 4 模块与代码文件对应关系

还有一些相关的文件,将在中篇详细说明。

作为一个底层模块设计人员,以数据流作为主线是必须的。关于本人这个结论,并没有真正的得到老一辈工程师的验证。但在本篇中,就以数据流的方式作为设计主线。

系统的 verilog 实现

一、数据传输过程

从上一章中的拓扑结构图中可知数据流的过程,如图 5 所示。

图 5 mcu

输出给 coder 模块原始信息,每 4 比特作为一组,所以,在 mcu 中,每一个字节拆分成 2 个 4 比特发送到 coder 模块中。

在coder中对原始信号进行扩频、信道编码、最终输出2比特的数据01和11(即 -1 和+1)给 add_noise,经过 add_noise 加性干扰噪声后,输出 3 比特的数据给decoder 模块,decoder 模块经过解扩后输出给 correct 模块纠错,最终发送给 slaver模块。

最终 top 模块根据发送的原始数据和接收后的数据进行比对,输出结果(打印到屏幕上)。这里只是大概的介绍了设计中数据流的过程。在以下各个模块设计中还会具体提到。

二、MCU模块

模块 mcu 负责通信的信源部分,除了给 coder 发送数据,也为 coder 模块和add_noise 模块提供时钟、复位信号。该模块对整个仿真有着相当重要。因为它的设计关系到系统仿真的完整性。

mcu 模块包含随机数据的产生、存储、发送。随机数的产生采用系统函数 random产生。而数据存储有两个位置,一个是输出存储到文件中,另一个是存储到 memory中。存储到文件中是为了提供仿真后数据的查看,而存放 memory 中为了数据的发送和之后数据的比对。

该模块与下游模块 coder 有一定的时序逻辑,它控制 coder 模块的开始,由 mcu发送 send_ena 到 coder,随后等待 coder 模块反馈信号 insourse_ena。Insourse_ena信号有效,则发送数据,否则停止发送数据。数据发送结束只要撤销 send_ena 信号的有效性即可。

具体代码如下:


//***************************************************************/ //模块名: mcu //作 者: The last one //用 途: 包含发送部分全部内容 //版本说明: //***************************************************************/ `timescale 1us/1us module mcu( noised_data //输出带有噪声信号 )
parameter TestNumber = 400; parameter Period = 100;
/**********************发送数据端口信号定义*********************/ wire [1:0] un_noised_data; output [2:0] noised_data; reg clk1,clk31,rst_n; reg send_ena; wire insourse_ena; integer indataFILE; //指向一个文件,用于存储 integer i,j,k; reg [7:0] indata_mem[TestNumber:1]; reg [7:0] indatabyte; wire in_data; assign in_data=indatabyte[7]; // 初始值 initial begin i = 0; j = 1; k = 1; end initial begin rst_n = 0; send_ena = 0; @(posedge clk31) #(Period * 150) rst_n = 1; #(Period * 33) send_ena = 1; end initial begin clk1 = 0; #(Period*3) forever #(Period * 31) clk1 = ~clk1; end initial begin clk31 = 0; #(Period*20) forever #(Period) clk31 = ~clk31; end initial /******************************************** 打开或者创建一个文件(名为 indataRandom.dat) 生成测试用的随机字节写入该文件中 把第一个数据赋给 indatabyte 最后关闭该文件,释放 indataFILE ********************************************/ begin indataFILE = $fopen("./indataRandom.dat"); $display (" indataFILE=%0d ", indataFILE); for(k = 1; k <= TestNumber; k = k+1) begin indata_mem[k]={$random}%256; $fdisplay(indataFILE," %0h ",indata_mem[k]); end indatabyte <= indata_mem[1]; $fclose(indataFILE ); end always@(posedge clk1) /************************************************ 当 coder 使能信号(insourse_ena)到来,每 1 个 clk1 时钟把一个数据传出 传出的数据与 indataRandom.dat 文件的数据一样 ************************************************/ begin if(insourse_ena) if ( j<=TestNumber ) begin if(i<7) begin indatabyte={indatabyte[6:0],1'b0}; i=i+1; end else if (i==7) begin indatabyte=indata_mem[j+1]; j=j+1; i=0; end end else j = 1; else ; end coder coder( .clk1(clk1), .clk31(clk31), .rst_n(rst_n), .send_ena(send_ena), .in_data(in_data), .out_data(un_noised_data), .insourse_ena(insourse_ena) ); add_noise noise( .clk31(clk31), .rst_n(rst_n), .un_noised_data(un_noised_data), .noised_data(noised_data) ); endmodule

三、coder 模块

模块 coder 为原始数据的接收、对数据的汉明码编码、扩频和信道编码等操作。

模块的通信时序如下:

1. 接收到模块 mcu 原始数据到来之前,先发送一个同步头,起止由 mcu 控制;

2. 每发送 128 个字节原始数据前,发送数据 0000 作为数据帧同步,用于检测发送和接收两端数据发送是否同步;

3. 对原始数据进行汉明码编码,监督位为 3 位,全部放到数据位后;

4. 对编好的信息进行扩频,1 比特扩频到 31 比特;

5. 对扩频后的信号进行信道编码,即 1 用 01(+1)、0 用 11(-1)。

扩频通信,原始数据的频率必然比扩频后的频率小得多,本设计的 m 序列码是 31 比特位为一个周期。所以,原始信息的频率假设为 f 1,则扩频频率 f2 = 31x f1。因此,该模块有两个时钟。

该模块采用输入使能信号(send_ena)和输出反馈信号(insourse_ena)作为与上游模块(mcu)的握手信号。当 send_ena 有效,同时 insourse_ena 有效时,mcu 才会发送真实的数据到输入端口。而当 send_ena 信号有效的一段时间内,先发送同步头和数据帧同步后,才使 insourse_ena 有效,发送原始数据。

发送端固定对应的 m 序列为{0000101011101100011111001101001}。则每一个数据的发送都是按该序列发送,在接收端更容易同步(解调时更详细解释)。因此 m 序列的寄存器需一个标示位(flag),使数据和随机码同步送。关

于该模块的工作过程,可以参照下篇的仿真。

该模块的参考具体代码如下:


//************************************************************ //模块名: coder //作 者: The last one //工 程: //用 途: 汉明码编码、扩频、信道编码 //版本说明: //************************************************************ module coder( input wire clk1, input wire clk31, input wire rst_n, input wire send_ena, //发送信号使能 input wire in_data, output reg insourse_ena, // 获取数据,用于与 mcu 握手 output wire [1:0] out_data // 输入数据。 ); parameter idle = 4'b0001, body = 4'b0010; reg in_data_buf; reg out_data_flag; reg check1,check2, check3; // 3 位监督位 reg [4:0] m_coder; //m 系列码组,最低位输出作为 m 序列 reg flag; reg [3:0] state1; reg [7:0] data_number; reg [3:0] state; reg [3:0] state_m; /*************************************************** 作为输出使能模块,并进行信道编码 ***************************************************/ assign out_data = (send_ena && out_data_flag)? (((in_data_buf ^ m_coder[0]) == 1'b1)? 2'b01 : 2'b11) : 2'b10; // 该部分的 ll 信号只用于调制代码时使用 reg ll; always @(posedge clk1) if(!rst_n) ll <= 0; else ll <= in_data_buf; /*********************************************** 主状态机,发送头同步->数据帧同步->数据 每发送 128 个数据又跳转到发送数据帧同步 Start ***********************************************/ always @(posedge clk1) begin if(!rst_n) sys_reset; else if(send_ena) case(state) // synthesis full_case 4'h0 : head; //产生头同步信号 11111111110 4'h1 : data_frames; //数据帧同步信号 0000+000 4'h2 : ready_data; //数据发送 endcase else sys_reset; //复位 end /*************** 复位 Start ****************/ task sys_reset; begin in_data_buf <= 1'b0; insourse_ena <= 1'b0; data_number <= 8'd0; out_data_flag <= 1'b0; flag <= 1'b0; state <= 4'h0; state1 <= 4'h0; check1 <= 1'b0; check2 <= 1'b0; check3 <= 1'b0; end endtask /***************************************** 发送数据帧同步信号 0000+000 Start *****************************************/ task head; begin case(state1) // synthesis full_case 0,1,2,3,4,5,6,7,8,9: begin out_data_flag <= 1'b1; flag <= 1'b1; in_data_buf <= 1'b1; state1 <= state1 + 1'b1; end 10 : begin in_data_buf <= 1'b0; state <= 4'h1; state1 <= 4'h0; end endcase end endtask /***************************************** 发送数据帧同步信号 0000+000 Start *****************************************/ task data_frames; begin case(state1) // synthesis full_case 0,1,2,3,4,5:begin in_data_buf <= 1'b0; state1 <= state1 + 1'b1; end 6 : begin in_data_buf <= 1'b0; state <= 4'h2; state1 <= 4'h0; data_number <= 8'd0; insourse_ena <= 1'b1; end endcase end endtask /******************************************************** 发送真实数据模块,每发送 4 位信息位和 3 位监督位 Start ********************************************************/ task ready_data; begin case(state1) // synthesis full_case 0 :begin insourse_ena <= 1'b1; in_data_buf <= in_data; check1 <= in_data; check2 <= in_data; check3 <= in_data; state1 <= state1 + 1'b1; end 1 :begin insourse_ena <= 1'b1; in_data_buf <= in_data; check1 <= check1 ^ in_data; check2 <= check2 ^ in_data; state1 <= state1 + 1'b1; end 2 :begin insourse_ena <= 1'b1; in_data_buf <= in_data; check1 <= check1 ^ in_data; check3 <= check3 ^ in_data; state1 <= state1 + 1'b1; end 3 :begin in_data_buf <= in_data; check2 <= check2 ^ in_data; check3 <= check3 ^ in_data; state1 <= state1 + 1'b1; insourse_ena <= 1'b0; //暂停主机送来数据,接下来发送监督位 end 4 :begin in_data_buf <= check1; state1 <= state1 + 1'b1; end 5 :begin in_data_buf <= check2; state1 <= state1 + 1'b1; end 6 :begin in_data_buf <= check3; state1 <= 4'h0; if(data_number == 8'd127) begin insourse_ena <= 1'b0; data_number <= 8'd0; state <= 4'h1; end else begin insourse_ena <= 1'b1; data_number <= data_number + 1'b1; end end endcase end endtask /********************************************* m 系列产生 由主机发出使能信号 Start *********************************************/ always @(posedge clk31) begin if(!rst_n) begin state_m <= idle; m_coder <= 5'b01000; end else case(state_m) // synthesis full_case idle : if(flag) state_m <= body; else state_m <= idle; body: begin m_coder[4] <= m_coder[0] ^ m_coder[3]; m_coder[3:0] <= m_coder[4:1]; end endcase end endmodule

四、add_noise 模块

该模块代码的作用是产生干扰,这里所说的干扰都为加性干扰,只要把无干扰数据 01(+1)和 11(-1)分别加上范围在[-2,+2]的随机数。

加干扰后,+1 将会变成 01±[-2,+2] = [-1,+3],-1 将会变成 11±[-2,+2] = [-3,+1]。并且两个范围都是均匀分布。

由于输入数据为 2 个比特,必须扩展后加减法才是我们需要的。具体代码如下:


/************************************************************/ //模块名: mcu //作 者: The last one //用 途: 添加加性干扰 //版本说明: //************************************************************/ module add_noise( clk31, rst_n, un_noised_data, //干扰数据输入 noised_data //添加干扰后输出 ); input clk31, rst_n; input [1:0] un_noised_data; output [2:0] noised_data; reg [2:0] noise; /***************************************************** +1 = [-1,3] -1 = [-3,1] 都是等概率的出现 *****************************************************/ assign noised_data = {un_noised_data[1],un_noised_data} + noise; always @(posedge clk31) if(!rst_n) noise <= 3'd0; else noise <= $random % 3; // noise = [-2,+2] endmodule

模块 mcu、模块 coder、模块 add_noise,使用 mcu 作为顶层模块,激励由 mcu产生、发送输出为加加性噪声后的信号。

五、decoder 模块

decoder 是解扩模块,包括查找同步头、数据同步、解扩。

同步头{1111_1111_110},数据帧同步{0000_000},必须接收到同步头,且同步同步头后和接收数据帧同步,之后才对数据解扩。

由于发送模块和接收模块有时间差,但可以确认的是,必须先接收,后再发送。发送端采用的是固定的 m 序列码作为扩频伪随机码,这样做的利处就是接收端只要采用一样的 m 序列作为解扩码。

由于伪随机序列具有很强的相关性。只要有 1 个时钟错误,解扩结果相差会相当的大。依靠它的这个特性,可以把发送数据一一解扩。(具体解扩过程在仿真部分将更详细说明)。

由于在模块 add_noise 中添加了干扰,发送数据会有一定的误差,所以,解扩过程需使用累加的方法进行。而累加的阀值这里固定在 28,由于累加过程会有减法运算,所以计算初值均为 100。

具体代码如下:


//*******************************************************/ //模块名: mcu //作 者: The last one //用 途: 解扩 //版本说明: //******************************************************/ module decoder( rst_n, ena, clk31x, in_data, out_data, decode_data_flag ); input rst_n; input ena; input clk31x; input [2:0] in_data; output out_data; output decode_data_flag; reg out_data; reg decode_data_flag; reg [39:0] m_coder_buf; reg [7:0] mm; reg temp; reg [7:0] temp_syn; reg [3:0] state; wire [2:0] psumi; //已知的解调系列 wire [30:0] m =31'b1001011001111100011011101010000; /************************************ 取绝对值 正数是本身,负数则取反加 1 ************************************/ assign psumi =(in_data[2]==0)?{in_data[1],in_data[0]}:(~in_data+1); parameter find_head = 4'b0001, synchronize = 4'b0010, //找到头信号之后的同步解码过程以找到 0 时为结束 find_head_end = 4'b0100, //用于解调除 11111111110 以外的所有传输数据 main_body = 4'b1000; reg [7:0] sum1, sum2, sum3, sum4, sum5, sum6, sum7, sum8, sum9, sum10, sum; reg [7:0] i,j; /****************************************************** 产生一个循环的随机码,用于解扩 ******************************************************/ always @(posedge clk31x) begin if(!rst_n || (!ena)) m_coder_buf <= {m[8:0],m}; else m_coder_buf<={m_coder_buf[9:1],m_coder_buf[0],m_coder_buf[30:1 ]}; end always @(posedge clk31x) if(!rst_n || (!ena)) begin state <= find_head; i <= 8'd0; j <= 8'd0; sum1 <= 8'd100; sum2 <= 8'd100; sum3 <= 8'd100; sum4 <= 8'd100; sum5 <= 8'd100; sum6 <= 8'd100; sum7 <= 8'd100; sum8 <= 8'd100; sum9 <= 8'd100; sum10 <= 8'd100; sum <= 8'd100; mm <= 8'd0; decode_data_flag <= 1'b0; temp <= 1'bz; out_data <= 1'bz; temp_syn <= 8'b0000_0000; end else case(state) find_head: /******************************************************** 寻找同步头。 *********************************************************/ begin if(j != 8'd30) begin j <= j + 1'b1; if(in_data[2] == m_coder_buf[i]) sum1 <= sum1 + psumi; else sum1 <= sum1 - psumi; if(in_data[2] == m_coder_buf[i+1]) sum2 <= sum2 + psumi; else sum2 <= sum2 - psumi; if(in_data[2] == m_coder_buf[i+2]) sum3 <= sum3 + psumi; else sum3 <= sum3 - psumi; if(in_data[2] == m_coder_buf[i+3]) sum4 <= sum4 + psumi; else sum4 <= sum4 - psumi; if(in_data[2] == m_coder_buf[i+4]) sum5 <= sum5 + psumi; else sum5 <= sum5 - psumi; if(in_data[2] == m_coder_buf[i+5]) sum6 <= sum6 + psumi; else sum6 <= sum6 - psumi; if(in_data[2] == m_coder_buf[i+6]) sum7 <= sum7 + psumi; else sum7 <= sum7 - psumi; if(in_data[2] == m_coder_buf[i+7]) sum8 <= sum8 + psumi; else sum8 <= sum8 - psumi; if(in_data[2] == m_coder_buf[i+8]) sum9 <= sum9 + psumi; else sum9 <= sum9 - psumi; if(in_data[2] == m_coder_buf[i+9]) sum10 <= sum10 + psumi; else sum10 <= sum10 - psumi; if(sum1 >= 8'd128 || sum2 >= 8'd128 || sum3 >= 8'd128 || sum4 >= 8'd128 || sum5 >= 8'd128 || sum6 >= 8'd128 || sum7 >= 8'd128 || sum8 >= 8'd128 || sum9 >= 8'd128 || sum10 >= 8'd128) begin if(sum1 >= 8'd128) mm <= i; if(sum2 >= 8'd128) mm <= i+1; if(sum3 >= 8'd128) mm <= i+2; if(sum4 >= 8'd128) mm <= i+3; if(sum5 >= 8'd128) mm <= i+4; if(sum6 >= 8'd128) mm <= i+5; if(sum7 >= 8'd128) mm <= i+6; if(sum8 >= 8'd128) mm <= i+7; if(sum9 >= 8'd128) mm <= i+8; if(sum10 >= 8'd128) mm <= i+9; state <= synchronize; end end else begin if(i < 30) i <= i + 8'd10; else i <= 8'd0; j <= 8'd0; if(in_data[2] == m_coder_buf[i]) sum1 <= 8'd100 + psumi; else sum1 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+1]) sum2 <= 8'd100 + psumi; else sum2 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+2]) sum3 <= 8'd100 + psumi; else sum3 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+3]) sum4 <= 8'd100 + psumi; else sum4 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+4]) sum5 <= 8'd100 + psumi; else sum5 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+5]) sum6 <= 8'd100 + psumi; else sum6 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+6]) sum7 <= 8'd100 + psumi; else sum7 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+7]) sum8 <= 8'd100 + psumi; else sum8 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+8]) sum9 <= 8'd100 + psumi; else sum9 <= 8'd100 - psumi; if(in_data[2] == m_coder_buf[i+9]) sum10 <= 8'd100 + psumi; else sum10 <= 8'd100 - psumi; end end synchronize : /********************************************************* 同步同步头 ************************************************/ begin if(mm < 8'd22) temp_syn<={m_coder_buf[mm+7],temp_syn[7:1]}; else temp_syn<= {m_coder_buf[mm-22],temp_syn[7:1]}; if(temp_syn == m[7:0]) begin state <= find_head_end; j <= 8'd0; if(in_data[2] == m_coder_buf[mm]) sum <= 8'd100 + psumi; else sum <= 8'd100 - psumi; end end find_head_end : /************************************************ 找数据帧同步 ************************************************/ begin if(j != 8'd30) begin if(in_data[2] == m_coder_buf[mm]) sum <= sum + psumi; else sum <= sum - psumi; j <= j + 1'b1; end else begin j <= 8'd0; if(in_data[2] == m_coder_buf[mm]) sum <= 8'd100 + psumi; else sum <= 8'd100 - psumi; if(sum >= 8'd100) begin temp <= 1'b1; end else begin temp <= 1'b0; decode_data_flag <= 1'b1; state <= main_body; end end end main_body : /************************************************** 解调数据 ****************************************************/ begin if(j != 8'd30) begin if(in_data[2] == m_coder_buf[mm]) sum <= sum + psumi; else sum <= sum - psumi; j <= j + 1'b1; end else begin j <= 8'd0; if(in_data[2] == m_coder_buf[mm]) sum <= 8'd100 + psumi; else sum <= 8'd100 - psumi; if(sum >= 8'd100) out_data <= 1'b1; else out_data <= 1'b0; end end endcase endmodule

该模块只是对应的解扩,并未涉及信息的检错和纠错,检错将在 correct 模块中进行。

六、correct 模块

模块 correct 将对解扩后的信息就行检错和纠错。检错过程就相当于汉明码编码的逆过程。但(7,4)汉明码仅在 1 位错误的情况下可以检出错误,如果多于 1 位错误,将无法纠错过来(依据 d>e+1;码距 d=3)。

具体代码如下:


//***********************************************************/ //模块名: mcu //作 者: The last one //用 途: 检错、纠错 //版本说明: //***********************************************************/ module correct ( clk1, rst_n, in_data, //输入解调后的数据 out_data, //输出纠错后的结果 decode_data_flag,//来自 decode 模块的信号,用以标 识信号已解调完毕(不包括同步判断信号 11111111110) correct_data_flag,//输出信号,表明已经完成查错和 纠错功能 asyn_flag //输出信号,高电平表示不同步 ); parameter IDLE = 4'b0001, PROCESS = 4'b0010, FLAG_OUT = 4'b0011; input clk1,rst_n; input in_data; input decode_data_flag; output [3:0] out_data; output correct_data_flag; output asyn_flag; reg [3:0] out_data; reg correct_data_flag; reg asyn_flag; reg [6:0] in_data_buf,//输入数据移位寄存器 data_buf; //输入数据缓冲寄存器 reg flag; //读满标示位 reg s1,s2,s3; //纠错码运算结果 reg [11:0] data_number; reg [3:0] state1,state2; always @(posedge clk1) //接收外来的数据 if(!rst_n || !decode_data_flag) begin state1 <= 4'h0; flag <= 1'b0; end else case(state1) /********************************************** 接收解调出来的 7 位数据 接收完 7 个数据后,给 flag 信号,进行数据处理. **********************************************/ 0 : state1 <= 1; 1,2,3,4,5,6 : begin flag <= 1'b0; in_data_buf <= {in_data_buf[5:0],in_data}; state1 <= state1 + 1'b1; end 7 : begin in_data_buf <= {in_data_buf[5:0],in_data}; flag <= 1'b1; state1 <= 4'h1; end endcase always @(posedge clk1) //把接收到的数据进行处理后,送出端口 if(!rst_n || !decode_data_flag) begin s1 <= 1'b0; s2 <= 1'b0; s3 <= 1'b0; data_buf <= 7'hxx; state2 <= IDLE; data_number <= 12'd0; correct_data_flag <= 1'b0; asyn_flag <= 1'b0; end else case(state2) IDLE : begin // 等待 flag 到来 correct_data_flag <= 1'b0; if(flag) begin state2 <= PROCESS; preprocessing; //预加工数据 end else state2 <= IDLE; end PROCESS: begin //纠错处理 correct_task; state2 <= FLAG_OUT; end FLAG_OUT: begin state2 <= IDLE; if(data_number != 12'd1) correct_data_flag <= 1'b1; end default : state2 <= 4'h0; endcase task preprocessing; begin /******************************************************* 计算错码情况,但如果有两个码组错误将无法判定 将 in_data_buf 赋给 data_buf 保存起来 数据计满 903 位(512 信息位,384 监督位,4 位数据帧,3 位数据帧监督位) data_number 赋 0 开始计数(0 -> 902) *******************************************************/ s1<=(in_data_buf[6]^in_data_buf[5]^in_data_buf[4]^in_data_buf[2]); s2<=(in_data_buf[6]^in_data_buf[5]^in_data_buf[3]^in_data_buf[1]); s3<=(in_data_buf[6]^in_data_buf[4]^in_data_buf[3]^in_data_buf[0]); data_buf <= in_data_buf; if(data_number < 902) data_number <=data_number + 1'b1; else data_number <= 12'd0; end endtask task correct_task; begin case({s3,s2,s1}) /*********************************************************** 数据位 监督位 -------------------------------- ------------------- d6 d5 d4 d3 s1 s2 s3 x x x s1 x x x s2 x x x s3 ----------------------------------------------------------- 如果有一位错,必定是监督位错。 如果是第一位数据,判断是否为数据帧(0000) 若不是数据帧,则认为系统没有同步数据帧, 发送 syn_flag 高电平,以下类似. s1,s2,s3 如果错误有两个以上,可以找到他们的相交区间 s1,s2 错误,s3 正确 可以找到 不属于 s3,而同时属于 s1,s2 的数据为 d5 s1,s3 错误,s2 正确 可以找到 不属于 s2,而同时属于 s1,s3 的数据为 d4 s2,s3 错误,s1 正确 可以找到 不属于 s1,而同时属于 s2,s3 的数据为 d3 s1,s2,s3 错误, 而同时属于 s1,s2,s3 的数据为 d6 *************************************************************/ 3'b000,3'b001,3'b010,3'b100 : begin if(data_number == 12'd1) if(data_buf[6:3] == 4'h0) asyn_flag <= 1'b0; else asyn_flag <= 1'b1; else if(data_number <= 902) out_data <= data_buf[6:3]; else ; end 3'b011 :begin if(data_number == 12'd1) if(data_buf[6:3] == 4'b0100) asyn_flag <= 1'b0; else asyn_flag <= 1'b1; else if(data_number <= 902) out_data<={data_buf[6],~data_buf[5],data_buf[4:3]}; else ; end 3'b110 :begin if(data_number == 12'd1) if(data_buf[6:3] == 4'b0001) asyn_flag <= 1'b0; else asyn_flag <= 1'b1; else if(data_number <= 902) out_data <= {data_buf[6:4],~data_buf[3]}; else ; end 3'b101 :begin if(data_number == 12'd1) if(data_buf[6:3] == 4'b0010) asyn_flag <= 1'b0; else asyn_flag <= 1'b1; else if(data_number <= 902) out_data<={data_buf[6:5],~data_buf[4],data_buf[3]}; else ; end 3'b111 :begin if(data_number == 12'd1) if(data_buf[6:3] == 4'b1000) asyn_flag <= 1'b0; else asyn_flag <= 1'b1; else if(data_number <= 902) out_data <= {~data_buf[6],data_buf[5:3]}; else ; end default : ; endcase end endtask endmodule

七、Correct_Decoder 模块

模块 Correct_Decoder 是模块 decoder 和模块 correct 的顶层模块。

代码如下:


//*******************************************************/ //模块名: Correct_Decoder //作 者: The last one //用 途: 解扩和纠错模块的顶层模块 //版本说明: //*******************************************************/ module Correct_Decoder( rst_n, clk1, clk31x, ena_decoder, noised_data, pro_correct_data, correct_data_flag, asyn_flag ); input rst_n, clk1, clk31x; input ena_decoder; //使能 decoder 信号 input [2:0] noised_data;//从 add_noise 输出的噪声信号,等待解调 output [3:0] pro_correct_data; //处理后输出的数据 output correct_data_flag; //错误码标示位 output asyn_flag; //系统数据帧同步信号 wire pro_decode_data; wire decode_data_flag; decoder decoder( .rst_n(rst_n), .ena(ena_decoder), .clk31x(clk31x), .in_data(noised_data), .out_data(pro_decode_data), .decode_data_flag(decode_data_flag) ); correct correct( .clk1(clk1), .rst_n(rst_n), .in_data(pro_decode_data), .out_data(pro_correct_data), .decode_data_flag(decode_data_flag), .correct_data_flag(correct_data_flag), .asyn_flag(asyn_flag) ); Endmodule

八、slaver 模块

模块 slaver 充当信宿。它接收来自于模块 correct 纠错后的数据,对数据进行保存。以便查看结果。

模块 slaver 作为接收端,它将给解扩和纠错模块提供时钟信号,但其的起始必须必发送的起始快。并且它所产生的时钟可以是随机的开始,以 mcu 模块产生的时钟没有相位上的关系。

其代码如下:


特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
林更新又开骂了,怼人小能手上线粉丝都帮不上忙,笑死在评论区

林更新又开骂了,怼人小能手上线粉丝都帮不上忙,笑死在评论区

执着的人
2024-06-20 07:32:27
复旦毕业典礼,学生暴打老师,聊天记录曝光,细节曝光果然不简单

复旦毕业典礼,学生暴打老师,聊天记录曝光,细节曝光果然不简单

眼光很亮
2024-06-20 08:00:13
轮到犹太人了!250枚火箭弹突袭以色列,数十万人半夜逃亡

轮到犹太人了!250枚火箭弹突袭以色列,数十万人半夜逃亡

王子看台海
2024-06-19 10:33:08
颠覆性突破!ASML发布超级EUV光刻机:可量产0.2nm工艺

颠覆性突破!ASML发布超级EUV光刻机:可量产0.2nm工艺

中关村在线
2024-06-19 11:31:25
F-16登场,留给侵略者耀武扬威的时间不多喽

F-16登场,留给侵略者耀武扬威的时间不多喽

临墨有余
2024-06-13 09:06:39
玉渊谭天:一场闭门会,业界提到更多对欧反制措施

玉渊谭天:一场闭门会,业界提到更多对欧反制措施

环球网资讯
2024-06-19 17:12:09
涉嫌严重违纪违法,中山市实验中学原党委书记、校长蒋晓敏接受审查调查

涉嫌严重违纪违法,中山市实验中学原党委书记、校长蒋晓敏接受审查调查

鲁中晨报
2024-06-20 10:03:05
噩耗:他于6月19日去世。出身“中国最牛家族”,干出七个第一

噩耗:他于6月19日去世。出身“中国最牛家族”,干出七个第一

华人星光
2024-06-19 16:23:29
当了局领导有这么多好处,怪不得在体制内的人都想当官

当了局领导有这么多好处,怪不得在体制内的人都想当官

时尚的弄潮
2024-06-20 07:30:23
原来夏天竟是夫妻空调温度之争的战场!网友:半夜摸男朋友都凉了

原来夏天竟是夫妻空调温度之争的战场!网友:半夜摸男朋友都凉了

有趣的火烈鸟
2024-06-19 19:13:35
毛主席病重去世,夫人江青扑在他的遗体上崩溃痛哭,跪求医生救他

毛主席病重去世,夫人江青扑在他的遗体上崩溃痛哭,跪求医生救他

华人书画艺术
2024-06-19 09:32:07
“滚出中国”,主持人:你是加拿大国籍,为什么要回国接商演捞钱

“滚出中国”,主持人:你是加拿大国籍,为什么要回国接商演捞钱

功标青史
2024-06-19 17:27:26
医生提醒:过了65岁的人,宁愿一周不洗澡,也不要随便做这4事

医生提醒:过了65岁的人,宁愿一周不洗澡,也不要随便做这4事

今日养生之道
2024-06-19 19:50:37
现在的年轻人已经不再尊重电脑了

现在的年轻人已经不再尊重电脑了

差评
2024-06-17 14:26:12
笑不活了!为什么中国人不喜欢穿西装,看了网友的分享笑麻了

笑不活了!为什么中国人不喜欢穿西装,看了网友的分享笑麻了

奇特短尾矮袋鼠
2024-06-18 18:40:47
“网约车之王”卖不动了?

“网约车之王”卖不动了?

蓝鲸财经
2024-06-19 13:10:14
这是疯狂祸害社会底层老百姓的血汗啊,太缺德了!

这是疯狂祸害社会底层老百姓的血汗啊,太缺德了!

雪中风车
2024-06-16 19:24:42
外蒙古有多离谱,“性旅游业”带动经济, 沦落为风俗业大国

外蒙古有多离谱,“性旅游业”带动经济, 沦落为风俗业大国

青栀伊人
2024-06-18 21:57:53
阿尔沙文:C罗领袖气质过盛,他已不再是葡萄牙最好的球员

阿尔沙文:C罗领袖气质过盛,他已不再是葡萄牙最好的球员

直播吧
2024-06-19 16:08:15
知名品牌被传倒闭,公司辟谣:没有!员工:深圳办公基地被卖了,部分人被“卖给”新公司,另一部分人“停工停产”

知名品牌被传倒闭,公司辟谣:没有!员工:深圳办公基地被卖了,部分人被“卖给”新公司,另一部分人“停工停产”

每日经济新闻
2024-06-19 21:30:08
2024-06-20 11:16:49
EETOP半导体社区
EETOP半导体社区
国内著名的电子工程师社区
4780文章数 15215关注度
往期回顾 全部

科技要闻

苹果回应AI仅限iPhone15Pro:不是为卖新机

头条要闻

环球:《我的阿勒泰》火到国外 西方媒体破防了

头条要闻

环球:《我的阿勒泰》火到国外 西方媒体破防了

体育要闻

绿军的真老大,开始备战下赛季了

娱乐要闻

黄一鸣“杀疯了” 直播间卖大葱养孩子

财经要闻

茅台大跌,谁的锅?

汽车要闻

售价11.79-14.39万元 新一代哈弗H6正式上市

态度原创

亲子
游戏
时尚
房产
教育

亲子要闻

网友路边看见的双胞胎宝宝,在车里乖乖等待爸妈回来

高手福利散人福音,梦幻西游全民PK赛终于有“单挑组”了!

“T恤”作为夏季的基础款,竟然有这么多种穿法

房产要闻

海棠湾!一所重量级国际学校真的来了!

教育要闻

初三最后一课老师弹吉他为学生鼓劲:希望同学们考出优异的成绩

无障碍浏览 进入关怀版