异步串口通信和逻辑分析仪

UART协议:

UART和RS232(单端全双工)RS422(差分全双工)RS485(差分半双工)

UART的全称叫做通用异步收发传输器。将数据在串行通信和并行通信间的传输转换。通俗的讲就是把多比特的数据转化为单比特的数据,或者把单比特的数据转化为多比特的数据。工作原理是将数据的每个bit一位一位传输

UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。

RS232是UART的一种,是目前最常用的一种串行通讯接口,用于pc机跟外部板级通信。有两根线,分别是rx和tx,这两根线都是1比特位宽的。其中rx是接收线,tx是发送线。

rx,位宽为1比特,pc机通过串口往FPGA发8比特数据时,FPGA通过串口线rx一位一位地接收,从最低位到最高位依次接收,最后在FPGA里面位拼接成8比特数据。

tx,位宽为1比特,FPGA通过串口往pc机发8比特数据时,FPGA把8比特数据通过tx线一位一位的传给pc机,从最低位到最高位依次发送,最后上位机通过串口助手把这一位一位的数据位拼接成8比特数据。、

在不发送或者不接收数据的情况下,rx和tx处于空闲状态,此时rx和tx线都保持高电平(1),如果有数据传递,首先会有一个起始位(O),然后是8比特的数据位,接着有1比特的停止位(1),如果停止位以后不再发数据,将进入空闲状态,否则又将数据线拉低(进入起始位状态)。

波特率:在串口通信时的速率,单位时间内载波变化的次数,这里选用的是9600Bd,即发送一比特数据需要的时间为1/9600秒。

用串口发送或者接收数据(起始位、数据位、停止位)时,每发送或者接收一位数据的时间都需要1个波特,即1/9600秒。

串口发送或者接收一比特数据的时间为一个波特(1/9600),因此如果用50M的系统时钟来计数﹐就需要记数cnt=(1/9600s)/20ns~5208个系统时钟,才再次发送或者接收下一个数据。

上位机通过串口发8比特数据时,会自动在发8位有效数据前发一个波特时间的起始位,也会自动在发完8位有效数据后发一个停止位。同理,串口助手接收fpga发送的数据前,必须检测到一波特的起始位才会接收数据,接收完数据后,再接收一个停止位,所以FPGA通过串口除了发数据以外,还要发起始位和停止位。

RS232测试回环
复制代码
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2025 All rights reserved
// -----------------------------------------------------------------------------
// Author : lvjitao lvjitao_o@163.com
// File   : top_uart.v
// Create : 2025-10-09 10:15:12
// Revise : 2025-10-09 10:42:54
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
`timescale  1ns/1ps



module top_uart(
	input	wire		rx,
	output	wire		tx

    );



assign tx = rx;

endmodule

set_property PACKAGE_PIN T11 [get_ports rx]
set_property IOSTANDARD LVCMOS18 [get_ports rx]
set_property PACKAGE_PIN T10 [get_ports tx]
set_property IOSTANDARD LVCMOS18 [get_ports tx]

首先画出三个输入信号,必不可少的两个输入信号是时钟和复位,另一个是串行输入数据rx,如图 34‑14所示,我们发现rx串行数据一开始直接打了两拍,就是经过了两级寄存器,理论上我们应该按照串口接收数据的时序要求找到rx的下降沿,然后开始接收起始位的数据,但为什么先将数据打了两拍呢?那就要先从跨时钟域会导致"亚稳态"的问题上说起。

由于在PC机中波特率和rx信号是同步的,而rx信号和FPGA的系统时钟sys_clk是异步的关系,我们此时要做的是将慢速时钟域(PC机中的波特率)系统中的rx信号同步到快速时钟域(FPGA中的sys_clk)系统中,所使用的方法叫电平同步,俗称"打两拍法"。

rx信号进入FPGA后会首先经过一级寄 存器,出现如图 所示的亚稳态现象,导致rx_reg1信号的状态不确定是0还是1,就会受其影响使其他相关信号做出不同的判断,有的判断到"0"有的判断到"1",有的也进入了亚稳态并产生连锁反应,导致后级相关逻辑电路混乱。为了避免这种情况,rx信号进来后首先进行打一拍的处理,打一拍后产生rx_r eg1信号。但rx_reg1可能还存在低概率的亚稳态现象,为了进一步降低出现亚稳态的概率,我们将从rx_reg1信号再打一拍后产生rx_reg2信号,使之能够较大概率保证rx_reg2信号是0或者1中的一种确定情况,这样rx_reg2所影响的后级电路就都是相对稳定的了。

但是大家一定要注意:打两拍后虽 然能让信号稳定到0或者1中确定的值,但究竟是0还是1却是随机的,与打拍之前输入信号的值没有必然的关系。

综合rx时序图:

当bit_cnt计数器的 计数值为1时说明第一个有用数据已经接收到了,刚好剔除了起始位,就可以进行移位了。注意移位的条件,要在bit_cnt计数器的计数值为1到8区间内且bit_flag取数标志信号同时为高时才能移位,也就是移动7次即可,接收最后1bit有用数据时就不需要再进行移位了。当移位7次后1bit的串行数据已经变为8 bit的并行数据了,此时产生一个移位完成标志信号rx_flag。

x_data信号是参与移位的数据,在移位的过程中数据是变动的,不可以被后级模块所使用,而可以肯定的是在移位完成标志信号rx_flag为高时,rx_data信号一定是移位完成的稳定的8bit有用数据。如图 34 ‑25所示,此时我们当移位完成标志信号rx_flag为高时让rx_data信号赋值给专门用于输出稳定8bit有用数据的po_data信号就可以了,但rx_flag信号又不能作为po_data信号有效的标志信号,所以需要将rx_flag信号再打一拍。最后输出的有用8bit数据为po_data信号和伴随 po_data信号有效的标志信号po_flag信号。

复制代码
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2025 All rights reserved
// -----------------------------------------------------------------------------
// Author : lvjitao lvjitao_o@163.com
// File   : top_uart.v
// Create : 2025-10-09 10:15:12
// Revise : 2025-10-09 18:42:11
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
`timescale  1ns/1ps



