FPGA常用接口协议--UART

前言

    UART接口协议是一种比较简单、非常常用的一种接口协议,使用它的场景很常见,是我们学习FPGA一定要会的接口协议;

UART协议

    通用异步收发器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种串行、异步、全双工的通信协议,在嵌入式领域应用的非常广泛。其数据通信格式如下图:
在这里插入图片描述

UART 数据传输格式

LSB:
least significant bit 表示二进制数据的最低位。

MSB :
most significant bit 表示二进制数据的最高位。

起始位: 
    每开始一次通信时发送方先发出一个逻辑”0”的信号(低电平),表示传输字符的开始。因为总线空闲时为高电平所以开始一次通信时先发送一个明显区别于空闲状态的信号即低电平。

数据位: 
    起始位之后就是我们所要传输的数据,数据位可以是5、6、7、8,9位等,构成一个字符(一般都是8位)。先发送最低位,最后发送最高位,使用低电平表示‘0’高电平表示‘1’完成数据位的传输。

奇偶校验位: 
    数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。

停止位: 
    它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备之间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟的机会。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢。

空闲位: 
     UART协议规定,当总线处于空闲状态时信号线的状态为‘1’即高电平,表示当前线路上没有数据传输。

UART协议实现(verilog)

     为了更加简单的描述串口协议,我们这里使用常用的1位起始位,1位停止位,8位数据位,无奇偶校验位,波特率9600(本次波特率以参数形式提供,如需测试其他波特率,计算后更改即可)。

     UART发送端代码如下

module uart_tx
#(
	parameter max_baud = 5208,
	parameter max_samp = 2604
)
(
	input	wire			sclk			,
	input	wire			rst_n			,
	input	wire			tx_data_en		,//接收完成标志
	input	wire[7:0]		tx_data			,//接收数据
	
	output	reg				uart_tx		
);	
	reg						tx_en			;
	reg[7:0]				tx_data_tmp		;
	reg[12:0]				cnt_baud		;//波特率
	reg						bit_flag		;
	reg[3:0]				bit_cnt			;
	
	// parameter				max_baud = 5208-1	;
	// parameter				max_samp = 2604-1	;	

	
always @(posedge sclk	or	negedge	rst_n)
	if(rst_n==0)
		tx_data_tmp <= 0;
	else if(tx_data_en==1)
		tx_data_tmp <= tx_data;
		
//计数标志
always @(posedge sclk	or	negedge	rst_n)
	if(rst_n==0)
		tx_en <= 0;	
	else if(tx_data_en==1)
		tx_en <=1 ;
	else if(bit_cnt==9 && bit_flag==1)
		tx_en <= 0;
		
//波特率产生
always @(posedge sclk	or	negedge	rst_n)
	if(rst_n==0)
		cnt_baud <= 0;
	else if(tx_en==0)	///****
		cnt_baud <= 0;
	else if(cnt_baud==max_baud)
		cnt_baud <= 0;
	else if(tx_en==1)
		cnt_baud <= cnt_baud + 1'b1;
		
//bit标志产生	
always @(posedge sclk	or	negedge	rst_n)
	if(rst_n==0)
		bit_flag <= 0;	
	else if(cnt_baud==max_samp)	
		bit_flag <= 1;
	else
		bit_flag <= 0;
		
//bit计数
always @(posedge sclk	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'b1;
		
//发送数据
always @(posedge sclk	or	negedge	rst_n)
	if(rst_n==0)
		uart_tx <= 0;
	else if(bit_flag==1)
	case(bit_cnt)
		0:uart_tx <= 0;					//起始位
		1:uart_tx <= tx_data_tmp[0]; 	//先发低位
		2:uart_tx <= tx_data_tmp[1];	
		3:uart_tx <= tx_data_tmp[2];
		4:uart_tx <= tx_data_tmp[3]; 	
		5:uart_tx <= tx_data_tmp[4];	
		6:uart_tx <= tx_data_tmp[5];
		7:uart_tx <= tx_data_tmp[6]; 	
		8:uart_tx <= tx_data_tmp[7];	
		9:uart_tx <= 1'b1;				//停止位
		default:uart_tx <= 1'b1;
	endcase  
endmodule

     UART接收端代码如下

module uart_rx
#(
	parameter max_baud = 5208,
	parameter max_samp = 2604
)
(
	input	wire		sclk			,	//50MHZ
	input	wire		rst_n			,
	input	wire		rx				,
	
	output	reg			rx_data_en		,
	output	reg[7:0]	rx_data
);	
	reg				rx1					;//延时一个时钟
	reg				rx2					;//延时两个时钟
	reg				rx_reg				;//延时三个时钟
	reg				rx_en				;//计数标志
	reg[12:0]		cnt_baud			;//波特率
	reg				bit_flag			;
	reg[3:0]		bit_cnt				;
	reg				rx_data_en_tmp		;
	reg[7:0]		rx_data_tmp			;


//消除亚稳态
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx1 <= 0;
	else 
		rx1 <= rx;
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx2 <= 0;
	else 
		rx2 <= rx1;
		
//边沿检测
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_reg <= 0;
	else 
		rx_reg <= rx2;	
		
