提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
本文章主要讲述了RS232串口原理,代码实现以及上板情况。
通用异步收发传输器,英文全称Universal Asynchronous Receiver/Transmitter,简称UART。
UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。包括RS232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范。
tx:发送数据端口线;
rx:接收数据端口线。
tx和rx可同时发送接收数据,实现全双工通信。
PC端通过tx发送8bit数据,FPGA通过rx一位一位进行接收,从最低位到最高位,最后在FPGA形成8位数据;从FPGA到PC端也是如此传输。
RS232串口示意图如图2所示。3个输入端,分别接收时钟信号、复位信号和接收信号;1个输出端,输出信号。
现分别对接收模块和发送模块进行设计。
如图3,分别为接收模块和发送模块。接收模块中,串行数据rx转换成并行数据数据Po_data[7:0],还生成了数据有效标志位Po_flag。而在发送模块,这些信号连同时钟信号和复位信号作为输入端,输出信号rx。
如图4为接收模块时序设计图。3个输入端分别为时钟信号、复位信号和8位串行的传输信号,用橙色方块标记;红色方块是接收模块的输出信号,并行数据Po_data[7:0]和数据标志位Po_flag。
黄色方块Rx_reg1、Rx_reg2、Rx_reg3为3个寄存器,用来传递串行信号,这里用3个寄存器是为了减少亚稳态的危害。
Start_flag是起始标志信号。
Work_en为使能信号,确定传输范围。
baud_cnt:计数器。这里波特率为9600,因此周期数=1/9600*10^9ns/20ns=5208。
Bit_flag:数据提取的标志信号。
Bit_cnt:比特计数器。对Rx_reg3的8个数据位提取。
rx_data:对传输信号进行拼接。
rx_flag:拼接完成标志位。
现对图4的时序设计图编写Verilog HDL程序uart_rx。
module uart_rx #( parameter Uart_bps = 'd9600, parameter Clk_freq = 'd50_000_000 ) //设置参数(波特率9600,时钟晶振频率50MHz) ( input wire clk, input wire rst_n, input wire rx, output reg [7:0] po_data, output reg po_flag ); parameter Baud_cnt_max = Clk_freq / Uart_bps; //设置计数器参数 reg rx_reg1; reg rx_reg2; reg rx_reg3; reg start_flag; reg work_en; reg [15:0] baud_cnt; reg bit_flag; reg [3:0] bit_cnt; reg [7:0] rx_data; reg rx_flag; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) rx_reg1 <= 1'b1; else rx_reg1 <= rx; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) rx_reg2 <= 1'b1; else rx_reg2 <= rx_reg1; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) rx_reg3 <= 1'b1; else rx_reg3 <= rx_reg2; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) start_flag <= 1'b0; else if((rx_reg3 == 1'b1) && (rx_reg2 == 1'b0) && (work_en ==1'b0)) start_flag <= 1'b1; else start_flag <= 1'b0; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) work_en <= 1'b0; else if(start_flag == 1'b1) work_en <= 1'b1; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) work_en <= 1'b0; else work_en <= work_en; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) baud_cnt <= 16'd0; else if((baud_cnt == Baud_cnt_max - 1) || (work_en == 1'b0)) baud_cnt <= 16'd0; else baud_cnt <= baud_cnt + 1'b1; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) bit_flag <= 1'b0; else if(baud_cnt == Baud_cnt_max/2 - 1) bit_flag <= 1'b1; else bit_flag <= 1'b0; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) bit_cnt <= 4'd0; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) bit_cnt <= 4'd0; else if(bit_flag == 1'b1) bit_cnt <= bit_cnt + 1'b1; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) rx_data <= 8'b0; else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1)) rx_data <= {rx_reg3,rx_data[7:1]}; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) rx_flag <= 1'b0; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) rx_flag <= 1'b1; else rx_flag <= 1'b0; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) po_data <= 1'b0; else if(rx_flag == 1'b1) po_data <= rx_data; always@(posedge clk or negedge rst_n) if(rst_n == 1'b0) po_flag <= 1'b0; else po_flag <= rx_flag; endmodule
进行仿真验证。编写仿真文件uart_rx_tb
`timescale 1ns / 1ns module uart_rx_tb(); reg clk; reg rst_n; reg rx; wire [7:0] po_data; wire po_flag; initial begin clk = 1'b1; rst_n <= 1'b0; rx <= 1'b1; #20 rst_n <= 1'b1; end initial begin #200 rx_bit(8'd0); rx_bit(8'd1); rx_bit(8'd2); rx_bit(8'd3); rx_bit(8'd4); rx_bit(8'd5); rx_bit(8'd6); rx_bit(8'd7); end always #10 clk = ~clk; task rx_bit ( input [7:0] data ); integer i; for(i = 0; i < 10; i = i + 1) begin case(i) 0:rx <= 1'b0; 1:rx <= data[0]; 2:rx <= data[1]; 3:rx <= data[2]; 4:rx <= data[3]; 5:rx <= data[4]; 6:rx <= data[5]; 7:rx <= data[6]; 8:rx <= data[7]; 9:rx <= 1'b1; endcase #(5208*20); end endtask uart_rx #( .Uart_bps (9600), .Clk_freq (50_000_000) ) uart_rx_inst ( .clk (clk ), .rst_n (rst_n), .rx (rx), .po_data (po_data), .po_flag (po_flag) ); endmodule
模拟接收0-7共8位数据,仿真图如下图所示:
对图5的时序设计图编写Verilog HDL程序uart_tx。
module uart_tx #( parameter uart_bps = 9600, parameter clk_freq = 50_000_000 ) ( input wire clk, input wire rst_n, input wire [7:0] Pi_data, input wire Pi_flag, output reg tx ); parameter baud_max_cnt = clk_freq / uart_bps; reg work_en; reg [15:0] baud_cnt; reg bit_flag; reg [8:0] bit_cnt; always@(posedge clk or negedge rst_n) if(rst_n == 0) work_en <= 0; else if(Pi_flag == 1) work_en <= 1; else if((bit_flag == 1) && (bit_cnt == 9)) work_en <= 0; always@(posedge clk or negedge rst_n) if(rst_n == 0) baud_cnt <= 0; else if((work_en == 0)||(baud_cnt == baud_max_cnt - 1)) baud_cnt <= 0; else if(work_en <= 1) baud_cnt <= baud_cnt + 1; always@(posedge clk or negedge rst_n) if(rst_n == 0) bit_flag <= 0; else if(baud_cnt == 1) bit_flag <= 1; else bit_flag <= 0; always@(posedge clk or negedge rst_n) if(rst_n == 0) bit_cnt <= 0; else if((bit_cnt == 9)&&(bit_flag == 1)) bit_cnt <= 0; else if(bit_flag == 1) bit_cnt <= bit_cnt + 1; always@(posedge clk or negedge rst_n) if(rst_n == 0) tx <= 1; else if(bit_flag == 1) case(bit_cnt) 0 :tx <= 0; 1 :tx <= Pi_data[0]; 2 :tx <= Pi_data[1]; 3 :tx <= Pi_data[2]; 4 :tx <= Pi_data[3]; 5 :tx <= Pi_data[4]; 6 :tx <= Pi_data[5]; 7 :tx <= Pi_data[6]; 8 :tx <= Pi_data[7]; 9 :tx <= 1; default: tx <= 1; endcase endmodule
进行仿真验证。编写仿真文件uart_rx_tb
`timescale 1ns / 1ns module uart_tx_tb(); reg clk; reg rst_n; reg [7:0] Pi_data; reg Pi_flag; wire tx; initial begin clk = 1; rst_n <= 0; #20 rst_n <= 1; end always #10 clk = ~clk; initial begin Pi_data <= 0; Pi_flag <= 0; #200 //数据0 Pi_data <= 0; Pi_flag <= 1; #20 Pi_flag <= 0; #(5208*10*20) //数据1 Pi_data <= 1; Pi_flag <= 1; #20 Pi_flag <= 0; #(5208*10*20) //数据2 Pi_data <= 2; Pi_flag <= 1; #20 Pi_flag <= 0; #(5208*10*20) //数据3 Pi_data <= 3; Pi_flag <= 1; #20 Pi_flag <= 0; #(5208*10*20) //数据4 Pi_data <= 4; Pi_flag <= 1; #20 Pi_flag <= 0; #(5208*10*20) //数据5 Pi_data <= 5; Pi_flag <= 1; #20 Pi_flag <= 0; #(5208*10*20) //数据6 Pi_data <= 6; Pi_flag <= 1; #20 Pi_flag <= 0; #(5208*10*20) //数据7 Pi_data <= 7; Pi_flag <= 1; #20 Pi_flag <= 0; end uart_tx #( .uart_bps (9600), .clk_freq (50_000_000) ) uart_tx_inst ( .clk(clk), .rst_n(rst_n), .Pi_data(Pi_data), .Pi_flag(Pi_flag), .tx(tx) ); endmodule
模拟发送0-7共8位数据,仿真图如下图所示:
设计好发送模块和接收模块后,对整体进行设计。
代码设计(对端口进行例化)
module rs232 ( input wire clk, input wire rst_n, input wire rx, output wire tx ); wire [7:0] rx_data; wire rx_flag; uart_rx #( .uart_bps (9600), .clk_freq (50_000_000) ) uart_rx_inst ( .clk (clk ), .rst_n (rst_n), .rx (rx), .po_data (rx_data), .po_flag (rx_flag) ); uart_tx #( .Uart_bps (9600), .Clk_freq (50_000_000) ) uart_tx_inst ( .clk(clk), .rst_n(rst_n), .Pi_data(rx_data), .Pi_flag(rx_flag), .tx(tx) ); endmodule
仿真验证:
`timescale 1ns/1ns module rs232_tb(); reg clk; reg rst_n; reg rx; wire tx; initial begin clk = 1; rst_n <= 0; rx <= 1; #20 rst_n <= 1; end always #10 clk = ~clk; initial begin #200 rx_byte(); end task rx_byte(); integer j; for(j = 0; j < 8; j = j + 1) rx_bit(j); endtask task rx_bit ( input [7:0] data ); integer i; for(i = 0; i < 10; i = i + 1) begin case(i) 0:rx <= 1'b0; 1:rx <= data[0]; 2:rx <= data[1]; 3:rx <= data[2]; 4:rx <= data[3]; 5:rx <= data[4]; 6:rx <= data[5]; 7:rx <= data[6]; 8:rx <= data[7]; 9:rx <= 1'b1; endcase #(5208*20); end endtask rs232 rs232_inst ( .clk(clk), .rst_n(rst_n), .rx(rx), .tx(tx) ); endmodule