前言

第一次使用FPGA实现一个算法,搓手手,于是我拿出一股势在必得的心情打开了FFT的视频教程,看了好几个视频和好些篇博客,于是我迷失在数学公式推导中,在一位前辈的建议下,我开始转换我的思维,从科研心态转变为先用起来,于是我关掉我的推导笔记,找了一篇叫我用Verilog写FFT的视频B站 - 使用Verilog写FFT,跟着他先让代码跑起来,然后再择需深入


使用软件:vivado
实现算法:N=8的FFT算法
大框架:使用并行的3级流水线

正文

以下内容以快速让FFT代码跑起来为出发点,所以不会有复杂的理论推导,如果想要深入研究,可参考网上的详细教程,以下我会介绍我实现的过程,如果下面内容有误,请一定帮我指出

一、如何用FPGA实现FFT

在这里我们先直接抛出在FPGA里面是如何实现FFT的,然后再逐次推进涉及到的内容

1.1 实现FFT的核心

核心就是用Verilog代码写出下面的这幅图
可能你和我一样一开始不知道 怎么下手,连这个图都看不懂,没关系!!我们一步步来

有了目标,就围绕着我们的目标进行知识补充,(这样以目标为导向,不至于迷失在数学公式推导中)
首先我们要知道这个图是个啥,推荐看这个老师的视频,视频时长很短,只需要十多分钟就能对这幅图有个初步的认识
推荐视频:B站-潘老师-数字信号处理

需要明确的地方:

  1. 上面的图叫:蝶形图
  2. WN0=1W_{N}^{0}=1WN0​=1
  3. 最左侧是时域,最右侧是 频域

以下是我看了视频后做的笔记:


这个口诀可以等你看完视频和我下面的笔记后,用来作为帮助记忆的辅助材料
口诀
箭尾出发,箭头停
箭身有值要乘上
每次走完2支箭
箭身长的写在前


首先最左侧的 x(0),...,x(7)x(0),...,x(7)x(0),...,x(7) 从箭尾出发,箭身上有值的就和上面的值相乘,每次只能走完2个箭就要停下来计算一次值,并且从斜着的箭过来的值写在计算表达式的第一位,直着过来的值写在计算表达式的第二位
先挖个坑,等有空录一个简单的视频说一说这个蝶形图

1.2 蝶形图的组成元素

蝶形图无非就是一些元素构成的:左右两边的 $x$ , $W_{N}^{0}$ , $W_{N}^{1}$ , $W_{N}^{2}$ , $W_{N}^{3}$, -1,还有一些箭头,以及图下面图例中 $N=8$
只要我们知道这些元素是啥,用来干什么就能大概看懂蝶形图了

1.2.1 旋转因子 WNW_{N}WN​

快速傅里叶变换(FFT)是对离散傅里叶变换(DFT)的一种加速算法,FFT比DFT运算速度快的原因,就是这个旋转因子的功劳。
旋转因子的表达式如下:

