我觉得FPGA的学习不能够只停留在理论的仿真这一层面上,诚然理论仿真已经能够教会我们很多东西,但板级验证还能提供不一样的bug供我们学习(\doge),因此我重启了去年购买的小梅哥ACX720开发板来进一步学习FPGA,这次主要复现并学习了小梅哥Xilinx FPGA自学教程中的串口发送模块的设计与验证。
异步串行通信原理
串口通信是通信系统中常用的一种通信方式,又称通用异步收发传输器(Universal Asynchronous Receiver/Transmitter, UART),此种通信方式在数据发送时将并行数据转换成串行数据来传输,再将数据接收到的串行数据转换成并行数据,以此来实现全双工传输和接收。它包括了RS232、RS449、RS423、RS422和RS485等接口标准规范和总线标准规范,这些规范规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容,本次学习主要是基于RS-232接口标准。
在RS-232标准中,最常用的配置是8N1(即8个数据位、无奇偶校验位、一个停止位),其发送一个字节时序图如下图所示:
RS232硬件电路设计
RS232早期使用的方案为RS23转TTL,这时需要MAX232或者SP3232等电平转换芯片来进行数据转换,具体的硬件设计原理图如下图所示:
随着系统集成度越来越高,如今大多数系统已经转用USB转串口的方式来实现RS232,此时需要CH340E来实现对应的USB转串口,具体的电路原理图如下所示(怪不得我以前做课设需要安装CH340的驱动原来是用来驱动串口通信的):
UART Verilog 异步串行发送模块设计与实现
本次复现的串口发送模块的整体框图如下所示:
其中输入端口clk为系统的50M时钟的接入端口,reset_n为系统低电平复位信号,data_byte为待发送的八位二进制数据,send_en为系统高电平使能信号,baud_set为串口波特率设定信号,以上端口均为输入端口。输出端口则包括:单比特串行发送信号uart_tx,串口发送完毕指示信号tx_done,串口发送状态信号uart_state。
具体硬件验证时,采用了虚拟IO(vio)IP核来实现八位data_byte数据的输入,并设置一虚拟按钮来实现模块使能信号的输入,然后用串口调试助手接收FPGA发送的串行数据来验证发送结果。
串口单字节发送验证效果如下所示:
系统顶层模块Verilog代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| module uart_tx_test( clk,reset_n,uart_tx,led );
input clk; input reset_n; output uart_tx; output led;
wire send_en; wire [7:0]data_byte; wire test_en; reg test_en_dly1; reg test_en_dly2;
assign reset=~reset_n;
always @(posedge clk) begin test_en_dly1 <= test_en; test_en_dly2 <= test_en_dly1; end
assign send_en=test_en_dly1 & !test_en_dly2;
vio_0 vio_0_inst1 ( .clk(clk), .probe_out0(test_en), .probe_out1(data_byte) );
uart_tx uart_tx_inst1 ( .clk (clk ), .reset_n (reset_n ), .data_byte (data_byte), .send_en (send_en ), .baud_set (3'd0 ), .uart_tx (uart_tx ), .tx_done ( ), .uart_state (led) );
endmodule
|
串口单字节发送模块的Verilog代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| module uart_tx( clk, reset_n, data_byte, send_en, baud_set,
uart_tx, tx_done, uart_state );
input clk; input reset_n; input [7:0]data_byte; input send_en; input [2:0]baud_set; output reg uart_tx; output reg tx_done; output reg uart_state;
reg [15:0]bps_DR; reg bps_clk; reg [15:0]div_cnt; reg [3:0]bps_cnt; reg [7:0]data_byte_reg;
assign reset = ~reset_n; localparam START_BIT = 1'b0; localparam STOP_BIT = 1'b1;
always @(posedge clk or posedge reset) begin if(reset) bps_DR <= 16'd5207; else begin case (baud_set) 0: bps_DR <= 16'd5207; 1: bps_DR <= 16'd2603; 2: bps_DR <= 16'd1301; 3: bps_DR <= 16'd867; 4: bps_DR <= 16'd433; default: bps_DR <= 16'd5207; endcase end end
always @(posedge clk or posedge reset) if(reset) div_cnt <= 16'd0; else if (uart_state) begin if(div_cnt == bps_DR) div_cnt <= 16'd0; else div_cnt <= div_cnt+1'b1; end else div_cnt <= 16'd0;
always @(posedge clk or posedge reset) if(reset) bps_clk <= 1'b0; else if(div_cnt==16'd1) bps_clk <= 1'b1; else bps_clk <= 1'b0;
always @(posedge clk or posedge reset) if(reset) bps_cnt <= 4'd0; else if(bps_cnt == 4'd11) bps_cnt <= 4'd0; else if(bps_clk) bps_cnt <= bps_cnt+1'b1; else bps_cnt <= bps_cnt;
always @(posedge clk or posedge reset) if(reset) tx_done <= 1'b0; else if(bps_cnt==4'd11) tx_done <= 1'b1; else tx_done <= 1'b0;
always @(posedge clk or posedge reset) if(reset) uart_state <= 1'b0; else if(send_en) uart_state <= 1'b1; else if(bps_cnt == 4'd11) uart_state <= 1'b0; else uart_state <= uart_state;
always @(posedge clk or posedge reset) if(reset) data_byte_reg <= 8'd0; else if(send_en) data_byte_reg <= data_byte; else data_byte_reg <= data_byte_reg;
always @(posedge clk or posedge reset) begin if(reset) uart_tx <= 1'b1; else begin case (bps_cnt) 0:uart_tx <= 1'b1; 1:uart_tx <= START_BIT; 2:uart_tx <= data_byte_reg[0]; 3:uart_tx <= data_byte_reg[1]; 4:uart_tx <= data_byte_reg[2]; 5:uart_tx <= data_byte_reg[3]; 6:uart_tx <= data_byte_reg[4]; 7:uart_tx <= data_byte_reg[5]; 8:uart_tx <= data_byte_reg[6]; 9:uart_tx <= data_byte_reg[7]; 10:uart_tx <= STOP_BIT; default: uart_tx <= 1'b1; endcase end end
endmodule
|