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

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
相关推荐
2401_868534785 小时前
分析RTOS与Linux有什么区别
linux·运维·服务器
richxu202510016 小时前
学完了江科大STM32,下一步该怎么学?
stm32·单片机·嵌入式硬件·学习
Peace6 小时前
【Nginx】
linux·运维·nginx
Deitymoon6 小时前
STM32——SPI通信
stm32·单片机·嵌入式硬件
网络与设备以及操作系统学习使用者6 小时前
Linux与Windows核心差异深度解析
linux·运维·网络·windows·学习
2035去旅行7 小时前
WIFI传输带宽
arm开发·嵌入式硬件
linbaiwan6667 小时前
内置VDD稳压管减少外围元件的三款LED驱动芯片集成度
单片机·嵌入式硬件
筠筠喵呜喵7 小时前
Linux CPU性能优化:D状态和Z状态排查与处理
linux·服务器·性能优化
Flash.kkl7 小时前
网络层协议IP、数据链路层、NAT详解
服务器·网络·网络协议·tcp/ip
三品吉他手会点灯8 小时前
STM32F103 学习笔记-21-串口通信(第6节)-串口发送命令控制RGB灯
笔记·stm32·单片机·嵌入式硬件·学习