FPGA Verilog 曼彻斯特编码译码 同步 DPLL(二)
前言
前面进行了曼彻斯特编码的讲解,只要注意数据来源频率与编码时钟频率之间的关系就能够精准进行编码工作。同样这两个问题也是保证译码工作的关键!
一、译码时钟与数据之间的相位差
目前是两个cpld开发板之间的通信,板a对数据源进行曼彻斯特编码,将编码完的数据通过传输线接到板b进行译码,但是由于传输过程本身的干扰,数据到达板b之后出现了一定的频率后移,表现为数据与译码时钟存在不规则的相位差,同时这个相位差不断变化影响到了译码的正确率。
如图所示:
这样的话,即使你和编码时钟保持一致,译码也会完全出错,所以这时候需要引入数字锁相环DPLL对曼彻斯特编码进行时钟的提取,保障时钟与数据的对齐,通过数字锁相环消除相位差对于译码造成的影响。不过对于高频的信号来讲,数字锁相环不一定能够完全使得提取的时钟与数据之间的稳定,它会缩小两者之间的相位差,但这时候的相位差小于半个周期,并不会对译码正确率造成影响。
二、数字锁相环
这是整体数字锁相环的工作框架,其中包括相位比较器、振荡器以及滤波器。其中我觉得比较关键的就是相位比较器。这是相位比较器需要实现的功能,通过获取相位差进行生成超前和滞后信号,为后续的振荡器以及滤波器提供输入以及方向。
三、使用步骤
1.顶层结构
代码如下:
/* Top module, output 2 clocks, one for clk_base and one for clk_freq */
//`include "freqdivider_multiple.v"
//`include "phasecomparator.v"
//`include "randomwalkfilter.v"
//`include "variableresetrandomwalkfilter.v"
module dpll(
sys_rst,
MainClock,
SignalIn, SignalOut, SignalOutX2, SynchronousSignal,
Lock,InputSignalEdge, OutputSignalEdge,InputSignalDownEdge,
Positive, Negative,
Lead, Lag,
PeriodCount,DividerMaxValue,
crc_flag,
data,data_code,data_code1,led,led1,led2,
spi_cs,spi_sclk,spi_miso,spi_mosi,
dis_clk,dis_miso,dis_cs,dis_flag,
);
input sys_rst;
input SignalIn; // input signal
input MainClock; // reference signal
output wire SignalOut; // output
output wire SignalOutX2; // output of multiple of 2
output wire Positive, Negative; // internal DPLL signals
output wire Lead, Lag; // internal DPLL signals
output wire InputSignalEdge, OutputSignalEdge,InputSignalDownEdge;
output wire Lock;
output wire SynchronousSignal;
output wire [7:0] PeriodCount;
output wire [7:0] DividerMaxValue;
output led,led1,led2;
output data;
output wire crc_flag;
output wire data_code;
output wire data_code1;
output spi_cs,spi_sclk,spi_mosi;//cs16-pb12 sclk18-pb13 mosi26-pb15
input spi_miso;//miso20-pb14
output dis_clk,dis_miso,dis_cs,dis_flag;
// reg sys_rst=1'b1;
wire [15:0] data_uart;
wire flag;
parameter DividerMultiple = 2;//输出为基带时钟的二倍频,即频带时钟
// phase comparator
phasecomparator inst_ph_cmp(.sys_rst(sys_rst),.MainClock(MainClock), .InputSignal(SignalIn),
.OutputSignal(SignalOut), .Lead(Lead), .Lag(Lag),
.InputSignalEdge(InputSignalEdge), .OutputSignalEdge(OutputSignalEdge),.InputSignalDownEdge(InputSignalDownEdge),
.Lock(Lock), .SynchronousSignal(SynchronousSignal),
.PeriodCount(PeriodCount)
);
/*
// "Zero-Reset Random Walk Filter"
randomwalkfilter inst_zrwf(.MainClock(MainClock), .Lead(Lead), .Lag(Lag),
.Positive(Positive), .Negative(Negative)
);
*/
// defparam inst_freqdiv.DividerMaxValue;
// "Variable-Reset Random Walk Filter"
variableresetrandomwalkfilter inst_zrwf(.MainClock(MainClock), .Lead(Lead), .Lag(Lag),
.Positive(Positive), .Negative(Negative)
);
// controlled frequency divider
freqdivider_multiple inst_freqdivmul(.sys_rst(sys_rst),.MainClock(MainClock), .FrequencyOut(SignalOut), .FrequencyOutX2(SignalOutX2), .DividerMax(PeriodCount/DividerMultiple),
.Positive(Positive), .Negative(Negative), .DividerMaxValue(DividerMaxValue)
);
yima inst_yima(.clk(SignalOut),.rst_n(sys_rst),.rxd(SignalIn),.data_code(data_code)
);
// data_change data_change_inst(.clk(MainClock),.rst_n(sys_rst),.data_i(data_code),.error_flag(crc_flag)
// );
//
//SignalOut-3pin-10m SignalOutX2-2pin-5m
// data_singe_double data_singe_double_inst(
// .clk(SignalOutX2),
// .rst_n(sys_rst),
// .data_i(data_i), //一位输入
// .flag(flag),
// .flag2(flag2),
// .pllout(pllout),
// .data_o(data_o) //8位并行输出
// );
one_sixteen one_sixteen_inst(
.clk(SignalOutX2),
.rst_n(sys_rst),
.din(data_code),
.send(flag),
.dout(data_uart)
);
SPI_Master SPI_Master_inst(
.I_clk(MainClock) , // 全局时钟50MHz
.I_rst_n(sys_rst) , // 复位信号,低电平有效
.I_tx_en(flag) , // 数据打包完成,可以进行spi传输标志
.I_data_in(data_uart) , // 要发送的数据
.I_spi_miso(spi_miso) , // SPI串行输入,用来接收从机的数据
.O_spi_sck(spi_sclk) , // SPI时钟
.O_spi_cs(spi_cs) , // SPI片选信号
.O_spi_mosi(spi_mosi) // SPI输出,用来给从机发送数据
);
assign dis_miso = spi_mosi;
assign dis_clk = spi_sclk;
assign dis_cs = spi_cs;
assign dis_flag = flag;
assign data = SignalIn;
endmodule
2.译码
代码如下:
//该代码主要负责接受曼彻斯特编码 并进行解码 解码后的数据在28pin 展示
module yima(
//global signal
input clk,
input rst_n,
//STM32 port
input rxd,
//input clk_bps_en,
output reg data_code
);
//编码数据定义
reg [1:0] temp; //存储1-01 0-10
reg flag1=0;
reg flag2=0;
reg fail=0;
always @ (posedge clk)
begin
temp <= {rxd,temp[1]};
if(temp == 2'b00 || temp == 2'b11)
begin
flag1 <= 1;//开始译码标志
end
end
always @ (negedge clk)
begin
if(flag1==1 )
begin
flag2 <= ~flag2;//开始译码标志
end
end
always @ (posedge flag2)
begin
if(temp == 2'b10)
begin
data_code <= 0;
fail <= 0;
end
else if(temp == 2'b01)
begin
data_code <= 1;
fail <= 0;
end
else if(temp == 2'b00 || temp ==2'b11)
begin
data_code <= 0;
fail <= 1;
end
end
endmodule
代码太多了,没有全复制,如果有人想看一下可以后台私信联系我。本身这一部分代码是参考网上很多人的进行整合修改的。
总结
实际上板之后仿真通过,通过示波器观察信号波形能够获取正确的信号波形。