module top_uart(
	input	wire		rx,
	input	wire		sclk,
	input	wire		s_rst_n,
	output	wire		tx

    );

wire 		po_flag;
wire [7:0]	po_data;

 assign tx = 1;

	uart_rx #(
			.MAX_BAUD_CNT(9600)
		) inst_uart_rx (
			.sclk    (sclk),
			.s_rst_n (s_rst_n),
			.rx      (rx),
			.po_flag (po_flag),
			.po_data (po_data)
		);


ila_0 ila_inst (
	.clk(sclk), // input wire clk


	.probe0({po_flag, po_data}) // input wire [15:0] probe0
);

endmodule

set_property PACKAGE_PIN T11 [get_ports rx]
set_property IOSTANDARD LVCMOS18 [get_ports rx]
set_property PACKAGE_PIN T10 [get_ports tx]
set_property IOSTANDARD LVCMOS18 [get_ports tx]

set_property PACKAGE_PIN W19 [get_ports s_rst_n]
set_property IOSTANDARD LVCMOS18 [get_ports s_rst_n]

set_property PACKAGE_PIN U14 [get_ports sclk]
set_property IOSTANDARD LVCMOS18 [get_ports sclk]

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2025 All rights reserved
// -----------------------------------------------------------------------------
// Author : lvjitao lvjitao_o@163.com
// File   : tb_uart_rx.v
// Create : 2025-10-09 15:05:53
// Revise : 2025-10-09 15:41:07
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
`timescale  1ns/1ps


module tb_uart_rx(

    );
reg sclk, s_rst_n;
wire po_flag;
wire [7:0] po_data;

reg [8:0] men[15:0];
reg 		rx;


initial begin
	sclk = 0;
	s_rst_n = 0;
	repeat(10) @(posedge sclk);
	s_rst_n = 1;
end


initial begin
	$readmemh("./data.mif", men);
end


initial begin
	repeat(100) @(posedge sclk);
	gen_rx_byte();
end

task gen_rx_byte;
	integer i;
	begin
		for (i=0; i<16; i=i+1)
		begin
			gen_rx_bit(men[i]);
		end
	end
endtask


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



	uart_rx #(
			.MAX_BAUD_CNT(MAX_BAUD_CNT)
		) inst_uart_rx (
			.sclk    (sclk),
			.s_rst_n (s_rst_n),
			.rx      (rx),
			.po_flag (po_flag),
			.po_data (po_data)
		);



endmodule
逻辑分析仪

异步复位进入同步后同步复位退出电路设计:

(*ASYNC_REG="true"*)是XilinxFPGA设计中用于明确标识异步同步寄存器的关键属性,通过硬件布局优化和仿真控制提升跨时钟域信号的可靠性。实际使用时需结合同步电路设计规范

使其部署在一个最小单元。

rx:

复制代码
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2025 All rights reserved
// -----------------------------------------------------------------------------
// Author : lvjitao lvjitao_o@163.com
// File   : uart_tx.v
// Create : 2025-10-09 19:55:12
// Revise : 2025-10-09 20:15:35
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
`timescale  1ns/1ps