//产生计数标志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_en <= 0;
	else if(rx2 == 0 && rx_reg == 1)
		rx_en <= 1;	
	else if(bit_cnt==9)
		rx_en <= 0;	
		
//波特率产生
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		cnt_baud <= 0;
	else if(cnt_baud==max_baud)
		cnt_baud <= 0;
	else if(rx_en==0)
		cnt_baud <= 0;
	else if(rx_en==1)
		cnt_baud <= cnt_baud + 1'b1;
		
//采样产生bit标志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		bit_flag <= 0;
	else if(cnt_baud==max_samp)
		bit_flag <= 1;
	else
		bit_flag <= 0;
		
//bit计数
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		bit_cnt <= 0;
	else if(bit_cnt==9)
		bit_cnt <= 0;
	else if(bit_flag==1)
		bit_cnt <= bit_cnt + 1'b1;
		
//接收数据
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_tmp <= 0;	
	else if(bit_flag==1 && bit_cnt!=0)
		rx_data_tmp <= {rx_reg,rx_data_tmp[7:1]}; //右移
				
//传输完标志
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_en_tmp <= 0;
	else if(bit_cnt==8 && bit_flag==1)
		rx_data_en_tmp <= 1;	
	else
		rx_data_en_tmp	<= 0;
		
//接收数据输出
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data <= 0;	
	else if(rx_data_en_tmp==1)
		rx_data <= rx_data_tmp; 		
		
//接收数据标志输出
always @(posedge sclk	or	negedge rst_n)
	if(rst_n==0)
		rx_data_en <= 0;	
	else
		rx_data_en <= rx_data_en_tmp;		
			
endmodule

     再写一个顶层文件将发送端和接收端例化(当然,使用时也可以直接使用发送代码和接收代码),代码如下

module	uart_top(
	input	wire		sclk			,//50MHZ
	input	wire		rst_n			,
	input	wire		rx_data			,
	
	output	wire	 	tx_data		
);

	wire		rx_flag		;
	wire[7:0]	data_net	;

uart_rx
#(
	.max_baud 	(5208-1		),		//波特率 50M/9600 ~5208
	.max_samp 	(2604-1		)		//在中间采样
)
uart_rx_inst(
	.sclk		(sclk		),
	.rst_n		(rst_n		),
	.rx			(rx_data	),
                                	
	.rx_data_en	(rx_flag	),
	.rx_data    (data_net	)
);	

uart_tx 
#(
	.max_baud 	(5208-1		),		//波特率 50M/9600 ~5208
	.max_samp 	(2604-1		)		//在中间采样
)
uart_tx_inst(
	.sclk		(sclk		),		//50MHZ
	.rst_n		(rst_n		),
	.tx_data_en	(rx_flag	),
	.tx_data	(data_net	),

	.uart_tx	(tx_data	)
);  
endmodule

    这里实现UART的方式是通过计数器的方式,当然也可以通过其他的方式实现(比如状态机等),主要还是要理解UART协议,再理解的基础上,实现起来就会比较轻松。

仿真

    给对应top写一个testbench(仿真激励),如下:

`timescale	1ns/1ns
module	tb_uart_top()		;
	reg		sclk			;
	reg		rst_n			;
	reg		rx_data			;
							
  	wire	tx_data			;
	
	
	
initial
	begin
		sclk = 0			;
		rst_n <= 0			;
		#20		
		rst_n <= 1			;
	end
	
always #5 sclk = ~sclk		;	//50MHZ

initial
	begin
		rx_data <= 1		;
		#200
		rx_byte()	;
	end

// defparam	uart_top_inst.uart_rx_inst.max_baud	= 51;
// defparam	uart_top_inst.uart_rx_inst.max_samp	= 25;
// defparam	uart_top_inst.uart_tx_inst.max_baud	= 51;
// defparam	uart_top_inst.uart_tx_inst.max_samp	= 25;


task	rx_bit(
	input	[7:0]	data	
);
integer	i;
for(i=0;i<10;i=i+1)
begin
	case(i)
		0:rx_data <= 0 ;
		1:rx_data <= data[0] ;
		2:rx_data <= data[1] ;
		3:rx_data <= data[2] ;
		4:rx_data <= data[3] ;
		5:rx_data <= data[4] ;
		6:rx_data <= data[5] ;
		7:rx_data <= data[6] ;
		8:rx_data <= data[7] ;
		9:rx_data <= 1'b1	;
	endcase
	#53000;
end
endtask

task	rx_byte();
integer	j;
for(j=0;j<24;j=j+1)
	rx_bit(j);
endtask


uart_top	uart_top_inst(
	.sclk		(sclk		),
	.rst_n		(rst_n		),
	.rx_data	(rx_data	),

	.tx_data	(tx_data	)	
);
endmodule

    这里通过task发送0-23到UART接收端,接收端接收到数据再通过发送端发送出去。
    截取其中仿真波形如下:

在这里插入图片描述
    以发送23(8‘h17)为例,从图上可以看出接收的二进制数为8‘b11101000,因为在仿真时(testbench)我们先发送的低位,所以需要将接收的数据倒置,即8’b00010111=8’h17,转化为十进制即为23。
    仿真结果正确。
    实际上本段代码可以直接拿来使用,下板测试通过。

    若有相关问题可以互相讨论

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值