FPGA串口原理及实现

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


前言

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

在这里插入图片描述

  • 26
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值