FPGA串口原理及实现

avatar
作者
猴君
阅读量:0

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

本文章主要讲述了RS232串口原理,代码实现以及上板情况。


一、串口原理

1.串口简介

  通用异步收发传输器,英文全称Universal Asynchronous Receiver/Transmitter,简称UART。
  UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。包括RS232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范。

2.串口通信方式

在这里插入图片描述

图1

tx:发送数据端口线;
rx:接收数据端口线。
tx和rx可同时发送接收数据,实现全双工通信

PC端通过tx发送8bit数据,FPGA通过rx一位一位进行接收,从最低位到最高位,最后在FPGA形成8位数据;从FPGA到PC端也是如此传输。

二、RS232串口设计

RS232串口示意图如图2所示。3个输入端,分别接收时钟信号、复位信号和接收信号;1个输出端,输出信号。
在这里插入图片描述

图2

现分别对接收模块和发送模块进行设计。
在这里插入图片描述

图3

如图3,分别为接收模块和发送模块。接收模块中,串行数据rx转换成并行数据数据Po_data[7:0],还生成了数据有效标志位Po_flag。而在发送模块,这些信号连同时钟信号和复位信号作为输入端,输出信号rx。

1.接收模块设计

在这里插入图片描述

图4

如图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:拼接完成标志位。

2.接收模块Verilog HDL程序设计

现对图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位数据,仿真图如下图所示:
在这里插入图片描述

3.发送模块设计

在这里插入图片描述

图5

4.接收模块Verilog HDL程序设计

对图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位数据,仿真图如下图所示:
在这里插入图片描述

5.串口设计

设计好发送模块和接收模块后,对整体进行设计。
在这里插入图片描述
代码设计(对端口进行例化)

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 

在这里插入图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!