基于插值算法和Gardner定时误差检测的OOK信号定时同步的FPGA实现
本文介绍如何用FPGA实现基于插值算法的OOK信号定时同步,Verilog代码参考杜勇《数字调制解调技术的MATLAB与FPGA实现》。我们的目标是用外部提供50MHz时钟的zynq7100芯片实现400MHz采样频率和100Mbps的OOK数字基带信号的定时同步。
采用传统的锁相环技术实现定时同步时,本地时钟需要有较高的频率。当数据采样频率很高,并且本地时钟受到器件性能限制而不能远高于采样频率时,锁相环技术性能不佳。插值算法可以不改变采样时钟的频率和相位来实现位同步信号的调整,同时,插值算法可以根据采样值以及数控振荡器输出的采样时刻信号和误差信号获取最佳采样值。
插值位同步算法的框图如下图所示(图片源于文献[1])。主要模块为括插值滤波器(INTERPOLATOR)、定时误差检测器(TIMING ERROR DETECTOR)、环路滤波器(LOOP FILTER)和数控振荡器(CONTROLLER)。下面我们一一介绍并给出Verilog代码。
插值滤波器
插值滤波器的功能是速率转换,上图显示了内插滤波器的原理。设采样周期为TsT_sTs,符号周期为TTT,插值周期为TiT_iTi,插值滤波器的脉冲响应为hI(t)h_I(t)hI(t)。由上图可知,插值滤波器输出的连续时间信号为 y(t)=∑mx(m)hI(t−mTs)y(t)=\sum_{m}x(m)h_I(t-mT_s)y(t)=m∑x(m)hI(t−mTs) 按插值周期对y(t)y(t)y(t)进行采样得 y(kTi)=∑mx(m)hI(kTi−mTs)(1)y(kT_i)=\sum_{m}x(m)h_I(kT_i-mT_s) \tag{1}y(kTi)=m∑x(m)hI(kTi−mTs)(1) 上式中,mmm是采样信号的索引,下面定义一个滤波器的索引 i=int[kTiTs]−mi=int[\frac{kT_i}{T_s}]-mi=int[TskTi]−m 基准点索引 mk=int[kTiTs]m_k=int[\frac{kT_i}{T_s}]mk=int[TskTi] 分数间隔 μk=kTiTs−mk\mu_k=\frac{kT_i}{T_s}-m_kμk=TskTi−mk 公式(1)(1)(1)可以重写为 y(kTi)=y[(mk+μk)Ts]=∑ix[(mk−i)Ts]hI[(i+μk)Ts]y(kT_i)=y[(m_k+\mu_k)T_s]=\sum_{i}x[(m_k-i)T_s]h_I[(i+\mu_k)T_s]y(kTi)=y[(mk+μk)Ts]=i∑x[(mk−i)Ts]hI[(i+μk)Ts] 其中kTikT_ikTi表示第kkk个插值点,mkTsm_kT_smkTs表示第kkk个插值点前相邻的采样点, μkTs\mu_kT_sμkTs是插值点kTikT_ikTi与采样点mkTsm_kT_smkTs之间的时间间隔。下图展示了采样点与插值点之间的关系。
根据文献[2],我们采用Farrow结构的插值滤波器,如下图所示。
插值滤波器有三条纵向支路和一条横向支路,它们的计算公式为 f1=0.5x(m)−0.5x(m−1)−0.5x(m−2)+0.5x(m−3)f2=−0.5x(m)+1.5x(m−1)−0.5x(m−2)−0.5x(m−3)f3=x(m−2)y(k)=f1μk2+f2μk+f3\begin{aligned} &f_1 = 0.5x(m)-0.5x(m-1)-0.5x(m-2)+0.5x(m-3) \\ &f_2 = -0.5x(m)+1.5x(m-1)-0.5x(m-2)-0.5x(m-3) \\ &f_3=x(m-2) \\ & y(k)=f_1\mu_k^2+f_2\mu_k+f_3 \end{aligned}f1=0.5x(m)−0.5x(m−1)−0.5x(m−2)+0.5x(m−3)f2=−0.5x(m)+1.5x(m−1)−0.5x(m−2)−0.5x(m−3)f3=x(m−2)y(k)=f1μk2+f2μk+f3
下面给出插值滤波器的Verilog代码。其中,mult18_16模块是vivado的乘法器IP核,设置了2级流水线;f1、f2、f3f_1、f_2、f_3f1、f2、f3的放在always内用时序逻辑描述(书上用的是组合逻辑),否则时序仿真时会出现很多毛刺造成性能下降。
`timescale 1ns / 1ps
module interpolate_filter(input resetn,input clk,input signed [14:0] data_in, // 采样数据,采样频率同clk频率input signed [15:0] uk, //分数间隔output signed [17:0] data_out //插值滤波输出,输出速率同clk频率);
reg signed [14:0] din_1,din_2,din_3,din_4,din_5,din_6;
reg signed [15:0] u_1,u_2;
wire signed [33:0] f1_u,f2_u;
wire signed [33:0] f1_u2;
reg signed [33:0] f2_u_1,f2_u_2;
reg signed [17:0] f1;
reg signed [17:0] f2;
reg signed [17:0] f3;
always @(posedge clk or negedge resetn) beginif(!resetn) begindin_1 <= 15'd0;din_2 <= 15'd0;din_3 <= 15'd0;din_4 <= 15'd0;din_5 <= 15'd0;din_6 <= 15'd0;u_1 <= 16'd0;u_2 <= 16'd0;f2_u_1 <= 32'd0;f2_u_2 <= 32'd0;f1 <= 18'd0;f2 <= 18'd0;f3 <= 18'd0;endelse begindin_1 <= data_in;din_2 <= din_1;din_3 <= din_2;din_4 <= din_3;din_5 <= din_4;din_6 <= din_5;u_1 <= uk;u_2 <= u_1;f2_u_1 <= f2_u;f2_u_2 <= f2_u_1;f1 <= {{4{data_in[14]}},data_in[14:1]}-{{4{din_1[14]}},din_1[14:1]}-{{4{din_2[14]}},din_2[14:1]}+{{4{din_3[14]}},din_3[14:1]};f2 <= {{3{din_1[14]}},din_1}+{{4{din_1[14]}},din_1[14:1]}-{{4{data_in[14]}},data_in[14:1]}-{{4{din_2[14]}},din_2[14:1]}-{{4{din_3[14]}},din_3[14:1]};f3 <= {{3{din_6[14]}},din_6}; //一个乘法器延时2个clkend
end
//乘法器2级流水线
mult18_16 u1 (.CLK(clk), // input wire CLK.A(f1), // input wire [17 : 0] A.B(uk), // input wire [15 : 0] B.P(f1_u) // output wire [33 : 0] P
);
//乘法器2级流水线
mult18_16 u2 (.CLK(clk), // input wire CLK.A(f2), // input wire [17 : 0] A.B(uk), // input wire [15 : 0] B.P(f2_u) // output wire [33 : 0] P
);
//乘法器2级流水线
mult18_16 u3 (.CLK(clk), // input wire CLK.A(f1_u[32:15]), // input wire [17 : 0] A //matlab仿真得f1_u有效位宽不超过31bit.B(u_2), // input wire [15 : 0] B.P(f1_u2) // output wire [33 : 0] P
);
wire signed [18:0] dt;
assign dt = (!resetn)? 19'd0:(f2_u_2[33:15]+f1_u2[33:15]+{f3,1'b0});//小数位对齐
reg signed [17:0] dt_1;
always @(posedge clk or negedge resetn) beginif(!resetn) begindt_1 <= 18'd0; endelse begindt_1 <= dt[17:0];end
end
assign data_out = dt_1;
endmodule
Gardner定时误差检测算法
Gardner定时误差检测算法根据插值滤波器输出的插值数据进行定时误差检测。一个符号周期内只需要两个插值点,一个插值点出现在数据的峰值时刻,另一个插值点出现在两个数据峰值的中间时刻。
Gardner定时误差检测算法的公式为 μt(k)=I(k−12)[I(k)−I(k−1)]\mu_t(k)=I(k-\frac{1}{2})[I(k)-I(k-1)]μt(k)=I(k−21)[I(k)−I(k−1)] 其中,I(k)I(k)I(k)表示第kkk个码元数据选通时的插值,I(k−12)I(k-\frac{1}{2})I(k−21)表示第kkk和k−1k-1k−1个码元中间时刻的插值。我们还可以用插值信号的正负号代替插值的实际值,虽然有一定的性能损失,但是提高了追踪能力,而且系统实现简单,公式为μt(k)=I(k−12){sgn[I(k)]−sgn[I(k−1)]}\mu_t(k)=I(k-\frac{1}{2})\{sgn[I(k)]-sgn[I(k-1)]\}μt(k)=I(k−21){sgn[I(k)]−sgn[I(k−1)]}
Gardner定时误差检测和环路滤波器的代码写在一个模块内,后面一起给出代码。
环路滤波器和数字振荡器
环路滤波器
采用二阶环路滤波器,滤波器框图如下图所示。
环路滤波器的传递函数为 H(z)=C1+C2z−11−z−1H(z)=C_1+\frac{C_2z^{-1}}{1-z^{-1}}H(z)=C1+1−z−1C2z−1 C1、C2C_1、C_2C1、C2可以下式计算 C1=8BLTs3C2=32(BLTs)29\begin{aligned} &C_1=\frac{8B_LT_s}{3} \\ &C_2=\frac{32(B_LT_s)^2}{9} \end{aligned} C1=38BLTsC2=932(BLTs)2其中,BLTsB_LT_sBLTs为单边噪声带宽与采样周期的乘积,通常要求BLTs≪0.1B_LT_s\ll0.1BLTs≪0.1。为了在FPGA中用移位运算代替乘除运算并简化设计,我们取C1=2−8,C2=0C_1=2^{-8},C_2=0C1=2−8,C2=0。
下面给出定时误差检测和环路滤波器的Verilog代码。环路滤波器输出的误差信号量化为16bit定点数,其中包括1bit符号位和15bit的小数位。
module ted_loop_filter #(parameter SPS_2 = 2 // 上采样率(采样速率与数据速率之比)的一半,)(input resetn,input clk,input strobe,input signed [17:0] data_in, //插值滤波器输出得插值数据output signed [17:0] data_out, //最佳采样时刻得插值数据,用于判决0、1output signed [15:0] wk, //环路滤波器输出定时误差信号,15 bit小数位output sync //位同步信号);
reg [3:0] strobe_cnt;
reg sk;
always @(posedge clk or negedge resetn) beginif(!resetn) beginstrobe_cnt <= 4'd0;sk <= 1'b0;endelse beginif(strobe) beginstrobe_cnt <= (strobe_cnt >= SPS_2-1'b1)?4'd0:(strobe_cnt+1'b1);sk <= (strobe_cnt >= SPS_2-1'b1)?(1'b1):1'b0; // sk翻转周期位符号周期,作为位定时时钟输出endend
end
assign sync = sk;
reg signed [17:0] din_1,din_2,din_3;
reg signed [17:0] dout;
reg signed [17:0] err,err_1;
reg signed [15:0] w;
always @(posedge clk or negedge resetn) beginif(!resetn) begindin_1 <= 18'd0;din_2 <= 18'd0;din_3 <= 18'd0;dout <= 18'd0;err <= 18'd0;err_1 <= 18'd0;w <= 16'b0100000000000000;endelse beginif((strobe_cnt==0 || strobe_cnt==SPS_2-1) && strobe) begindin_1 <= data_in;din_2 <= din_1;din_3 <= din_2;err <= (!din_1[17] && din_3[17])?{din_2[17:1],1'b0}:((din_1[17] && !din_3[17])?(-{din_2[17:1],1'b0}):18'd0);if(sk) begindout <= din_1;err_1 <= err;//w(ms+1)=w(ms)+c1*(err(ms)-err(ms-1))+c2*err(ms), c1 = 2^(-8), c2≈0w <= w+{{6{err[17]}},err[17:8]}-{{6{err_1[17]}},err_1[17:8]};endendend
end
assign wk = w;
assign data_out = dout;
endmodule
数控振荡器(NCO)
数控振荡器是一个相位递减器,它的差分方程为 η(m+1)=[η(m)−w(m)]mod1\eta(m+1)=[\eta(m)-w(m)]mod1η(m+1)=[η(m)−w(m)]mod1 其中η(m+1)\eta(m+1)η(m+1)是nco寄存器的值,w(m)w(m)w(m)是环路滤波器输出的定时误差值。
nco的原理如上图所示。根据相似三角形的性质可以得出下式μkTsη(mk)=(1−μk)Ts1−η(mk+1)\frac{\mu_kT_s}{\eta(m_k)}=\frac{(1-\mu_k)T_s}{1-\eta(m_k+1)}η(mk)μkTs=1−η(mk+1)(1−μk)Ts 分数间隔为μk=η(mk)1−η(mk+1)+η(mk)=η(mk)w(mk)(2)\mu_k=\frac{\eta(m_k)}{1-\eta(m_k+1)+\eta(m_k)}=\frac{\eta(m_k)}{w(m_k)} \tag{2}μk=1−η(mk+1)+η(mk)η(mk)=w(mk)η(mk)(2)
当环路稳定时,nco每隔TsT_sTs时间就递减w(m)w(m)w(m),即平均nco每1w(m)\frac{1}{w(m)}w(m)1个时钟就下溢一次,那么插值周期与采样周期有近似关系Ti=Tsw(m)T_i=\frac{T_s}{w(m)}Ti=w(m)TsGardner定时误差算法每个符号内有两个插值点参与运算,故T≈2TiT\approx2T_iT≈2Ti。Farrow插值滤波器每个插值需要4个采样点,故T≈4TsT\approx4T_sT≈4Ts。所以w(m)≈0.5w(m)\approx0.5w(m)≈0.5。公式(2)(2)(2)可以改写为μk≈2η(mk)\mu_k\approx2\eta(m_k)μk≈2η(mk)或者一阶修正公式μk≈2η(mk)[2−2w(mk−1)]\mu_k\approx2\eta(m_k)[2-2w(m_k-1)]μk≈2η(mk)[2−2w(mk−1)]
下面给出定数控振荡器的Verilog代码。理论分析中的小数(分数)都采用16bit定点数量化。理论上,当nco寄存器的值下溢时进行一次插值。在FPGA中,插值是在不断进行的,但是只有nco寄存器的值下溢时获得的插值有用。
module nco(input resetn,input clk,input signed [15:0] wk, //环路滤波器输出定时误差信号,15 bit小数位output signed [15:0] uk, //NCO输出的插值间隔小数,15 bit小数位output strobe //NCO输出的插值计算选通信号,高电平有效);
reg signed [16:0] nkt;
reg signed [16:0] ut;
reg str;
always @(posedge clk or negedge resetn) beginif(!resetn) beginnkt <= 17'b00110000000000000; //0.75ut <= 17'b00100000000000000; //0.5str <= 1'b0;endelse beginif(nkt < {wk[15],wk}) begin // 负值+1,相当于mod(1);nkt <= 17'b01000000000000000+nkt-{wk[15],wk};ut <= {nkt[14:0],1'b0}; //取出nkt减去wk之前的值,乘以2作为u值输出str <= 1'b1;endelse beginnkt <= nkt-{wk[15],wk};str <= 1'b0;endend
end
assign uk = ut;
assign strobe = str;
endmodule
定时同步顶层文件
将前面几个模块整合起来,获得完整的定时同步模块。
(* dont_touch = "yes" *) module bit_sync #(parameter SPS_2 = 2 // 上采样率(采样速率与数据速率之比)的一半,)(input resetn,input clk,input signed [14:0] data_in, //采样数据output data_out, //位同步后0、1bitoutput sync //位同步脉冲);
(* dont_touch = "yes" *) reg signed [14:0] din;
always @(posedge clk or negedge resetn) beginif(!resetn) begindin <= 15'd0;endelse begindin <= data_in;end
end
(* dont_touch = "yes" *) wire signed [15:0] uk,wk;
(* dont_touch = "yes" *) wire signed [17:0] data_interpolate;
(* dont_touch = "yes" *) wire strobe;
(* dont_touch = "yes" *) wire [17:0] dout;
(* dont_touch = "yes" *) wire bit_sync;
(* dont_touch = "yes" *) interpolate_filter u1(.resetn(resetn),.clk(clk),.data_in(din), // 采样数据,采样频率同clk频率.uk(uk), //分数间隔.data_out(data_interpolate) //插值滤波输出,输出速率同clk频率);
(* dont_touch = "yes" *) ted_loop_filter #(.SPS_2(SPS_2) // 上采样率(采样速率与数据速率之比)的一半,)u2 (.resetn(resetn),.clk(clk),.strobe(strobe),.data_in(data_interpolate), //插值滤波器输出得插值数据.data_out(dout), //最佳采样时刻得插值数据,用于判决0、1.wk(wk), //环路滤波器输出定时误差信号,15 bit小数位.sync(bit_sync) //位同步信号);
(* dont_touch = "yes" *) nco u3(.resetn(resetn),.clk(clk),.wk(wk), //环路滤波器输出定时误差信号,15 bit小数位.uk(uk), //NCO输出的插值间隔小数,15 bit小数位.strobe(strobe) //NCO输出的插值计算选通信号,高电平有效);
(* dont_touch = "yes" *) reg bit_dout;
(* dont_touch = "yes" *) reg bit_sync_1;
(* dont_touch = "yes" *) reg bit_sync_2;
always @(posedge clk or negedge resetn) beginif(!resetn) beginbit_dout <= 1'b0;bit_sync_1 <= 1'b0;bit_sync_2 <= 1'b0;endelse beginbit_sync_1 <= bit_sync;if(bit_sync & (!bit_sync_1)) beginbit_dout <= !dout[17];bit_sync_2 <= 1'b1;endelse beginbit_dout <= bit_dout;bit_sync_2 <= 1'b0;endend
end
assign data_out = bit_dout;
assign sync = bit_sync_2; //上升沿
endmodule
我们要实现400MHz采样频率,同步100Mbps的OOK信号,而FPGA的外部晶振只有50MHz,所以还要再放一个PLL IP核将时钟倍频至400MHz。将PLL IP核和位同步模块封装在一起。
module test_top(input resetn,input clk, // 50MHzinput signed [14:0] data_in, //采样数据output clk400M,output data_out, //位同步后0、1bitoutput sync //位同步脉冲);
wire locked;
clk_wiz_0 u1(// Clock out ports.clk_out1(clk400M), // output clk_out1// Status and control signals.reset(~resetn), // input reset.locked(locked), // output locked// Clock in ports.clk_in1(clk)); // input clk_in1
bit_sync #(.SPS_2(2) // 上采样率(采样速率与数据速率之比)的一半,)u2 (.resetn(resetn&locked),.clk(clk400M),.data_in(data_in), //采样数据.data_out(data_out), //位同步后0、1bit.sync(sync) //位同步脉冲);endmodule
时序仿真
创建vivado工程,选择zynq7100芯片,分析,综合,编写testbench文件,然后进行综合后时序仿真。
testbench文件如下。adc_data.txt是用MATLAB生成的存放波形数据的文件,定时同步结果输出到bit_sync.txt用于计算误码率。
module tb_test_top();
parameter LEN = 4915200;
reg resetn;
reg clk;
reg [14:0] data [LEN-1:0];
reg [14:0] data_in;
wire clk400M;
wire data_out;
wire sync;
integer k;
integer file;
initial beginresetn = 1'b0;clk = 1'b1;data_in <= 15'd0;k = 0;file = $fopen("bit_sync.txt");$readmemh("adc_data.txt",data);#100resetn = 1'b1;#1600for(k = 0; k < LEN; k = k+1) begin#2.5 //400Mhzdata_in <= data[k];end#2.5data_in <= 15'd0;#100 $finish;
end
always #10 clk = ~clk; // 50Mhz
always @(posedge sync)$fdisplay(file,"%d",data_out);
test_top u1 (.resetn(resetn),.clk(clk),.data_in(data_in), //采样数据.clk400M(clk400M),.data_out(data_out), //位同步后0、1bit.sync(sync) //位同步脉冲);
endmodule
等一会儿,时序仿真很慢,然后能看到仿真波形。
参考文献
[1] Gardner F M. Interpolation in Digital Modems-Part I: Fundamentals[J]. IEEE Trans. Commun, 1993, 41(3):501-507. [2] Erup L, Gardner F M, Harris R A. Interpolation in digital modems-Part II : Implementation and performance[J]. IEEE Trans. Commun, 1993, 41(6):998 - 1008. [3] Gardner F M. A BPSK/QPSK Timing-Error Eetector for Sampled Receivers[J]. IEEE Trans. Commun, 1986, 34(5):423-429. [4] 杜勇. 数字调制解调技术的MATLAB与FPGA实现[M]. 北京:电子工业出版社, 2014.
基于插值算法和Gardner定时误差检测的OOK信号定时同步的FPGA实现相关推荐
- 数字定时误差检测算法系列之 ———时域Gardner算法
1.Gardner 算法简介 Gardner 算法是一种非数据辅助,实现复杂度低,广泛应用于实际工程中的一种定时误差检测算法.Gardner 算法具有如下特点: 1). 工作于2倍采样率,每个码元需要 ...
- smoteenn算法_基于EasyEnsemble算法和SMOTE算法的不均衡数据分类方法与流程
本发明涉及不均衡数据二分类技术领域,尤其涉及一种基于EasyEnsemble算法和SMOTE算法的不均衡数据二分类方法. 背景技术: 数据不均衡指的是在一个样本数据集中,某一类的样本数远少于其他类的样 ...
- 基于Huffman算法和LZ77算法的文件压缩的改进方向
基于Huffman算法和LZ77算法的文件压缩(八) 到这里已经简单实现基于Huffman算法和LZ77算法的文件压缩, GitHub源码:点我 根据基于Huffman算法和LZ77算法的文件压缩(七 ...
- 【定时同步系列10】16QAM基带调制+Gardener定时误差检测+解调误码率曲线之MATLAB仿真
引言 重要声明:为防止爬虫和盗版贩卖,文章中的核心代码和数据集可凭[CSDN订阅截图或公z号付费截图]私信免费领取,一律不认其他渠道付费截图! 在前面的博客中 [定时同步系列8]QPSK基带调制+Ga ...
- 【定时同步系列8】QPSK基带调制+Gardener定时误差检测+解调误码率曲线之MATLAB并行仿真姊妹篇一
结果预览 重要声明:为防止爬虫和盗版贩卖,文章中的核心代码和数据集可凭[CSDN订阅截图或公z号付费截图]私信免费领取,一律不认其他渠道付费截图! 误码率曲线 关于反馈型定时同步技术的原理已经陆陆续续 ...
- 【MATLAB教程案例7】基于Gardner环的定时误差检测matlab仿真
FPGA教程目录 MATLAB教程目录 -------------------------------------------------------------------------------- ...
- 【信号分解】基于LMD算法和ELMD算法实现管道泄漏信号处理附matlab代码
1 内容介绍 在科技水平相当发达的今天,互联网+.大数据慢慢渗透进人们的生活当中,但 科技的进步不仅仅要体现在生活质量水平的提高.经济的快速发展,更应该体现在对社会资源的合理利用.自建国以来,我国管道 ...
- 【定时同步系列9】QPSK基带调制+Gardener定时误差检测+解调误码率曲线之MATLAB并行仿真姊妹篇二
重要声明:为防止爬虫和盗版贩卖,文章中的核心代码和数据集可凭[CSDN订阅截图或公z号付费截图]私信免费领取,一律不认其他渠道付费截图! 上一篇主要对Gardener定时算法进行了MATLAB仿真,其 ...
- 分形插值matlab,分形插值算法和MATLAB实验
一,分形插值算法 --分形图的递归算法1,分形的定义 分形(Fractal)一词,是法国人B.B.Mandelbrot 创造出来的,其原意包含了不规则.支离破碎等意思.Mandelbrot 基于对不规 ...
- 基于深度优先算法和A*算法的迷宫游戏开发(Java实现)
先上图 文章目录 一.实验内容 二.深度优先算法生成迷宫 三.A*算法走迷宫 四.结果测试 五.源代码 六.参考文献 一.实验内容 1.要求: 1)迷宫随机生成 2)系统用A*算法寻路,输出路径 3) ...
最新文章
- 一文读懂 CNN、DNN、RNN 内部网络结构区别
- PostgreSQL — 常规操作
- python程序设计报告-《Python程序设计》 实验报告.doc
- 职高计算机教学案例 反思,关于职高数学优质课教学案例的研究与反思
- mysql分区失败_MYSQL表分区操作错误1503解决方案
- hive链接mysql的shell命令_Hive shell 基本命令
- JAVA CGI 远程代码执行_Apache Tomcat CVE-2019-0232 远程代码执行漏洞
- Android 应用开发(第3章)线性布局LinearLayout
- 如何让shell脚本自杀
- vs2010找不到服务器,在服务器上找不到Crystal Reports dll部署VS2010应用程序
- 10本畅销全球的技术经典,这次整个大的
- 68.TextView设置中划线、下划线
- mysql数据库客户端--navicat for mysql 12中文破解版64位/32位 v12.0.29
- redis cluster集群架构详解(十二)- 集群通信
- 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)
- 为何个税突然变多了?“税率跳档”了解一下
- 《跟开涛学SpringMVC》学习笔记
- 透过现象看本质-IT程序员成长及管理
- navicat for mysql 12 破解工具 亲测可用
- idea中提示程序包不存在或cannot resolve symbol