基于FPGA的UART串口发送模块设计

文中如果有不正确或者不恰当之处,欢迎指正。感谢!

1. RS232 通信接口标准

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART)是一种异步收发传输器,其在数据发送时将并行数据转换成串行数据来传输,在数据接收时将接收到的串行数据转换成并行数据,可以实现全双工传输和接收。它包括了 RS232、RS449、RS423、RS422 和 RS485 等接口标准规范和总线标准规范。换句话说,UART 是异步串行通信的总称。而 RS232、RS449、RS423、RS422 和 RS485 等,是对应各种异步串行通信口的接口标准和总线标准,它们规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容。
本文重点阐述RS-232 串行数据通信的接口标准,及其使用Verilog HDL语言实现。RS-232的DB9 接口的针脚定义如下图所示:
在这里插入图片描述

引脚简写功能
Pin1DCD调制解调器通知电脑有载波被侦测到
Pin2RXD接收数据
Pin3TXD发送数据
Pin4DTR电脑告诉调制解调器可以进行传输
Pin5GND地线
Pin6DSR调制解调器告诉电脑一切准备就绪
Pin7RTS电脑要求调制解调器将数据提交;
Pin8CTS调制解调器通知电脑可以传数据过来;
Pin9R1调制解调器通知电脑有电话进来。

2. UART 关键参数及时序图

UART 通信在使用前需要做多项设置,最常见的设置包括数据位数、波特率大小、奇偶校验类型和停止位数。

关键参数时序
数据位(Data bits)该参数定义单个 UART 数据传输在开始到停止期间发送的数据位数。可选择为:5、6、7 或者 8(默认)
波特率(Baud)是指从一设备发到另一设备的波特率,即每秒钟可以通信的数据比特个数。典型的波特率有 300, 1200, 2400, 9600, 19200, 115200 等。一般通信两端设备都要设为相同的波特率,但有些设备也可设置为自动检测波特率。
奇偶校验类型(Parity Type)是用来验证数据的正确性。
停止位(Stop bits)在每个字节的数据位发送完成之后,发送停止位,来标志着一次数据传输完成,同时用来帮助接受信号方硬件重同步。可选择为:1(默认)、1.5 或者 2 位。

按照一个完整的字节包括一位起始位、8 位数据位、一位停止位即总共十位数据来算, 要想完整的实现这十位数据的发送,就需要 11 个波特率时钟脉冲,第 1 个脉冲标记一次传输的起始,第 11 个脉冲标记一次传输的结束,如下图所示:
在这里插入图片描述

BPS_CLK 信号的第一个上升沿到来时,字节发送模块开始发送起始位,接下来的 2 到 9个上升沿,发送 8 个数据位,第 10 个上升沿到第 11 个上升沿为停止位的发送。

3. UART 异步串行通信发送模块设计与实现

基于上述原理,本文要实现的串口发送模块整体框图,如下图所示,其接口下表所示。
在这里插入图片描述

接口功能
Send_En发送使能信号
Data_Byte待传输 8bit 数
Baud_Set波特率设置信号
Clk系统时钟
Rst_n系统复位信号
Rs232_TxRs232 信号输出
Tx_down发送结束信号,一个时钟周期高电平
UART_state发送状态,处于发送状态时为 1

在uart模块中通常包括五部分
1.波特率设置模块
2.波特率时钟产生模块
3.波特率计数模块
4.数据发送模块
5.控制信号模块

3.1 波特率设置模块

波特率是 UART 通信中需要设置的参数之一。需要波特率周期的计数值bps_DR计算的计算公式如下
"

bps_DR=sclk/bps

sclk为系统时钟的频率;bps为波特率
例如波特率为9600的计数值:bps_DR=50000000/9600=5208

波特率50M 系统时钟计数值
96005208
192002604
384001302
57600868
115200434

波特率设置模块如下图所示:
在这里插入图片描述

3.1.1 Verilog代码实现

reg		[15:0]	bps_DR;//分频计数最大值

always@ (posedge	Clk  or  negedge  Rst_n)
	if(!Rst_n)
		bps_DR<=16'd5208;
	else
	begin
		case(Baud_Set)
		3'b000: bps_DR<=16'd5208;
		3'b001: bps_DR<=16'd2604;
		3'b010: bps_DR<=16'd1302;
		3'b011: bps_DR<=16'd868;
		3'b100: bps_DR<=16'd434;
		default: bps_DR<=16'd5208;
		endcase
	end

