FPGA串口原理及实现
创始人
2024-11-15 09:05:55
0

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

文章目录

  • 前言
  • 一、串口原理
    • 1.串口简介
    • 2.串口通信方式
  • 二、RS232串口设计
    • 1.接收模块设计
    • 2.接收模块Verilog HDL程序设计
    • 3.发送模块设计
    • 4.接收模块Verilog HDL程序设计
    • 5.串口设计


前言

本文章主要讲述了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 

在这里插入图片描述

相关内容

热门资讯

探索CSGO连接问题,究竟是什... 当连接至csgo服务器时发生错误,可能是由于网络问题、服务器维护或更新、游戏文件损坏、防火墙或安全软...
如何识别iPhone 6 Pl... 要查看iPhone 6 Plus的具体版本,可以进入“设置”>“通用”>“关于本机”,然后查看“型号...
服务器租赁成本之谜,为何价格如... 租用服务器的成本高是因为需要覆盖硬件采购、维护、电力消耗、网络带宽、数据中心安全与冷却系统,以及技术...
hscan.exe进程,它是什... hscan.exe是迅雷的后台服务程序,用于支持其下载加速功能。该进程通常在系统后台运行,对迅雷软件...
高德地图搜索难题,为何某些店铺... 高德地图上找不到店名可能是因为该店铺未在高德地图注册或更新信息,或者店铺已关闭、搬迁但地图数据尚未更...
如何查询百度服务器的IP地址? 百度的服务器IP地址不是固定的,因为它使用了DNS解析来分配请求到多个服务器。你可以通过在命令提示符...
S6设备无法连接互联网,原因何... s6不能上网可能是由于多种原因导致的。检查设备的网络设置是否正确,包括WiFi或移动数据是否已开启并...
为何在网吧无法连接到使命召唤1... 网吧中的《使命召唤16》(COD16)无法连接服务器可能是由于网络配置错误、服务器维护、游戏更新未完...
抖音中已看标记代表什么含义? 在抖音里,“录制已看”通常指的是用户已经观看过某个视频或直播内容。这个标记可能用于帮助用户追踪他们已...
为何外国人将中国英雄联盟服务器... 外国玩家通常将中国《英雄联盟》服务器称为“中国服务器”或简称“国服”,以区别于其他地区的服务器。这种...