旋转因子有一些比较好的性质:周期性、可约性、对称性,个人认为如果不做公式推导,那就知道它的这些性质即可
在下面的代码中,第二级流水线里的例化复数乘法IP核时,我们直接将旋转因子给出(如下的倒数第三行

// 复数乘法的IP核,求解与旋转因子的乘积
cmpy_0 cmpy23(.aclk(clk),.s_axis_a_tvalid(fft1_en),.s_axis_a_tdata({4'd0, fft1_im3,1'd0,4'd0, fft1_re3,1'd0}),//乘法元素中的复数:既有实部又有虚部.s_axis_b_tvalid(1'b1),.s_axis_b_tdata({8'd0,8'b10110101,8'd0,8'b10110101}),// 旋转因子.m_axis_dout_tvalid(fft2_en1),.m_axis_dout_tdata(fft2_cmpy23));

1.2.2 输入数据倒序排列

可能有细心的朋友会发现,蝶形图左边的 xxx 的排序不是按照升序或降序拍的,而是将 0−70-70−7 的二进制写出来后,将二进制的高位、低位互换后得到的

1.2.3 N是什么

目前蝶形图中的元素只剩下这个图例中的 N=8N=8N=8 了,这里的 NNN 表示每一列的点数,虽然蝶形图中有很多点,但是每一列都只有8个点,log2Nlog_{2}Nlog2​N= 每组的蝶形次数,这里 N=8N=8N=8,每组就要做4次蝶形

我在看教程时,还会看到基-2、基-4这样的名词,NNN 还可以用来区分这两个名词(虽然我不知道区分这俩有啥用)

1.3 Verilog编写蝶形图

从图中可以看到,处理N=8这样的蝶形图,分3步走,即可以使用3级流水线来实现

1.3.1 第一级流水线

always @(posedge clk or negedge rst_n)beginif(!rst_n)beginfft1_en  <= 0;fft1_re0 <= 0;fft1_im0 <= 0;fft1_re1 <= 0;fft1_im1 <= 0;fft1_re2 <= 0;fft1_im2 <= 0;fft1_re3 <= 0;fft1_im3 <= 0;fft1_re4 <= 0;fft1_im4 <= 0;fft1_re5 <= 0;fft1_im5 <= 0;fft1_re6 <= 0;fft1_im6 <= 0;fft1_re7 <= 0;fft1_im7 <= 0;endelse if(data_in_en)begin// 实现第一级流水线输出fft1_en  <= 1;fft1_re0 <= data_in_re0 + data_in_re4;fft1_im0 <= data_in_im0 + data_in_im4;fft1_re1 <= data_in_re0 - data_in_re4;fft1_im1 <= data_in_im0 - data_in_im4;fft1_re2 <= data_in_re2 + data_in_re6;fft1_im2 <= data_in_im2 + data_in_im6;fft1_re3 <= data_in_re2 - data_in_re6;fft1_im3 <= data_in_im2 - data_in_im6;fft1_re4 <= data_in_re1 + data_in_re5;fft1_im4 <= data_in_im1 + data_in_im5;fft1_re5 <= data_in_re1 - data_in_re5;fft1_im5 <= data_in_im1 - data_in_im5;fft1_re6 <= data_in_re3 + data_in_re7;fft1_im6 <= data_in_im3 + data_in_im7;fft1_re7 <= data_in_re3 - data_in_re7;fft1_im7 <= data_in_im3 - data_in_im7;endelse beginfft1_en <= 0;       endend

1.3.2 第二级流水线

// 第二级流水线
wire fft2_en1;
wire signed [10:0] fft2_im3_wn;
wire signed [10:0] fft2_re3_wn;
wire signed [47:0] fft2_cmpy23;assign fft2_re3_wn = fft2_cmpy23[19:9]; //从48位中提取出实部
assign fft2_im3_wn = fft2_cmpy23[43:33];//从48位中提取出虚部// 复数乘法的IP核,求解与旋转因子的乘积
cmpy_0 cmpy23(.aclk(clk),.s_axis_a_tvalid(fft1_en),.s_axis_a_tdata({4'd0, fft1_im3,1'd0,4'd0, fft1_re3,1'd0}),//乘法元素中的复数:既有实部又有虚部.s_axis_b_tvalid(1'b1),.s_axis_b_tdata({8'd0,8'b10110101,8'd0,8'b10110101}),// 旋转因子.m_axis_dout_tvalid(fft2_en1),.m_axis_dout_tdata(fft2_cmpy23));wire fft2_en2;
wire signed [10:0] fft2_im7_wn;
wire signed [10:0] fft2_re7_wn;
wire signed [47:0] fft2_cmpy27;
assign fft2_re7_wn =fft2_cmpy27[19:9];
assign fft2_im7_wn =fft2_cmpy27[43:33];cmpy_0 cmpy27(.aclk(clk),.s_axis_a_tvalid(fft1_en),.s_axis_a_tdata({4'd0, fft1_im7,1'd0,4'd0, fft1_re7,1'd0}),.s_axis_b_tvalid(1'b1),.s_axis_b_tdata({8'd0,8'b10110101,8'd0,8'b10110101}),.m_axis_dout_tvalid(fft2_en2),.m_axis_dout_tdata(fft2_cmpy27));reg fft2_en;
reg signed [11:0] fft2_re0;
reg signed [11:0] fft2_im0;
reg signed [11:0] fft2_re1;
reg signed [11:0] fft2_im1;
reg signed [11:0] fft2_re2;
reg signed [11:0] fft2_im2;
reg signed [11:0] fft2_re3;
reg signed [11:0] fft2_im3;
reg signed [11:0] fft2_re4;
reg signed [11:0] fft2_im4;
reg signed [11:0] fft2_re5;
reg signed [11:0] fft2_im5;
reg signed [11:0] fft2_re6;
reg signed [11:0] fft2_im6;
reg signed [11:0] fft2_re7;
reg signed [11:0] fft2_im7;always@(posedge clk or negedge rst_n) beginif(!rst_n)beginfft2_en  <= 0;fft2_re0 <= 0;fft2_im0 <= 0;fft2_re1 <= 0;fft2_im1 <= 0;fft2_re2 <= 0;fft2_im2 <= 0;fft2_re3 <= 0;fft2_im3 <= 0;fft2_re4 <= 0;fft2_im4 <= 0;fft2_re5 <= 0;fft2_im5 <= 0;fft2_re6 <= 0;fft2_im6 <= 0;fft2_re7 <= 0;fft2_im7 <= 0;endelse if(fft2_en2 && fft2_en1)begin// 实现第二级流水线输出fft2_en  <= 1;fft2_re0 <= fft1_re0 + fft1_re2;fft2_im0 <= fft1_im0 + fft1_im2;fft2_re2 <= fft1_re0 - fft1_re2;fft2_im2 <= fft1_im0 - fft1_im2;fft2_re1 <= fft1_re1 + fft2_re3_wn;fft2_im1 <= fft1_im1 + fft2_im3_wn;fft2_re3 <= fft1_re1 - fft2_re3_wn;fft2_im3 <= fft1_im1 - fft2_im3_wn;fft2_re4 <= fft1_re4 + fft1_re6;fft2_im4 <= fft1_im4 + fft1_im6;fft2_re6 <= fft1_re4 - fft1_re6;fft2_im6 <= fft1_im4 - fft1_im6;fft2_re5 <= fft1_re5 + fft2_re7_wn;fft2_im5 <= fft1_im5 + fft2_im7_wn;fft2_re7 <= fft1_re5 - fft2_re7_wn;fft2_im7 <= fft1_im5 - fft2_im7_wn;endelsefft2_en <= 0;
end

1.3.3 第三级流水线

// 第三级流水线
wire fft3_en1;
wire signed [11:0] fft3_im5_wn;
wire signed [11:0] fft3_re5_wn;
wire signed [47:0] fft3_cmpy35;
assign fft3_re5_wn = fft3_cmpy35[19:8];
assign fft3_im5_wn = fft3_cmpy35[43:32];cmpy_0 cmpy35(.aclk(clk),.s_axis_a_tvalid(fft2_en),.s_axis_a_tdata({4'd0, fft2_im5, 4'd0, fft2_re5}),.s_axis_b_tvalid(1'b1),.s_axis_b_tdata({4'd0,12'b0110_0001_1111,4'd0,12'b1110_1100_1000}),.m_axis_dout_tvalid(fft3_en1),.m_axis_dout_tdata(fft3_cmpy35));wire fft3_en2;
wire signed [11:0] fft3_im6_wn;
wire signed [11:0] fft3_re6_wn;
wire signed [47:0] fft3_cmpy36;
assign fft3_re6_wn = fft3_cmpy36[19:8];
assign fft3_im6_wn = fft3_cmpy36[43:32];cmpy_0 cmpy36(.aclk(clk),.s_axis_a_tvalid(fft2_en),.s_axis_a_tdata({4'd0, fft2_im6,4'd0, fft2_re6}),.s_axis_b_tvalid(1'b1),.s_axis_b_tdata({4'd0,12'b1011_0101_0000,4'd0,12'b1011_0101_0000}),.m_axis_dout_tvalid(fft3_en2),.m_axis_dout_tdata(fft3_cmpy36));wire fft3_en3;
wire signed [11:0] fft3_im7_wn;
wire signed [11:0] fft3_re7_wn;
wire signed [47:0] fft3_cmpy37;
assign fft3_re7_wn =fft3_cmpy37[19:8];
assign fft3_im7_wn =fft3_cmpy37[43:32];cmpy_0 cmpy37(.aclk(clk),.s_axis_a_tvalid(fft2_en),.s_axis_a_tdata({4'd0, fft2_im7,4'd0, fft2_re7}),.s_axis_b_tvalid(1'b1),.s_axis_b_tdata({4'd0,12'b1110_1100_1000,4'd0,12'b0110_0001_1111}),.m_axis_dout_tvalid(fft3_en3),.m_axis_dout_tdata(fft3_cmpy37));reg fft3_en;
reg signed [12:0] fft3_re0;
reg signed [12:0] fft3_im0;
reg signed [12:0] fft3_re1;
reg signed [12:0] fft3_im1;
reg signed [12:0] fft3_re2;
reg signed [12:0] fft3_im2;
reg signed [12:0] fft3_re3;
reg signed [12:0] fft3_im3;
reg signed [12:0] fft3_re4;
reg signed [12:0] fft3_im4;
reg signed [12:0] fft3_re5;
reg signed [12:0] fft3_im5;
reg signed [12:0] fft3_re6;
reg signed [12:0] fft3_im6;
reg signed [12:0] fft3_re7;
reg signed [12:0] fft3_im7;always@(posedge clk or negedge rst_n) begin
if(!rst_n)
beginfft3_en  <= 0;fft3_re0 <= 0;fft3_im0 <= 0;fft3_re1 <= 0;fft3_im1 <= 0;fft3_re2 <= 0;fft3_im2 <= 0;fft3_re3 <= 0;fft3_im3 <= 0;fft3_re4 <= 0;fft3_im4 <= 0;fft3_re5 <= 0;fft3_im5 <= 0;fft3_re6 <= 0;fft3_im6 <= 0;fft3_re7 <= 0;fft3_im7 <= 0;
end
else if(fft3_en1 && fft3_en2 && fft3_en3)
begin// 实现第三级流水线输出fft3_en  <=1'b1;fft3_re0 <=fft2_re0 + fft2_re4;fft3_im0 <=fft2_im0 + fft2_im4;fft3_re4 <=fft2_re0 - fft2_re4;fft3_im4 <=fft2_im0 - fft2_im4;fft3_re1 <=fft2_re1 + fft3_re5_wn;fft3_im1 <=fft2_im1 + fft3_im5_wn;fft3_re5 <=fft2_re1 - fft3_re5_wn;fft3_im5 <=fft2_im1 - fft3_im5_wn;fft3_re2 <=fft2_re2 + fft3_re6_wn;fft3_im2 <=fft2_im2 + fft3_im6_wn;fft3_re6 <=fft2_re2 - fft3_re6_wn;fft3_im6 <=fft2_im2 - fft3_im6_wn;fft3_re3 <= fft2_re3 + fft3_re7_wn;fft3_im3 <= fft2_im3 + fft3_im7_wn;fft3_re7 <= fft3_re3 - fft3_re7_wn;fft3_im7 <= fft3_im3 - fft3_im7_wn;
end
elsefft3_en <= 0;
end

1.3.4 模块输出

assign data_out_en  = fft3_en;
assign data_out_re0 = fft3_re0;
assign data_out_im0 = fft3_im0;
assign data_out_re1 = fft3_re1;
assign data_out_im1 = fft3_im1;
assign data_out_re2 = fft3_re2;
assign data_out_im2 = fft3_im2;
assign data_out_re3 = fft3_re3;
assign data_out_im3 = fft3_im3;
assign data_out_re4 = fft3_re4;
assign data_out_im4 = fft3_im4;
assign data_out_re5 = fft3_re5;
assign data_out_im5 = fft3_im5;
assign data_out_re6 = fft3_re6;
assign data_out_im6 = fft3_im6;
assign data_out_re7 = fft3_re7;
assign data_out_im7 = fft3_im7;

1.4 复数乘法器的ip核

在第二级、第三级流水线中,需要和旋转因子做乘法,于是调用vivado中的复数乘法器IP核(complex multiplier

找到后,双击图中的③,就会得到如下图:

1.5 测试文件

然后将测试文件加入到工程中即可

`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/06/29 21:15:21
// Design Name:
// Module Name: tb_FFT
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module tb_FFT();
reg  clk;
reg  rst_n;
reg  data_in_en;
wire data_out_en;
reg [9:0] data_in_re0;
reg [9:0] data_in_im0;
reg [9:0] data_in_re1;
reg [9:0] data_in_im1;
reg [9:0] data_in_re2;
reg [9:0] data_in_im2;
reg [9:0] data_in_re3;
reg [9:0] data_in_im3;
reg [9:0] data_in_re4;
reg [9:0] data_in_im4;
reg [9:0] data_in_re5;
reg [9:0] data_in_im5;
reg [9:0] data_in_re6;
reg [9:0] data_in_im6;
reg [9:0] data_in_re7;
reg [9:0] data_in_im7;wire [12:0] data_out_re0;
wire [12:0] data_out_im0;
wire [12:0] data_out_re1;
wire [12:0] data_out_im1;
wire [12:0] data_out_re2;
wire [12:0] data_out_im2;
wire [12:0] data_out_re3;
wire [12:0] data_out_im3;
wire [12:0] data_out_re4;
wire [12:0] data_out_im4;
wire [12:0] data_out_re5;
wire [12:0] data_out_im5;
wire [12:0] data_out_re6;
wire [12:0] data_out_im6;
wire [12:0] data_out_re7;
wire [12:0] data_out_im7;initial clk = 0;
always#5 clk = ~clk;
initial
beginrst_n = 0;#10rst_n = 1;data_in_en  = 1;data_in_re0 = 10'b0010110011;//0.7data_in_im0 = 10'b0000000000;data_in_re1 = 10'b0000000000;//0data_in_im1 = 10'b0000000000;data_in_re2 = 10'b0010000000;//0.5data_in_im2 = 10'b0000000000;data_in_re3 = 10'b0000000000;//0data_in_im3 = 10'b0000000000;data_in_re4 = 10'b0100000000;//1Idata_in_im4 = 10'b0000000000;data_in_re5 = 10'b0000000000;//0data_in_im5 = 10'b0000000000;data_in_re6 = 10'b0000000000;//0data_in_im6 = 10'b0000000000;data_in_re7 = 10'b0000000000;//0data_in_im7 = 10'b0000000000;
endFFT  FFT_inst(. clk(C1k),. rst_n(rst_n),. data_in_en(data_in_en),. data_in_re0(data_in_re0),. data_in_im0(data_in_im0),. data_in_re1(data_in_re1),. data_in_im1(data_in_im1),. data_in_re2(data_in_re2),. data_in_im2(data_in_im2),. data_in_re3(data_in_re3),. data_in_im3(data_in_im3),. data_in_re4(data_in_re4),. data_in_im4(data_in_im4),. data_in_re5(data_in_re5),. data_in_im5(data_in_im5),. data_in_re6(data_in_re6),. data_in_im6(data_in_im6),. data_in_re7(data_in_re7),. data_in_im7(data_in_im7),. data_out_en(data_out_en),. data_out_re0(data_out_re0),. data_out_im0(data_out_im0),. data_out_re1(data_out_re1),. data_out_im1(data_out_im1),. data_out_re2(data_out_re2),. data_out_im2(data_out_im2),. data_out_re3(data_out_re3),. data_out_im3(data_out_im3),. data_out_re4(data_out_re4),. data_out_im4(data_out_im4),. data_out_re5(data_out_re5),. data_out_im5(data_out_im5),. data_out_re6(data_out_re6),. data_out_im6(data_out_im6),. data_out_re7(data_out_re7),. data_out_im7(data_out_im7));endmodule

二、FFT在FPGA工程中的应用

后面遇到再补充

三、推荐阅读

  1. 文档资料:
    FFT详细介绍教程

  2. B站视频:
    B站 - 使用Verilog写FFT
    B站-潘老师-数字信号处理
    B站-FFT公式推导

四、补充

哈哈哈如果有盆友需要工程可以扫下面的马,然后回复:FFT(拖延症的我,等后台收到第一条FFT我再去弄自动回复)
这个公众号是一年前弄好的,现在终于不闲置啦,有了它的用武之地

FPGA:实现快速傅里叶变换(FFT)算法相关推荐

  1. OpenCV快速傅里叶变换(FFT)用于图像和视讯流的模糊检测

    OpenCV快速傅里叶变换(FFT)用于图像和视频流的模糊检测 翻译自[OpenCV Fast Fourier Transform (FFT) for blur detection in images ...

  2. 基于FPGA的快速傅里叶变换加速(三)

    基于FPGA的快速傅里叶变换加速(三) 硬件加速介绍及部分verilog代码实现 1. 硬件加速 1.1 FPGA 1.1.1 FPGA介绍 概念: 基本结构: 工作原理: 1.1.2 开发板 开发板 ...

  3. 基于python的快速傅里叶变换FFT(二)

    基于python的快速傅里叶变换FFT(二) 本文在上一篇博客的基础上进一步探究正弦函数及其FFT变换. 知识点   FFT变换,其实就是快速离散傅里叶变换,傅立叶变换是数字信号处理领域一种很重要的算 ...

  4. 基于python的快速傅里叶变换FFT(一)

    基于python的快速傅里叶变换FFT(一) FFT可以将一个信号变换到频域.有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易看出特征了.这就是很多信号分析采用FFT变换的原因. ...

  5. Matlab如何进行利用离散傅里叶变换DFT (快速傅里叶变换FFT)进行频谱分析

    文章目录 1. 定义 2. 变换和处理 3. 函数 4. 实例演示 例1:单频正弦信号(整数周期采样) 例2:单频正弦信号(非整数周期采样) 例3:含有直流分量的单频正弦信号 例4:正弦复合信号 例5 ...

  6. 快速傅里叶变换FFT进行频谱分析(matlab)

    快速傅里叶变换FFT进行频谱分析(matlab) 本章摘要:FFT是离散傅立叶变换的快速算法,可以将一个信号变换到频域.有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易看出特征了 ...

  7. Java编程实现快速傅里叶变换FFT

    快速傅里叶变换的时间复杂度分析 1 快速傅里叶变换FFT 1.1 理论分析 1.1.1 离散傅里叶变换 1.1.2 快速傅里叶变换 1.2 编程实现 1.2.1 算法思想 1.2.2 实验结果 1 快 ...

  8. 快速傅里叶变换FFT C语言实现 可用于嵌入式系统进行模拟采样频谱分析

    快速傅里叶变换C语言实现 模拟采样进行频谱分析 FFT是DFT的快速算法用于分析确定信号(时间连续可积信号.不一定是周期信号)的频率(或相位.此处不研究相位)成分,且傅里叶变换对应的 ω \omega ...

  9. 快速傅里叶变换python_基于python的快速傅里叶变换FFT(二)

    基于python的快速傅里叶变换FFT(二) 本文在上一篇博客的基础上进一步探究正弦函数及其FFT变换. 知识点 FFT变换,其实就是快速离散傅里叶变换,傅立叶变换是数字信号处理领域一种很重要的算法. ...

  10. 离散傅里叶变换 (DFT)、快速傅里叶变换 (FFT)

    目录 离散傅里叶变换 (DFT) 离散傅里叶变换的基 离散傅里叶变换 快速傅里叶变换 (FFT) 卷积 线性时不变系统 傅里叶级数 参考文献 离散傅里叶变换 (DFT) 离散傅里叶变换的基 对于周期为 ...

最新文章

  1. python 字典中的value 不在字典中,key才在
  2. Project 2007如何打开项目向导
  3. 阿里云云原生网关,开启下一代网关新进程
  4. UML序列图总结(Loop、Opt、Par和Alt)
  5. [Wf2011]Chips Challenge(最小费用最大流)
  6. typescript_如何掌握高级TypeScript模式
  7. 电大计算机网络网考,电大计算机网络(本)学习周期01任务A_0009答案
  8. make、make clean、make uninstall的使用
  9. PostgreSQL 10.1 手册_部分 III. 服务器管理_第 26 章 高可用、负载均衡和复制_26.4. 日志传送的替代方法...
  10. jvisualvm (Java VisualVM)
  11. Mysql数据库恢复到指定时间点
  12. mysql字段动态扩展_数据库动态扩展字段
  13. java根据word模板导出word文件
  14. maker win10有movie_手把手解决win10系统出现windows MovieMaker故障的方法
  15. win7计算机组策略打不开,三个步骤解决win7系统本地组策略打不开的问题
  16. “鸿蒙之父”王成录重申:鸿蒙系统不是安卓套壳;苹果多款产品电池修理费将涨价;Debian移除Python 2|极客头条...
  17. HTML图片打开新窗口
  18. [转载]海康摄像头_2
  19. c# 扫描局域网IP列表的几种方法
  20. 把list集合转换为JSON

热门文章

  1. Vue中相同逻辑如何抽离?
  2. 《摩根写给儿子的32封信》 03 企业家的资质
  3. 计算机主板芯片组型号有哪些,如何鉴别主板芯片组型号
  4. 2021多校补题(8)
  5. 云计算时代,NGINX将是你的“必杀技”
  6. 上任第十年,库克功与过
  7. 对于时间管理初识--时间管理入门
  8. python 词表里的词不符合_收藏干货丨初中英语单词1600个词表+mp3下载
  9. 网页游戏对java的技术要求_网页制作谈谈什么技术是Java开发网页游戏的必要条件呢?怎样在微信公众平台上制作5级游戏?...
  10. 【Hyperledger Fabric】学习笔记2——超级账本介绍