FPGA接口系列——UART

FPGA接口系列——UART

一、UART简介

UART是一种采用异步串行通信方式的通用异步收发传输器。这里我们主要弄明白两个问题:①什么是串行通信,与并行通信有什么区别 ②同步串行通信和异步串行通信有什么区别 ③我们常说的UART和 RS232 以及 RS485 又有什么区别?

串行通信和并行通信

串行通信只用一根数据线,将数据转换成一个bit一个bit进行发送;并行通信则可以采用8根或16根数据线,将数据逐个Byte或者Word发送出去。

同步串行通信和异步串行通信:

同步串行通信需要双方在同意时钟的控制下,同步传输数据;异步串行通信的原理大致是Transmitter端在发送数据之前会给出一个跳变信号,随即进行信号的传输,而不需要额外的时钟线,这个时候Transmitter的发送频率就叫做波特率,Receiver端的采样频率一般要比Transmitter端的频率高出4-16倍。

UART,RS232和RS485

串口、COM口是指的物理接口形式(硬件),TTL、RS232、RS485是指的电平标准(电信号)。TTL标准是低电平为0,高电平为1(+5V电平)。RS-232标准是正电平为0,负电平为1(±15V电平),是单端输入输出。RS-485与RS-232类似,但是采用差分信号负逻辑。一般我们见到的串口,有D型9针插头和4针杜邦头两种。

二、数据格式

1.传输时序

1.1

一般常用的数据位是:一个起始位、八个数据位和一个校验位以及一个停止位;关于校验位,奇校验要求校验位和数据位中“1”的结果是一个奇数;一个题外话就是在verilog中使用奇校验应为数据位异或取反。

奇校验 = ~(^data);  偶校验 = ^ data

2.传输速率:波特率

串口通信的速率用波特率表示,它表示麦苗传输二进制数据的位数,单位是bps(位/秒)。常用的波特率有9600、19200、35400、57600以及115200等

项目中使用时,考虑计数器的计数阈值就要结合波特率来考虑。比如波特率设计为9600,那么就是9600bit/s,如果系统时钟是50MHz,(1/9600)÷(1/50M)=5208.333333,也就是说,系统计数器计数到5209则可以完成一个bit数据的transmit。省略校验位,大概一个Byte数据的transmit需要10位(1起始加上8数据加上1停止)

3.程序设计

程序的设计和验证主要分为核心文件和testbench文件的编写,下面简要阐述一下发生的问题:

  1. 注意接口与接口之间的对应,切忌在文件编写例化调用的时候定义错数据的位宽,这种操作往往为造成数据的高阻态
  2. 在编写testbench仿真的时候出现了很多细小的问题,比如数据传输的问题,应该去找到源头进行分析,比如你定义的txt文件为“1 2 3 4”,实际仿真得到接受的字符却是“ 24 56 78 21”这种,就要考虑字符的失序,时序可能存在没有对齐的操作,像我这一步就是因为没有留出足够的空闲时间(因为前面有打过三拍)造成了数据的丢失与失序。

4.上板验证

上板验证中出现了一个待解决的问题

1711459258205

这里主要的变动是因为:我将原作者 咸鱼FPGA 代码中的(1起始+8数据+0.5停止)改成了(1起始+8数据+1停止)然后就发生了失序,这是一个待解决的问题,主要的猜想可能是,设计0.5个停止位就是空出时间以待接受新的数据?少了这0.5个余量之后可能发生了失序。

module uart_rx 
#(
parameter			CNT_MAX		=	'd5208	,
parameter			CNT_HALF	=	'd2603	
)
(
	//system signals
	input				clk					,  //系统时钟 50M
	input				rst_n				,
	//input and output signals
	input		wire				din				,
	output		reg		[7:0]		dout			,
	output		reg					da_vld			
);
//========================================================================\
// =========== Define Parameter and Internal signals =========== 
//========================================================================/

reg 				[2:0]	din_reg			; // 寄存器打拍信号,用于消除亚稳态
wire						din_neg			; // 下降沿检测信号
reg 						flag			; // 状态检测信号
wire						flag_convert	; // 数据转换标志信号
reg 				[12:0]	cnt_50M			; // 系统时钟计数器
reg 				[3:0]	cnt_bit			; // 传输位数计数器
reg 				[7:0]	data			; // 缓存PC机传输过来的数据

wire						begin_cnt1			;
wire						begin_cnt2			;
wire						end_cnt1			;
wire						end_cnt2			;
//=============================================================================
//****************************     Main Code    *******************************
//=============================================================================

//=============================================================================
//==			消除亚稳态加上下降沿检测
//=============================================================================

assign din_neg = din_reg[2] & (~din_reg[1]);

always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		// reset
		din_reg <= 1'b0;
	end
	else begin
		din_reg <= {din_reg[1],din_reg[0],din};
	end
end
//=============================================================================
//==			接受状态指示
//=============================================================================
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		// reset
		flag <= 1'b0;
	end
	else if (din_neg) begin
		flag <= 1'b1;
	end
	else if (end_cnt2)begin
		flag <= 1'b0;
	end
end
//=============================================================================
//==			波特率计数
//=============================================================================
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		// reset
		cnt_50M <= 1'b0;
	end
	else if (begin_cnt1) begin
		if (end_cnt1) 
		    begin
		        cnt_50M <= 1'b0;
		    end
		else 
		    begin
		        cnt_50M <= cnt_50M + 1'b1;
		    end
	end
end

assign begin_cnt1 = flag;
assign end_cnt1 = cnt_50M == CNT_MAX || end_cnt2;
//=============================================================================
//==			1起始8数据和0.5停止
//=============================================================================
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		// reset
		cnt_bit <= 1'b0;
	end
	else if (begin_cnt2) begin
		if (end_cnt2) 
		    begin
		        cnt_bit <= 1'b0;
		    end
		else 
		    begin
		        cnt_bit <= cnt_bit + 1'b1;
		    end
	end
end

assign begin_cnt2 = end_cnt1;
assign end_cnt2 = (cnt_50M == CNT_MAX) && (cnt_bit == 'd9);
//=============================================================================
//==			数据转换与缓存
//=============================================================================
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		// reset
		data <= 1'b0;
	end
	else if (flag_convert) begin
		data[cnt_bit-1] <= din_reg[2];
	end
end
assign flag_convert = flag && (cnt_bit > 0) && (cnt_bit < 9) && (cnt_50M == CNT_HALF);
//=============================================================================
//==			输出数据
//=============================================================================
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		// reset
		dout <= 1'b0;
	end
	else if (end_cnt2) begin
		dout <= data;
	end
end

always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		// reset
		da_vld <= 1'b0;
	end
	else if (end_cnt2) begin
		da_vld <= 1'b1;
	end
	else begin
		da_vld <= 1'b0;
	end
end
endmodule

参考资料:咸鱼FPGA

posted @ 2024-03-26 21:29  齐迩  阅读(28)  评论(0编辑  收藏  举报