FPGA学习之HDMI接口显示
FPGA学习之HDMI接口显示
- 简介
- 程序代码
- 实验任务
- 实验原理
- 代码部分
- 方块移动实验
参考正点原子视频
简介
HDMI接口英文全称叫High Definition Multimedia Interface,它能够同时传输视频和音频,简化了设备的接口和连线;同时提供了更高的数据传输带宽, 可以传输无压缩的数字音频及高分辨率视频信号 。HDMI 1.0版本于 2002年 发布 最高数据传输速度为 5Gbps 而2017年发布的 HDMI 2.1标准的理论带宽可达 48Gbps。
HDMI向下兼容 DVI,但是 DVI(数字视频接口)只能用来传输视频,而不能 同时传输音频,这是两者最主要的差别 。此外, DVI接口的尺寸明显大于 HDMI接口,如
程序代码
实验任务
通过HDMI接口显示图像,实现图像的发送功能。
实验原理
上图为本次实验的系统框图。如图所示,我们需要实现的部分就只有RGB2DVI的模块即可,其他的和之前的RGB彩条显示实验的代码基本一样(也就是需要把之前VGA接口的时序转换成DVI接口需要的时序,把RGB888格式转化为TMDS数据输出)。
RGB2DVI顶层模块的设计框图如下:
如图,该顶层模块分为encoder和serializer两个子模块,前者用来实现8位/2位到10位的编码转换,后者用来实现这10位数据的并转串(调用OSERDESE2原语,同时使用5倍频,因为本身该原语具有两倍频的功能,实现传输时间上的平衡)。
代码部分
dvi_encoder.v
module dvi_encoder(input clkin, // pixel clock inputinput rstin, // async. reset input (active high)input [7:0] din, // data inputs: expect registeredinput c0, // c0 inputinput c1, // c1 inputinput de, // de inputoutput reg [9:0] dout // data outputs);// Counting number of 1s and 0s for each incoming pixel// component. Pipe line the result.// Register Data Input so it matches the pipe lined adder// outputreg [3:0] n1d; //number of 1s in dinreg [7:0] din_q;//计算像素数据中“1”的个数always @ (posedge clkin) beginn1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];din_q <=#1 din;end///// Stage 1: 8 bit -> 9 bit// Refer to DVI 1.0 Specification, page 29, Figure 3-5///wire decision1;assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0));wire [8:0] q_m;assign q_m[0] = din_q[0];assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);assign q_m[8] = (decision1) ? 1'b0 : 1'b1;/// Stage 2: 9 bit -> 10 bit// Refer to DVI 1.0 Specification, page 29, Figure 3-5/reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_malways @ (posedge clkin) beginn1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);endparameter CTRLTOKEN0 = 10'b1101010100;parameter CTRLTOKEN1 = 10'b0010101011;parameter CTRLTOKEN2 = 10'b0101010100;parameter CTRLTOKEN3 = 10'b1010101011;reg [4:0] cnt; //disparity counter, MSB is the sign bitwire decision2, decision3;assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);/// [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]/assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));// pipe line alignmentreg de_q, de_reg;reg c0_q, c1_q;reg c0_reg, c1_reg;reg [8:0] q_m_reg;always @ (posedge clkin) beginde_q <=#1 de;de_reg <=#1 de_q;c0_q <=#1 c0;c0_reg <=#1 c0_q;c1_q <=#1 c1;c1_reg <=#1 c1_q;q_m_reg <=#1 q_m;end///// 10-bit out// disparity counter///always @ (posedge clkin or posedge rstin) beginif(rstin) begindout <= 10'h0;cnt <= 5'h0;end else beginif (de_reg) beginif(decision2) begindout[9] <=#1 ~q_m_reg[8]; dout[8] <=#1 q_m_reg[8]; dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);end else beginif(decision3) begindout[9] <=#1 1'b1;dout[8] <=#1 q_m_reg[8];dout[7:0] <=#1 ~q_m_reg[7:0];cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);end else begindout[9] <=#1 1'b0;dout[8] <=#1 q_m_reg[8];dout[7:0] <=#1 q_m_reg[7:0];cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);endendend else begincase ({c1_reg, c0_reg})2'b00: dout <=#1 CTRLTOKEN0;2'b01: dout <=#1 CTRLTOKEN1;2'b10: dout <=#1 CTRLTOKEN2;default: dout <=#1 CTRLTOKEN3;endcasecnt <=#1 5'h0;endendendendmodule
具体的编码原理不用了解。总的来说,前八位数据是经过原始八位运算而来,第九位表示运算的方式,1是异或0是异或非,第十位的作用是DC平衡。
接收端在收到信号后,再进行相反的运算。TMDS和LVDS、TTL相比有较好的电磁兼容性能。这种算法可以减小传输信号过程的上冲和下冲,而 DC平衡使信号对传输线的电磁干扰减少,可以用低成本的专用电缆实现长距离、高质量的数字信号传输。
serializer_10_to_1.v
module serializer_10_to_1(input reset, // 复位,高有效input paralell_clk, // 输入并行数据时钟input serial_clk_5x, // 输入串行数据时钟input [9:0] paralell_data, // 输入并行数据output serial_data_out // 输出串行数据);//wire define
wire cascade1; //用于两个OSERDESE2级联的信号
wire cascade2;//*****************************************************
//** main code
//***************************************************** //例化OSERDESE2原语,实现并串转换,Master模式
OSERDESE2 #(.DATA_RATE_OQ ("DDR"), // 设置双倍数据速率.DATA_RATE_TQ ("SDR"), // DDR, BUF, SDR.DATA_WIDTH (10), // 输入的并行数据宽度为10bit.SERDES_MODE ("MASTER"), // 设置为Master,用于10bit宽度扩展.TBYTE_CTL ("FALSE"), // Enable tristate byte operation (FALSE, TRUE).TBYTE_SRC ("FALSE"), // Tristate byte source (FALSE, TRUE).TRISTATE_WIDTH (1) // 3-state converter width (1,4)
)
OSERDESE2_Master (.CLK (serial_clk_5x), // 串行数据时钟,5倍时钟频率.CLKDIV (paralell_clk), // 并行数据时钟.RST (reset), // 1-bit input: Reset.OCE (1'b1), // 1-bit input: Output data clock enable.OQ (serial_data_out), // 串行输出数据.D1 (paralell_data[0]), // D1 - D8: 并行数据输入.D2 (paralell_data[1]),.D3 (paralell_data[2]),.D4 (paralell_data[3]),.D5 (paralell_data[4]),.D6 (paralell_data[5]),.D7 (paralell_data[6]),.D8 (paralell_data[7]),.SHIFTIN1 (cascade1), // SHIFTIN1 用于位宽扩展.SHIFTIN2 (cascade2), // SHIFTIN2.SHIFTOUT1 (), // SHIFTOUT1: 用于位宽扩展.SHIFTOUT2 (), // SHIFTOUT2.OFB (), // 以下是未使用信号.T1 (1'b0), .T2 (1'b0),.T3 (1'b0),.T4 (1'b0),.TBYTEIN (1'b0), .TCE (1'b0), .TBYTEOUT (), .TFB (), .TQ ()
);//例化OSERDESE2原语,实现并串转换,Slave模式
OSERDESE2 #(.DATA_RATE_OQ ("DDR"), // 设置双倍数据速率.DATA_RATE_TQ ("SDR"), // DDR, BUF, SDR.DATA_WIDTH (10), // 输入的并行数据宽度为10bit.SERDES_MODE ("SLAVE"), // 设置为Slave,用于10bit宽度扩展.TBYTE_CTL ("FALSE"), // Enable tristate byte operation (FALSE, TRUE).TBYTE_SRC ("FALSE"), // Tristate byte source (FALSE, TRUE).TRISTATE_WIDTH (1) // 3-state converter width (1,4)
)
OSERDESE2_Slave (.CLK (serial_clk_5x), // 串行数据时钟,5倍时钟频率.CLKDIV (paralell_clk), // 并行数据时钟.RST (reset), // 1-bit input: Reset.OCE (1'b1), // 1-bit input: Output data clock enable.OQ (), // 串行输出数据.D1 (1'b0), // D1 - D8: 并行数据输入.D2 (1'b0),.D3 (paralell_data[8]),.D4 (paralell_data[9]),.D5 (1'b0),.D6 (1'b0),.D7 (1'b0),.D8 (1'b0),.SHIFTIN1 (), // SHIFTIN1 用于位宽扩展.SHIFTIN2 (), // SHIFTIN2.SHIFTOUT1 (cascade1), // SHIFTOUT1: 用于位宽扩展.SHIFTOUT2 (cascade2), // SHIFTOUT2.OFB (), // 以下是未使用信号.T1 (1'b0), .T2 (1'b0),.T3 (1'b0),.T4 (1'b0),.TBYTEIN (1'b0), .TCE (1'b0), .TBYTEOUT (), .TFB (), .TQ ()
); endmodule
该模块就是调用原语来实现的,原语是Xilinx底层硬件中的功能模块,它使用专用的资源来实现一系列的功能。相比于IP核,调用方法更简单,但是一般只用于实现一些简单的功能。
但是一个OSERDESE2最多只能实现8:1的转换率,这里我们使用了位宽拓展。最多能实现14:1,而且扩展模块的D1D2接口不能用。
dvi_transmitter_top.v
module dvi_transmitter_top(input pclk, // pixel clockinput pclk_x5, // pixel clock x5input reset_n, // resetinput [23:0] video_din, // RGB888 video ininput video_hsync, // hsync datainput video_vsync, // vsync datainput video_de, // data enableoutput tmds_clk_p, // TMDS 时钟通道output tmds_clk_n,output [2:0] tmds_data_p, // TMDS 数据通道output [2:0] tmds_data_n,output tmds_oen // TMDS 输出使能);
//wire define
wire reset;//并行数据
wire [9:0] red_10bit;
wire [9:0] green_10bit;
wire [9:0] blue_10bit;
wire [9:0] clk_10bit; //串行数据
wire [2:0] tmds_data_serial;
wire tmds_clk_serial;//*****************************************************
//** main code
//*****************************************************
assign tmds_oen = 1'b1;
assign clk_10bit = 10'b1111100000;//异步复位,同步释放
asyn_rst_syn reset_syn(.reset_n (reset_n),.clk (pclk),.syn_reset (reset) //高有效);//对三个颜色通道进行编码
dvi_encoder encoder_b (.clkin (pclk),.rstin (reset),.din (video_din[7:0]),.c0 (video_hsync),.c1 (video_vsync),.de (video_de),.dout (blue_10bit)) ;dvi_encoder encoder_g (.clkin (pclk),.rstin (reset),.din (video_din[15:8]),.c0 (1'b0),.c1 (1'b0),.de (video_de),.dout (green_10bit)) ;dvi_encoder encoder_r (.clkin (pclk),.rstin (reset),.din (video_din[23:16]),.c0 (1'b0),.c1 (1'b0),.de (video_de),.dout (red_10bit)) ;//对编码后的数据进行并串转换
serializer_10_to_1 serializer_b(.reset (reset), // 复位,高有效.paralell_clk (pclk), // 输入并行数据时钟.serial_clk_5x (pclk_x5), // 输入串行数据时钟.paralell_data (blue_10bit), // 输入并行数据.serial_data_out (tmds_data_serial[0]) // 输出串行数据); serializer_10_to_1 serializer_g(.reset (reset),.paralell_clk (pclk),.serial_clk_5x (pclk_x5),.paralell_data (green_10bit),.serial_data_out (tmds_data_serial[1]));serializer_10_to_1 serializer_r(.reset (reset),.paralell_clk (pclk),.serial_clk_5x (pclk_x5),.paralell_data (red_10bit),.serial_data_out (tmds_data_serial[2]));serializer_10_to_1 serializer_clk(.reset (reset),.paralell_clk (pclk),.serial_clk_5x (pclk_x5),.paralell_data (clk_10bit),.serial_data_out (tmds_clk_serial));//转换差分信号
OBUFDS #(.IOSTANDARD ("TMDS_33") // I/O电平标准为TMDS
) TMDS0 (.I (tmds_data_serial[0]),.O (tmds_data_p[0]),.OB (tmds_data_n[0])
);OBUFDS #(.IOSTANDARD ("TMDS_33") // I/O电平标准为TMDS
) TMDS1 (.I (tmds_data_serial[1]),.O (tmds_data_p[1]),.OB (tmds_data_n[1])
);OBUFDS #(.IOSTANDARD ("TMDS_33") // I/O电平标准为TMDS
) TMDS2 (.I (tmds_data_serial[2]), .O (tmds_data_p[2]), .OB (tmds_data_n[2])
);OBUFDS #(.IOSTANDARD ("TMDS_33") // I/O电平标准为TMDS
) TMDS3 (.I (tmds_clk_serial), .O (tmds_clk_p),.OB (tmds_clk_n)
);endmodule
可以注意到还例化了一个模块:asyn_rst_syn
module asyn_rst_syn(input clk, //目的时钟域input reset_n, //异步复位,低有效output syn_reset //高有效);//reg define
reg reset_1;
reg reset_2;//*****************************************************
//** main code
//*****************************************************
assign syn_reset = reset_2;//对异步复位信号进行同步释放,并转换成高有效
always @ (posedge clk or negedge reset_n) beginif(!reset_n) beginreset_1 <= 1'b1;reset_2 <= 1'b1;endelse beginreset_1 <= 1'b0;reset_2 <= reset_1;end
endendmodule
该模块也是用来将异步复位信号同步到串行时钟域,并且把低电平有效转换成高电平有效。
最后我们在完成顶层模块。
hdmi_colorbar_top.v
module hdmi_colorbar_top(input sys_clk,input sys_rst_n, output tmds_clk_p, // TMDS 时钟通道output tmds_clk_n,output [2:0] tmds_data_p, // TMDS 数据通道output [2:0] tmds_data_n,output tmds_oen // TMDS 输出使能);//wire define
wire pixel_clk;
wire pixel_clk_5x;
wire clk_locked;wire [10:0] pixel_xpos_w;
wire [10:0] pixel_ypos_w;
wire [23:0] pixel_data_w;wire video_hs;
wire video_vs;
wire video_de;
wire [23:0] video_rgb;//*****************************************************
//** main code
//*****************************************************//assign hpdout = hpdin;//例化MMCM/PLL IP核
clk_wiz_0 clk_wiz_0(.clk_in1 (sys_clk),.clk_out1 (pixel_clk), //像素时钟.clk_out2 (pixel_clk_5x), //5倍像素时钟.reset (~sys_rst_n), .locked (clk_locked)
);//例化视频显示驱动模块
video_driver u_video_driver(.pixel_clk (pixel_clk),.sys_rst_n (sys_rst_n),.video_hs (video_hs),.video_vs (video_vs),.video_de (video_de),.video_rgb (video_rgb),.pixel_xpos (pixel_xpos_w),.pixel_ypos (pixel_ypos_w),.pixel_data (pixel_data_w));//例化视频显示模块
video_display u_video_display(.pixel_clk (pixel_clk),.sys_rst_n (sys_rst_n),.pixel_xpos (pixel_xpos_w),.pixel_ypos (pixel_ypos_w),.pixel_data (pixel_data_w));//例化HDMI驱动模块
dvi_transmitter_top u_rgb2dvi_0(.pclk (pixel_clk),.pclk_x5 (pixel_clk_5x),.reset_n (sys_rst_n & clk_locked),.video_din (video_rgb),.video_hsync (video_hs), .video_vsync (video_vs),.video_de (video_de),.tmds_clk_p (tmds_clk_p),.tmds_clk_n (tmds_clk_n),.tmds_data_p (tmds_data_p),.tmds_data_n (tmds_data_n), .tmds_oen (tmds_oen));endmodule
其他代码和lcd彩条显示实验基本相同,于是就完成了彩条显示实验啦。
方块移动实验
实验任务:控制方块的移动。
实验原理:方块的移动实际上是方块某个点的移动,我们选择左上角的顶点,这样就可以看作在不同的位置绘制一个方块。同时需要一个信号来控制方块移动的速度,具体的代码如下:
video_display.v
module rgb_display(input pixel_clk, //VGA驱动时钟input sys_rst_n, //复位信号input [10:0] pixel_xpos, //像素点横坐标input [10:0] pixel_ypos, //像素点纵坐标 output reg [23:0] pixel_data //像素点数据); //parameter define
parameter H_DISP = 11'd1280; //分辨率--行
parameter V_DISP = 11'd720; //分辨率--列localparam SIDE_W = 11'd40; //屏幕边框宽度
localparam BLOCK_W = 11'd40; //方块宽度
localparam BLUE = 24'b00000000_00000000_11111111; //屏幕边框颜色 蓝色
localparam WHITE = 24'b11111111_11111111_11111111; //背景颜色 白色
localparam BLACK = 24'b00000000_00000000_00000000; //方块颜色 黑色//reg define
reg [10:0] block_x = SIDE_W ; //方块左上角横坐标
reg [10:0] block_y = SIDE_W ; //方块左上角纵坐标
reg [21:0] div_cnt; //时钟分频计数器
reg h_direct; //方块水平移动方向,1:右移,0:左移
reg v_direct; //方块竖直移动方向,1:向下,0:向上//wire define
wire move_en; //方块移动使能信号,频率为100hz//*****************************************************
//** main code
//*****************************************************
assign move_en = (div_cnt == 22'd742500) ? 1'b1 : 1'b0;//通过对vga驱动时钟计数,实现时钟分频
always @(posedge pixel_clk ) begin if (!sys_rst_n)div_cnt <= 22'd0;else beginif(div_cnt < 22'd742500) div_cnt <= div_cnt + 1'b1;elsediv_cnt <= 22'd0; //计数达10ms后清零end
end//当方块移动到边界时,改变移动方向
always @(posedge pixel_clk ) begin if (!sys_rst_n) beginh_direct <= 1'b1; //方块初始水平向右移动v_direct <= 1'b1; //方块初始竖直向下移动endelse beginif(block_x == SIDE_W - 1'b1) //到达左边界时,水平向右h_direct <= 1'b1; else //到达右边界时,水平向左if(block_x == H_DISP - SIDE_W - BLOCK_W)h_direct <= 1'b0; elseh_direct <= h_direct;if(block_y == SIDE_W - 1'b1) //到达上边界时,竖直向下v_direct <= 1'b1; else //到达下边界时,竖直向上if(block_y == V_DISP - SIDE_W - BLOCK_W)v_direct <= 1'b0; elsev_direct <= v_direct;end
end//根据方块移动方向,改变其纵横坐标
always @(posedge pixel_clk ) begin if (!sys_rst_n) beginblock_x <= SIDE_W; //方块初始位置横坐标block_y <= SIDE_W; //方块初始位置纵坐标endelse if(move_en) beginif(h_direct) block_x <= block_x + 1'b1; //方块向右移动elseblock_x <= block_x - 1'b1; //方块向左移动if(v_direct) block_y <= block_y + 1'b1; //方块向下移动elseblock_y <= block_y - 1'b1; //方块向上移动endelse beginblock_x <= block_x;block_y <= block_y;end
end//给不同的区域绘制不同的颜色
always @(posedge pixel_clk ) begin if (!sys_rst_n) pixel_data <= BLACK;else beginif( (pixel_xpos < SIDE_W) || (pixel_xpos >= H_DISP - SIDE_W)|| (pixel_ypos < SIDE_W) || (pixel_ypos >= V_DISP - SIDE_W))pixel_data <= BLUE; //绘制屏幕边框为蓝色elseif( (pixel_xpos >= block_x) && (pixel_xpos < block_x + BLOCK_W)&& (pixel_ypos >= block_y) && (pixel_ypos < block_y + BLOCK_W))pixel_data <= BLACK; //绘制方块为黑色elsepixel_data <= WHITE; //绘制背景为白色end
endendmodule
其中move_en用来控制方块移动的速度。方块的大小和颜色也是可控的。
其他和显示彩条的代码一样。
FPGA学习之HDMI接口显示相关推荐
- FPGA学习--RGB-LCD屏彩条显示实验
FPGA学习--RGB-LCD屏彩条显示实验 RGB-LCD屏原理 程序设计 参考正点原子视频 RGB-LCD屏原理 LCD 是一种液晶显示屏,它采用薄膜晶体管(TFT)技术提升图像质量,如提高图像亮 ...
- FPGA学习之路—接口(3)—SPI详解及Verilog源码分析
FPGA学习之路--SPI详解及Verilog源码分析 概述 SPI = Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线. 优点 支持全 ...
- FPGA学习之路—接口(1)—URAT Verilog程序设计
FPGA学习之路--URAT Verilog程序设计 UART(Universal Asynchronous Receiver Transmitter,通用异步收发器)是广泛使用的异步串行数据通信协议 ...
- FPGA学习之路—接口(2)—I2C协议详解+Verilog源码分析
FPGA学习之路--I2C协议详解+Verilog源码分析 定义 I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半导体(现被NXP收购)开发的两线时 ...
- 【正点原子FPGA连载】第二十四章HDMI彩条显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1
1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...
- 【正点原子FPGA连载】第二十一章 HDMI彩条显示实验 -摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0
1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...
- 【正点原子FPGA连载】 第十七章 HDMI彩条显示实验摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670 3)全套实验源码+手册+视频下载地址: h ...
- FPGA实现HDMI接口
作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 参考 用FPGA实现HDMI ...
- FPGA学习-VGA接口
FPGA学习-VGA接口 一般FPGA开发板的VGA会向用户暴露两共五个种接口,第一种是时序信号,用于同步传输和显示:第二种是色彩信号,用于随着时序把色彩显示到显示器上 时序接口 行同步信号-用于指示 ...
最新文章
- vue代码转换成小程序
- Tech.ED 2009前瞻:认识System Center
- ubuntu装机必备
- java中的轮子是什么意思_后端的轮子(一) - java后端开发的个人空间 - OSCHINA - 中文开源技术交流社区...
- Java 包(package)
- 拷贝Python对象、浅拷贝、深拷贝
- docker 安装nginx_使用 Docker 在你的 mac 上搭建个服务器
- a标签去掉下划线_html中a标签的一些用法
- [BZOJ] 1025 [SCOI2009]游戏
- Django缓存设置
- 程序员面试金典——1.1确定字符互异
- careercup-链表 2.7
- asp.net中commandname应用
- 正则表达式语法及例子
- wincc系统冗余服务器系统专用授权,wincc冗余问题
- Redis的读写分离
- 一分钟教会你烫了头发该怎么打理
- 苹果支付验单java
- 蓝桥杯 Beaver's Calculator
- test题目:袋鼠过河