3.2 波特率时钟产生模块

该模块用于产生分频时钟,分频时钟的高电平为1个系统周期,低电平为(bps_DR-1)系统周期。
在这里插入图片描述

3.2.1 波特率时钟Verilog代码实现

reg	[15:0]		Div_Cnt;
reg					en_cnt;
reg					bps_clk;
//分频计数
always@ (posedge	Clk  or  negedge  Rst_n)
	if(!Rst_n)
		Div_Cnt<=16'd0;
	else
		if(en_cnt)
		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  negedge  Rst_n)
	if(!Rst_n)
		bps_clk<=1'b0;
	else
		if(Div_Cnt==16'd1)
		bps_clk<=1'b1;
		else
		bps_clk<=1'b0;

3.3 波特率计数模块

对产生的波特率时钟进行进行计数。波特率计数模块模块的结构图如下:
在这里插入图片描述

3.3.1 波特率计数模块verilog代码

reg	[3:0]		bps_cnt_q;
reg				clr;
always@ (posedge	Clk  or  negedge  Rst_n)
	if(!Rst_n)
		bps_cnt_q<=4'd0;
	else
		if(clr)
		bps_cnt_q<=4'd0;
		else
			   if(bps_clk)
			   bps_cnt_q<=bps_cnt_q+1'b1;
				else
				bps_cnt_q<=bps_cnt_q;

3.4 数据发送模块

数据发送模块相等于一个并行数据转化为串行数据。此处采用状态机控制,状态图如下:
在这里插入图片描述

3.4.1数据发送模块Verilog代码实现

localparam	IDLE=5'b00000;
localparam	STAET_BIT=5'b00001;
localparam	B0=5'b00011;
localparam	B1=5'b00010;
localparam	B2=5'b00110;
localparam	B3=5'b00111;
localparam	B4=5'b00101;
localparam	B5=5'b00100;
localparam	B6=5'b01100;
localparam  B7=5'b01101;
localparam	STOP_BIT=5'b01111;

reg	[5:0]	state;
reg			Rx232_Tx;
reg	[7:0]	r_date_byte;
reg			Tx_down;

always@(posedge	Clk  or  negedge	Rst_n)
			if(!Rst_n)
			state<=IDLE;
			else
			case(state)
			IDLE: if(bps_cnt_q==4'd1)
					state<=STAET_BIT;
			STAET_BIT: if(bps_cnt_q==4'd2)
					state<=B1;
			B1: if(bps_cnt_q==4'd3)
					state<=B2;
			B2: if(bps_cnt_q==4'd4)
					state<=B3;
			B3: if(bps_cnt_q==4'd5)
					state<=B4;
			B4: if(bps_cnt_q==4'd6)
					state<=B5;
			B5: if(bps_cnt_q==4'd7)
					state<=B6;
			B6: if(bps_cnt_q==4'd8)
					state<=B7;
			B7: if(bps_cnt_q==4'd9)
					state<=B8;
			B8: if(bps_cnt_q==4'd10)
					state<=STOP_BIT;
			STOP_BIT: if(bps_cnt_q==4'd11)
					state<=IDLE;
			default:state<=IDLE;
			endcase
always@(posedge	Clk  or  negedge	Rst_n)
			if(Rst_n)
			Rx232_Tx<=1'd1;
			else
			if(State==IDLE)
			Rx232_Tx<=1'd1;
			else
			if(state==STAET_BIT && bps_cnt_q==4'd1)
			Rx232_Tx<=1'd0;
			else
			if(state==B1 && bps_cnt_q==4'd2)
			RX232_Tx<=r_date_byte[0];
			else
			if(state==B2 && bps_cnt_q==4'd3)
			RX232_Tx<=r_date_byte[1];
			else
			if(state==B3 && bps_cnt_q==4'd4)
			RX232_Tx<=r_date_byte[2];
			else
			if(state==B4 && bps_cnt_q==4'd5)
			RX232_Tx<=r_date_byte[3];
			else
			if(state==B5 && bps_cnt_q==4'd6)
			RX232_Tx<=r_date_byte[4];
			else
			if(state==B6 && bps_cnt_q==4'd7)
			RX232_Tx<=r_date_byte[5];
			else
			if(state==B7 && bps_cnt_q==4'd8)
			RX232_Tx<=r_date_byte[6];
			else
			if(state==B8 && bps_cnt_q==4'd9)
			RX232_Tx<=r_date_byte[7];
			else
			if(state==STOP_BIT && bps_cnt_q==4'd10)
			RX232_Tx<=1'B1;
			else
			if(state==IDLE && bps_cnt_q==4'd11)
			RX232_Tx<=1'B1;
always@(posedge	Clk  or  negedge	Rst_n)
			if(Rst_n)
			Tx_down<=1'd0;
			else
			if(state==STOP_BIT && bps_cnt_q==4'd10)
			Tx_down<=1'd1;
			else
			Tx_down<=1'd0;

3.5 控制信号模块

控制信号是当Send_En给一个高脉冲,UART模块中可以产生一个使UART模块工作的使能信号。结构图如下图所示:
在这里插入图片描述

3.5.1 控制信号模块Verilog代码实现

always@(posedge	Clk  or  negedge	Rst_n)
			if(!Rst_n)
			UART_state<=1'B0;
			else
			if(Send_En)
			UART_state<=1'B1;
			else
			if(Tx_down)
			UART_state<=1'B0;
			else
			UART_state<=UART_state;

4. Verilog实现UART模块

以下代码已经在FPGA上硬件验证过

module	uart_tx(
	input		wire					Clk,
	input		wire					Rst_n,
	input		wire		[2:0]		Baud_Set,
	input		wire					Send_En,
	input		wire		[7:0]		date_byte,
	output	reg					RX232_Tx,
	output	reg					Tx_down,
	output	reg					UART_state
);

localparam	IDLE=5'b00000;
localparam	STAET_BIT=5'b00001;
localparam	B1=5'b00011;
localparam	B2=5'b00010;
localparam	B3=5'b00110;
localparam	B4=5'b00111;
localparam	B5=5'b00101;
localparam	B6=5'b00100;
localparam	B7=5'b01100;
localparam  B8=5'b01101;
localparam	STOP_BIT=5'b01111;

reg		[5:0]	state;
reg		[7:0]	r_date_byte;
reg		[15:0]	bps_DR;//分频计数最大值
reg		[15:0]	Div_Cnt;
reg					bps_clk;
reg	[3:0]		bps_cnt_q;

			
always@(posedge	Clk  or  negedge	Rst_n)
			if(!Rst_n)
			UART_state<=1'B0;
			else
			if(Send_En)
			UART_state<=1'B1;
			else
			if(Tx_down)
			UART_state<=1'B0;
			else
			UART_state<=UART_state;
			
always@(posedge	Clk  or  negedge	Rst_n)
			if(!Rst_n)
			Tx_down<=1'd0;
			else
			if(state==STOP_BIT && bps_cnt_q==4'd11)
			Tx_down<=1'd1;
			else
			Tx_down<=1'd0;
			
always@ (posedge	Clk  or  negedge  Rst_n)
	if(!Rst_n)
	r_date_byte<=8'd0;
	else
	if(UART_state)
	r_date_byte<=date_byte;
	else
	r_date_byte<=r_date_byte;

always@ (posedge	Clk  or  negedge  Rst_n)
	if(!Rst_n)
		bps_DR<=16'd5208;
	else
	begin
		case(Baud_Set)
		3'b000: bps_DR<=16'd5208;
		3'b001: bps_DR<=16'd2604;
		3'b010: bps_DR<=16'd1302;
		3'b011: bps_DR<=16'd868;
		3'b100: bps_DR<=16'd434;
		default: bps_DR<=16'd5208;
		endcase
	end


//分频计数
always@ (posedge	Clk  or  negedge  Rst_n)
	if(!Rst_n)
		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  negedge  Rst_n)
	if(!Rst_n)
		bps_clk<=1'b0;
	else
		if(Div_Cnt==16'd1)
		bps_clk<=1'b1;
		else
		bps_clk<=1'b0;
		
//波特率计数


always@ (posedge	Clk  or  negedge  Rst_n)
	if(!Rst_n)
		bps_cnt_q<=4'd0;
	else
		if(Tx_down)
		bps_cnt_q<=4'd0;
		else
			   if(bps_clk)
			   bps_cnt_q<=bps_cnt_q+1'b1;
				else
				bps_cnt_q<=bps_cnt_q;
				


always@(posedge	Clk  or  negedge	Rst_n)
			if(!Rst_n)
			state<=IDLE;
			else
			case(state)
			IDLE: if(bps_cnt_q==4'd1)
					state<=STAET_BIT;
			STAET_BIT: if(bps_cnt_q==4'd2)
					state<=B1;
			B1: if(bps_cnt_q==4'd3)
					state<=B2;
			B2: if(bps_cnt_q==4'd4)
					state<=B3;
			B3: if(bps_cnt_q==4'd5)
					state<=B4;
			B4: if(bps_cnt_q==4'd6)
					state<=B5;
			B5: if(bps_cnt_q==4'd7)
					state<=B6;
			B6: if(bps_cnt_q==4'd8)
					state<=B7;
			B7: if(bps_cnt_q==4'd9)
					state<=B8;
			B8: if(bps_cnt_q==4'd10)
					state<=STOP_BIT;
			STOP_BIT: if(bps_cnt_q==4'd11)
					state<=IDLE;
			default:state<=IDLE;
			endcase
always@(posedge	Clk  or  negedge	Rst_n)
			if(!Rst_n)
			RX232_Tx<=1'd1;
			else
			if(state==IDLE)
			RX232_Tx<=1'd1;
			else
			if(state==STAET_BIT && bps_cnt_q==4'd1)
			RX232_Tx<=1'd0;
			else
			if(state==B1 && bps_cnt_q==4'd2)
			RX232_Tx<=r_date_byte[0];
			else
			if(state==B2 && bps_cnt_q==4'd3)
			RX232_Tx<=r_date_byte[1];
			else
			if(state==B3 && bps_cnt_q==4'd4)
			RX232_Tx<=r_date_byte[2];
			else
			if(state==B4 && bps_cnt_q==4'd5)
			RX232_Tx<=r_date_byte[3];
			else
			if(state==B5 && bps_cnt_q==4'd6)
			RX232_Tx<=r_date_byte[4];
			else
			if(state==B6 && bps_cnt_q==4'd7)
			RX232_Tx<=r_date_byte[5];
			else
			if(state==B7 && bps_cnt_q==4'd8)
			RX232_Tx<=r_date_byte[6];
			else
			if(state==B8 && bps_cnt_q==4'd9)
			RX232_Tx<=r_date_byte[7];
			else
			if(state==STOP_BIT && bps_cnt_q==4'd10)
			RX232_Tx<=1'B1;
			else
			if(state==IDLE && bps_cnt_q==4'd11)
			RX232_Tx<=1'B1;
		
endmodule 

5. testbench

`timescale     1ns/1ps
`define		Clk_period	20			
module	uart_tx_tb();

	reg					Clk			;
	reg					Rst_n			;
	reg		[2:0]		Baud_Set		;
	reg					Send_En		;
	reg		[7:0]		date_byte	;
	wire					RX232_Tx		;
	wire					Tx_down		;
	wire					UART_state	;


uart_tx	U1(
	.Clk(Clk),
	.Rst_n(Rst_n),
	.Baud_Set(Baud_Set),
	.Send_En(Send_En),
	.date_byte(date_byte),
	.RX232_Tx(RX232_Tx),
	.Tx_down(Tx_down),
	.UART_state(UART_state)
);

initial	Clk=1;
always	#(`Clk_period/2)	Clk=~Clk;

initial
begin
	Rst_n=0;
	Send_En=0;
	Baud_Set=3'b000;
	date_byte=8'haa;
	#(`Clk_period*20+1);
	Rst_n=1;
	Send_En=1;
	#(`Clk_period);
	Send_En=0;
	#(`Clk_period*100000);
	
	Send_En=1;
	date_byte=8'h55;
	#(`Clk_period);
	Send_En=0;
	#(`Clk_period*100000);
	$stop;
	
end
endmodule 

6.仿真结果

在这里插入图片描述

https://www.bilibili.com/video/BV1oE411Y7uv?p=12 小梅哥教学视频_UART串口发送
https://www.bilibili.com/video/BV1j441117a9?p=4 尤恺元教学视频_状态机

  • 14
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值