module uart_tx #(
	parameter MAX_BAUD_CNT = 5200 - 1
	)(
	input	wire		sclk,
	input	wire		s_rst_n,
	input	wire[7:0]	pi_data,
	input	wire		pi_flag,
	output	reg		tx=1

    );

reg[7:0]	data_reg;
reg 		tx_flag;
reg[15:0]	cnt_baud;
reg 		bit_flag;
reg[3:0]	bit_cnt;
reg[8:0]	shift_flag = 1;

always @(posedge sclk) begin
	if (pi_flag == 1) begin
		data_reg <= pi_data;
	end
end
 
always @(posedge sclk ) begin
	if (s_rst_n == 0) begin
		// reset
		tx_flag <= 0;
	end
	else if (bit_flag == 1 && bit_cnt == 'd0) begin
		tx_flag <= 0;
	end
	else if (pi_flag == 1'b1) begin
		tx_flag <= 1;
	end
end

always @(posedge sclk ) begin
	if (s_rst_n == 1'b0) begin
		// reset
		cnt_baud <= 0;
	end
	else if (tx_flag == 1'b1) begin
		if (cnt_baud == MAX_BAUD_CNT) begin
			cnt_baud <= 0;
		end
		else begin
			cnt_baud <= cnt_baud + 1;
		end
	end
	else begin
		cnt_baud <= 'd0;
	end
end


always @(posedge sclk ) begin
	if (s_rst_n == 0) begin
		// reset
		bit_flag <= 0;
	end
	else if (cnt_baud == MAX_BAUD_CNT - 1) begin
		bit_flag <= 1;
	end
	else begin
		bit_flag <= 0;
	end
end

always @(posedge sclk ) begin
	if (s_rst_n == 0) begin
		// reset
		bit_cnt <= 0;
	end
	else if (bit_flag == 1'b1) begin
		bit_cnt <= bit_cnt + 1'b1;
	end
end

always @(posedge sclk ) begin
	if (s_rst_n) begin
		// reset
		tx <= 1;
	end
	else if(tx_flag == 1) begin
		case(bit_cnt)
			4'd0:tx <= 1'b0;
			4'd1:tx <= data_reg[0];
			4'd2:tx <= data_reg[1];
			4'd3:tx <= data_reg[2];
			4'd4:tx <= data_reg[3];
			4'd5:tx <= data_reg[4];
			4'd6:tx <= data_reg[5];
			4'd7:tx <= data_reg[6];
			default: tx <= 1'b1;
		endcase
	end
	else begin
		tx <= 1;
	end
end



//相同写法
// always @(posedge sclk ) begin
// 	if (s_rst_n == 0) begin
// 		// reset
// 		shift_flag <= 1;
// 	end
// 	else if (pi_flag == 1) begin
// 		shift_flag <= {pi_data, 1'b0};
// 	end
// 	else if (bit_flag == 0) begin
// 		shift_flag <= {1'b1, shift_flag[8:1]};
// 	end
// end


// always @(posedge sclk ) begin
// 	if (s_rst_n == 0) begin
// 		// reset
// 		tx<=1'b1;
// 	end
// 	else begin
// 		tx <= shift_flag[0];
// 	end
// end

endmodule
相关推荐
AOI小白新手上路3 分钟前
DRSformer·论文蒸馏笔记:可学习 Top-k 稀疏注意力去雨网络
网络·笔记·学习
你的强来了9988 分钟前
WCHToolKit场景实战
网络·串口·调试·usb·ble·分析
嵌入式-老费8 分钟前
esp32开发与应用(题外话之SoC核心板Layout外包)
嵌入式硬件
Mikko715 分钟前
Linux 磁盘满了但 du 找不到?LVM 扩容 df 不变?fstab 写错开不了机?三台真机实测磁盘排查
linux·运维·centos
蓝胖的四次元口袋21 分钟前
Jenkins知识梳理(2)
运维·jenkins
神一样的老师24 分钟前
Windows 中文用户名导致 ccache 编译失败
嵌入式硬件
yumgpkpm30 分钟前
Acceldata ODP 3.3.6.4 vs CDP Private Cloud Base 7.3.2 对比
大数据·运维·服务器·hadoop·华为·zookeeper·hbase
Liuqy-0540 分钟前
特殊进程——孤儿、僵尸、守护进程
linux·运维·服务器
Brilliantwxx41 分钟前
【STM32】 工程实战 WS2812B 全彩 LED 灯控制(DMA 驱动)
stm32·单片机·嵌入式硬件·mcu
科芯创展1 小时前
10~120V宽压1.5A降压DC-DC XZ1830技术特性与应用解析
单片机