一、HDMI接口的简要介绍
最先接触到的时VGA那么两者有什么区别呢?主要区别如下:
1、HDMI接口:是数字信号接口,可传输音频和视频,硬件接口较小,支持热插拔。
2、VGA接口:是模拟信号接口,只可传输视频流数据,硬件接口较大,虽说不支持热插拔,但是也没什么问题,损坏显卡而已。
HDMI接口就是在VGA接口的基础上发展而来的,至于HDMI接口为什么可以做到那么小,主要是数据与VGA不同,是串行传输的。HDMI的实现方法有两种,一种是使用HDMI芯片,另一种因为HDMI协议本身就是数字协议,所以我们来使用IO模拟,本次实验使用的是第二种方法。
二、数据手册中给出的HDMI协议简介:
1、从西面的结构图看一看出,一个HDMI包括三个TMDS数据通道,一个TMDS时钟通道。TMDS是中通道以某种低唱的速率运行。该速率和视屏的图像速率成比例。在每个TMDS通道中每一个都发送10bit 的数据。这个10位的字被编码。采用8bit---10bit 的数据编码方式。
2、输入到信源端的数据;视屏像素、数据包、以及控制数据。数据包包括音频数据和辅助以及相关的纠错码。
3、我们现在只是用到了图传数据,主要包括:chanel0的像素通道和行场时序;chanel1的像素通道;chanel2的像素通道。
用到的资源所组成的框图:当然这只是大概框图。
三、分模块实现
1、VGA驱动模块,这个模块都不模式,直接上框图:
注意:o_de 信号是指当静茹有效显示区域是,此信号拉高。是为了后面的编码数据坐标值使用,当此信号拉高是开始,进行编码转换。实现的代码如下:vga_drive.v
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 `timescale 1ns / 1ps 2 ////////////////////////////////////////////////////////////////////////////////// 3 // Company: 4 // Engineer: 5 // 6 // Create Date: 2020/06/26 16:37:42 7 // Design Name: 8 // Module Name: vga_drive 9 // Project Name: 10 // Target Devices: 11 // Tool Versions: 12 // Description: 13 // 14 // Dependencies: 15 // 16 // Revision: 17 // Revision 0.01 - File Created 18 // Additional Comments: 19 // 20 ////////////////////////////////////////////////////////////////////////////////// 21 22 module vga_drive ( 23 //system signals 24 input wire sclk , 25 input wire s_rst_n , 26 //vga interfaces 27 output reg [7:0] vga_r , 28 output reg [7:0] vga_g , 29 output reg [7:0] vga_b , 30 output wire o_hs , 31 output wire o_vs , 32 output reg o_de 33 34 35 ); 36 37 //========================================================================\ 38 // =========== Define Parameter and Internal signals =========== 39 //========================================================================/ 40 41 //480 X 640 @ 60HZ 42 43 parameter H_total_time = 800 ; 44 parameter H_sync_time = 96 ; 45 parameter H_back_porch = 40 ; 46 parameter H_left_border = 8 ; 47 parameter H_addr_time = 640 ; 48 49 parameter V_total_time = 525 ; 50 parameter V_sync_time = 2 ; 51 parameter V_back_porch = 25 ; 52 parameter V_top_border = 8 ; 53 parameter V_addr_time = 480 ; 54 55 56 reg [9:0] vga_h_cnt ; 57 reg [9:0] vga_v_cnt ; 58 59 60 //============================================================================= 61 //**************************** Main Code ******************************* 62 //============================================================================= 63 //vga_h_cnt 64 always @ (posedge sclk or negedge s_rst_n) begin 65 if(s_rst_n == 1'b0) 66 vga_h_cnt <= 'd0 ; 67 else if(vga_h_cnt == (H_total_time-1)) 68 vga_h_cnt <= 10'd0 ; 69 else 70 vga_h_cnt <= vga_h_cnt + 1'b1 ; 71 end 72 73 assign o_hs = (vga_h_cnt < H_sync_time ) ? 1'b1: 1'b0 ; 74 //vga_v_cnt 75 always @ (posedge sclk or negedge s_rst_n) begin 76 if(s_rst_n == 1'b0) 77 vga_v_cnt <= 10'd0 ; 78 else if(vga_v_cnt == (V_total_time-1)&&(H_total_time-1)) 79 vga_v_cnt <= 10'd0 ; 80 else if(vga_h_cnt == (H_total_time-1)) 81 vga_v_cnt <= vga_v_cnt + 1'b1 ; 82 end 83 84 assign o_vs = (vga_v_cnt < V_sync_time ) ? 1'b1 : 1'b0 ; 85 86 //generate RGB band 87 always @ (posedge sclk or negedge s_rst_n) begin 88 if(s_rst_n == 1'b0) 89 {vga_r,vga_g,vga_b} <= 24'h0 ; 90 else if((vga_h_cnt>=(H_sync_time + H_back_porch + H_left_border-1) && vga_h_cnt <(H_sync_time + H_back_porch + H_left_border-1+H_addr_time)) 91 && (vga_v_cnt>=(V_sync_time+V_top_border+V_back_porch-1) && vga_v_cnt<(V_sync_time+V_top_border+V_back_porch-1+160))) 92 {vga_r,vga_g,vga_b} <= {8'hff,8'h00,8'h00} ; 93 else if(vga_h_cnt>=(H_sync_time + H_back_porch + H_left_border-1) && vga_h_cnt <(H_sync_time + H_back_porch + H_left_border-1+H_addr_time) 94 && (vga_v_cnt>=(V_sync_time+V_top_border+V_back_porch-1+160) && vga_v_cnt<(V_sync_time+V_top_border+V_back_porch-1+320))) 95 {vga_r,vga_g,vga_b} <= {8'h00,8'hff,8'h00} ; 96 else if(vga_h_cnt>=(H_sync_time + H_back_porch + H_left_border-1) && vga_h_cnt <(H_sync_time + H_back_porch + H_left_border-1+H_addr_time) 97 && (vga_v_cnt>=(V_sync_time+V_top_border+V_back_porch-1+320) && vga_v_cnt<(V_sync_time+V_top_border+V_back_porch-1+V_addr_time))) 98 {vga_r,vga_g,vga_b} <= {8'h00,8'h00,8'hff} ; 99 else 100 {vga_r,vga_g,vga_b} <= 24'h0 ; 101 end 102 //o_de 103 always @ (posedge sclk or negedge s_rst_n) begin 104 if(s_rst_n == 1'b0) 105 o_de <= 1'b0 ; 106 else if(vga_h_cnt>=(H_sync_time + H_back_porch + H_left_border-1) && vga_h_cnt <(H_sync_time + H_back_porch + H_left_border-1+H_addr_time) 107 && (vga_v_cnt>=(V_sync_time+V_top_border+V_back_porch-1+320) && vga_v_cnt<(V_sync_time+V_top_border+V_back_porch-1+V_addr_time))) 108 o_de <= 1'b1 ; 109 end 110 endmodule
2、8bit --10bit的编码模块实现。这个模块并不是自己实现的,而是官网上面有的。可以自行下载。把模块图和代码贴在这边,方便理解和使用。
如上图模块所示:其中在通道0 中使用到了c1和c2端口用来传V-sync,h_sync.其他两个通道都没有用到c1和c0.这里的复位信号是是高复位有效。实现代码:encode.v
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 ////////////////////////////////////////////////////////////////////////////// 2 // 3 // Xilinx, Inc. 2008 www.xilinx.com 4 // 5 ////////////////////////////////////////////////////////////////////////////// 6 // 7 // File name : encode.v 8 // 9 // Description : TMDS encoder 10 // 11 // Date - revision : Jan. 2008 - v 1.0 12 // 13 // Author : Bob Feng 14 // 15 // Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are 16 // provided to you "as is". Xilinx and its licensors make and you 17 // receive no warranties or conditions, express, implied, 18 // statutory or otherwise, and Xilinx specifically disclaims any 19 // implied warranties of merchantability, non-infringement,or 20 // fitness for a particular purpose. Xilinx does not warrant that 21 // the functions contained in these designs will meet your 22 // requirements, or that the operation of these designs will be 23 // uninterrupted or error free, or that defects in the Designs 24 // will be corrected. Furthermore, Xilinx does not warrantor 25 // make any representations regarding use or the results of the 26 // use of the designs in terms of correctness, accuracy, 27 // reliability, or otherwise. 28 // 29 // LIMITATION OF LIABILITY. In no event will Xilinx or its 30 // licensors be liable for any loss of data, lost profits,cost 31 // or procurement of substitute goods or services, or for any 32 // special, incidental, consequential, or indirect damages 33 // arising from the use or operation of the designs or 34 // accompanying documentation, however caused and on any theory 35 // of liability. This limitation will apply even if Xilinx 36 // has been advised of the possibility of such damage. This 37 // limitation shall apply not-withstanding the failure of the 38 // essential purpose of any limited remedies herein. 39 // 40 // Copyright © 2006 Xilinx, Inc. 41 // All rights reserved 42 // 43 ////////////////////////////////////////////////////////////////////////////// 44 `timescale 1 ps / 1ps 45 46 module encode ( 47 input clkin, // pixel clock input 48 input rstin, // async. reset input (active high) 49 input [7:0] din, // data inputs: expect registered 50 input c0, // c0 input 51 input c1, // c1 input 52 input de, // de input 53 output reg [9:0] dout // data outputs 54 ); 55 56 //////////////////////////////////////////////////////////// 57 // Counting number of 1s and 0s for each incoming pixel 58 // component. Pipe line the result. 59 // Register Data Input so it matches the pipe lined adder 60 // output 61 //////////////////////////////////////////////////////////// 62 reg [3:0] n1d; //number of 1s in din 63 reg [7:0] din_q; 64 65 always @ (posedge clkin) begin 66 n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7]; 67 68 din_q <= din; 69 end 70 71 /////////////////////////////////////////////////////// 72 // Stage 1: 8 bit -> 9 bit 73 // Refer to DVI 1.0 Specification, page 29, Figure 3-5 74 /////////////////////////////////////////////////////// 75 wire decision1; 76 77 assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0)); 78 /* 79 reg [8:0] q_m; 80 always @ (posedge clkin) begin 81 q_m[0] <=#1 din_q[0]; 82 q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); 83 q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); 84 q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); 85 q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); 86 q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); 87 q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); 88 q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); 89 q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1; 90 end 91 */ 92 wire [8:0] q_m; 93 assign q_m[0] = din_q[0]; 94 assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); 95 assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); 96 assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); 97 assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); 98 assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); 99 assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); 100 assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); 101 assign q_m[8] = (decision1) ? 1'b0 : 1'b1; 102 103 ///////////////////////////////////////////////////////// 104 // Stage 2: 9 bit -> 10 bit 105 // Refer to DVI 1.0 Specification, page 29, Figure 3-5 106 ///////////////////////////////////////////////////////// 107 reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m 108 always @ (posedge clkin) begin 109 n1q_m <= q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]; 110 n0q_m <= 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]); 111 end 112 113 parameter CTRLTOKEN0 = 10'b1101010100; 114 parameter CTRLTOKEN1 = 10'b0010101011; 115 parameter CTRLTOKEN2 = 10'b0101010100; 116 parameter CTRLTOKEN3 = 10'b1010101011; 117 118 reg [4:0] cnt; //disparity counter, MSB is the sign bit 119 wire decision2, decision3; 120 121 assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m); 122 ///////////////////////////////////////////////////////////////////////// 123 // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)] 124 ///////////////////////////////////////////////////////////////////////// 125 assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m)); 126 127 //////////////////////////////////// 128 // pipe line alignment 129 //////////////////////////////////// 130 reg de_q, de_reg; 131 reg c0_q, c1_q; 132 reg c0_reg, c1_reg; 133 reg [8:0] q_m_reg; 134 135 always @ (posedge clkin) begin 136 de_q <= de; 137 de_reg <= de_q; 138 139 c0_q <= c0; 140 c0_reg <= c0_q; 141 c1_q <= c1; 142 c1_reg <= c1_q; 143 144 q_m_reg <= q_m; 145 end 146 147 /////////////////////////////// 148 // 10-bit out 149 // disparity counter 150 /////////////////////////////// 151 always @ (posedge clkin or posedge rstin) begin 152 if(rstin) begin 153 dout <= 10'h0; 154 cnt <= 5'h0; 155 end else begin 156 if (de_reg) begin 157 if(decision2) begin 158 dout[9] <= ~q_m_reg[8]; 159 dout[8] <= q_m_reg[8]; 160 dout[7:0] <= (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0]; 161 162 cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m); 163 end else begin 164 if(decision3) begin 165 dout[9] <= 1'b1; 166 dout[8] <= q_m_reg[8]; 167 dout[7:0] <= ~q_m_reg[7:0]; 168 169 cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m); 170 end else begin 171 dout[9] <= 1'b0; 172 dout[8] <= q_m_reg[8]; 173 dout[7:0] <= q_m_reg[7:0]; 174 175 cnt <= cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m); 176 end 177 end 178 end else begin 179 case ({c1_reg, c0_reg}) 180 2'b00: dout <= CTRLTOKEN0; 181 2'b01: dout <= CTRLTOKEN1; 182 2'b10: dout <= CTRLTOKEN2; 183 default: dout <= CTRLTOKEN3; 184 endcase 185 186 cnt <= 5'h0; 187 end 188 end 189 end 190 191 endmodule
3、par2serders 模块实现,就是总体框图中的并转串模块。主要是调用了select_i/o的IP。配置为输出模式,DDR的输出(双边输出模式)。输出位宽为1bit ,长度是10.数据模式采用差分模式(和板子的电路图有关);时钟设置为单端模式。
产生IP。复位也是高电平有效。
1 selectio_wiz_0 chanel_2 2 ( 3 .data_out_from_device(temd_red), // input [9:0] data_out_from_device 4 .data_out_to_pins_p(hdmi_data_p[2]), // output [0:0] data_out_to_pins_p 5 .data_out_to_pins_n(hdmi_data_p[2]), // output [0:0] data_out_to_pins_n 6 .clk_in(clk_125M), // input clk_in 7 .clk_div_in(clk_25M), // input clk_div_in 8 .io_reset(~locked) // input io_reset 9 );
4、顶层模块.把所有的子模块实例化进去。这里主要涉及到一个时钟的问题。因为数据经过selec_io ,也就是说,VGA的像素时钟是25MHz,是并行输入的。一下子输入10Bit。然而并转串模块,需要一个一个的输出。对时钟就有要求了。在相同的时间内要保值正常工做,那么我的船型输出时钟,就得加倍。具体加多少倍呢。这和我们选择的select_io的输出出发模式有关。我们选择的DDR触发,也就是一个时钟周期输出两个数据。那么可以的出串行输出的时钟为=10bit/2 *25MHz = 125MHz.所以PLL模块如下:
1 clk_wiz_0 instance_name 2 ( 3 // Clock out ports 4 .clk_out1(clk_25M), // output clk_out1 25M 5 .clk_out2(clk_125M), // output clk_out2 125M 6 // Status and control signals 7 .reset(~s_rst_n), // input reset 8 .locked(locked), // output locked 9 // Clock in ports 10 .clk_in1_p(sclk_p), // input clk_in1_p 11 .clk_in1_n(sclk_n)); // input clk_in1_n
选用差分时钟,是因为板子上给到HDMI的时钟是差分的。
顶层模块代码:HDMI_TOP.v
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 // Project Name : 3 // Website : https://home.cnblogs.com/lgy-gdeu/ 4 // Author : LGY GUET Uiversity 5 // Weixin : li15226499835 6 // Email : 15277385992@163.com 7 // File : 8 // Create : 2020 9 // Revise : 10 // Editor : sublime text{SUBLIME_VERSION}, tab size ({TABS}) 11 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 // Modification History: 13 // Date By Version Change Description 14 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 // {DATE} {TIME} LGY 1.0 ++++++++++++++++ 16 // ********************************************************************************* 17 `timescale 1ns/1ns 18 19 module hdmi_top( 20 //systerm 21 input wire sclk_p ,//zedboard sigle clock port 22 input wire sclk_n , 23 input wire s_rst_n , 24 25 //hdmi 26 output wire [2:0] hdmi_data_p , 27 output wire [2:0] hdmi_data_n , 28 output wire hdmi_clk_p , 29 output wire hdmi_clk_n , 30 output wire hdmi_oen 31 ); 32 33 //========================================================================\ 34 // ################ Define Parameter and Internal signals ################ 35 //========================================================================/ 36 wire [7:0] vga_r ; 37 wire [7:0] vga_g ; 38 wire [7:0] vga_b ; 39 wire vga_hs ; 40 wire vga_vs ; 41 wire vga_de ; 42 43 44 wire clk_25M ;//vga pxil clock 25M 45 wire clk_125M ;//chuan xingshizhong xuyao 5 bei 46 47 //8-10bit 48 wire [9:0] temd_red ; 49 wire [9:0] temd_green; 50 wire [9:0] temd_blue ; 51 wire locked ; 52 53 54 //============================================================================= 55 //+++++++++++++++++++++++++ Main Code +++++++++++++++++++++++++++++++ 56 //============================================================================= 57 assign hdmi_oen = vga_de; 58 59 clk_wiz_0 instance_name 60 ( 61 // Clock out ports 62 .clk_out1(clk_25M), // output clk_out1 25M 63 .clk_out2(clk_125M), // output clk_out2 125M 64 // Status and control signals 65 .reset(~s_rst_n), // input reset 66 .locked(locked), // output locked 67 // Clock in ports 68 .clk_in1_p(sclk_p), // input clk_in1_p 69 .clk_in1_n(sclk_n)); // input clk_in1_n 70 71 72 vga_drive inst_vga_drive ( 73 .sclk (clk_25M), 74 .s_rst_n (s_rst_n), 75 .vga_r (vga_r), 76 .vga_g (vga_g), 77 .vga_b (vga_b), 78 .o_hs (vga_hs), 79 .o_vs (vga_vs), 80 .o_de (vga_de) 81 ); 82 83 encode encode_R ( 84 .clkin (clk_25M),// pixel clock input 85 .rstin (~locked),// async. reset input (active high) 86 .din (vga_r),// data inputs: expect registered 87 .c0 (1'b0),// c0 input 88 .c1 (1'b0),// c1 input 89 .de (vga_de),// de input 90 .dout (temd_red)// data outputs 91 ); 92 93 encode encode_G ( 94 .clkin (clk_25M),// pixel clock input 95 .rstin (~locked),// async. reset input (active high) 96 .din (vga_g),// data inputs: expect registered 97 .c0 (1'b0),// c0 input 98 .c1 (1'b0),// c1 input 99 .de (vga_de),// de input 100 .dout (temd_green)// data outputs 101 ); 102 103 encode encode_B ( 104 .clkin (clk_25M),// pixel clock input 105 .rstin (~locked),// async. reset input (active high) 106 .din (vga_b),// data inputs: expect registered 107 .c0 (vga_hs),// c0 input 108 .c1 (vga_vs),// c1 input 109 .de (vga_de),// de input 110 .dout (temd_blue)// data outputs 111 ); 112 113 selectio_wiz_0 chanel_0 114 ( 115 .data_out_from_device(temd_blue), // input [9:0] data_out_from_device 116 .data_out_to_pins_p(hdmi_data_p[0]), // output [0:0] data_out_to_pins_p 117 .data_out_to_pins_n(hdmi_data_p[0]), // output [0:0] data_out_to_pins_n 118 .clk_in(clk_125M), // input clk_in 119 .clk_div_in(clk_25M), // input clk_div_in 120 .io_reset(~locked) // input io_reset 121 ); 122 123 selectio_wiz_0 chanel_1 124 ( 125 .data_out_from_device(temd_green), // input [9:0] data_out_from_device 126 .data_out_to_pins_p(hdmi_data_p[1]), // output [0:0] data_out_to_pins_p 127 .data_out_to_pins_n(hdmi_data_p[1]), // output [0:0] data_out_to_pins_n 128 .clk_in(clk_125M), // input clk_in 129 .clk_div_in(clk_25M), // input clk_div_in 130 .io_reset(~locked) // input io_reset 131 ); 132 133 selectio_wiz_0 chanel_2 134 ( 135 .data_out_from_device(temd_red), // input [9:0] data_out_from_device 136 .data_out_to_pins_p(hdmi_data_p[2]), // output [0:0] data_out_to_pins_p 137 .data_out_to_pins_n(hdmi_data_p[2]), // output [0:0] data_out_to_pins_n 138 .clk_in(clk_125M), // input clk_in 139 .clk_div_in(clk_25M), // input clk_div_in 140 .io_reset(~locked) // input io_reset 141 ); 142 143 selectio_wiz_0 chanel_clk 144 ( 145 .data_out_from_device(10'b11111_00000), // input [9:0] data_out_from_device 146 .data_out_to_pins_p(hdmi_clk_p), // output [0:0] data_out_to_pins_p 147 .data_out_to_pins_n(hdmi_clk_p), // output [0:0] data_out_to_pins_n 148 .clk_in(clk_125M), // input clk_in 149 .clk_div_in(clk_25M), // input clk_div_in 150 .io_reset(~locked) // input io_reset 151 ); 152 153 endmodule
总体框图:么画完,有点丑。抽时间再画