FPGA学习[4]——异步串口的接收实验与环回测试

本文深入探讨了FPGA中异步串口的接收机制,包括波特率计数器的设置、采样信号的时机选择及如何克服信号反射与边沿抖动带来的问题。介绍了在FPGA中实现串行转并行的细节,如计数器设计、使能信号处理以及数据接收过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异步串口的接收跟发送不一样,本质上串口的发送属于并行转串行,而接收则属于串行转并行

跟发送一样,接收也需要约定波特率与设置波特率计数器,使得FPGA能够按照约定的波特率接收数据。

在FPGA学习[3]里面,已经计算了计数器计数值与波特率之间的关系
countbaud=fclkbaudcount_{baud} = \frac{f_{clk}}{baud}countbaud=baudfclk
在实际程序中,我们在计数器counter的值在波特率计数值中间的时候采样接收电平信号

为什么要在计数器中间处采样信号?

在这里插入图片描述
当信号传输路径上存在杂散电容与引线电感时,在信号的跳变沿会出现反射现象,这个现象在传输路径较长时非常明显。这种跳变沿上发生的过冲、下冲或振铃有时候可能干扰正常数据信号的采集。(图示是吉布斯现象,并非信号反射)

对于串口来说,当波特率较高的时候,很容易因为边沿抖动造成数据接收异常,特别是糟糕的PCB设计更令此类问题普遍发生。对于普通单片机而言,无法支持如此高的波特率主要原因有以下几点

  • 时钟频太低
  • PCB设计不当
  • 传输路径糟糕
  • 电平转换芯片或其他芯片无法支持

对于STM32单片机,芯片设计了过采样来解决因为边沿电平不稳定带来的问题,具体思路如下:

使用高于波特率若干倍的时钟,在时钟采样时钟的中间位置读取信号电平。为了内部逻辑简单一般过采样率是2的幂次:4,8,16等。

对于FPGA而言使用同步逻辑设计的串口接收模块采样率固定为fclkf_{clk}fclk,这一点比大多数单片机有先天的优势,使用PLL将时钟倍频至150MHz以上也是可以做到的,因此设计得当的FPGA能轻松做到较高的波特率。然而无论如何提高波特率,串口通信仍然是低速的通信。

Verilog HDL实现

1.输入信号处理

always @(posedge clk or negedge nrst) begin
	if(!nrst) begin
		rx_in <= 1'b1;
	end
	else begin
		rx_in <= rx;
	end
end

这一步将输入信号打一拍处理,可以缓解信号的亚稳态,便于综合与约束。

2.接收起始标志处理

always @(posedge clk or negedge nrst) begin
	if(!nrst) begin
		rx_start <= 1'b0;
	end
	else begin
		if(rx_in == 1'b0 && rx_start == 1'b0)
			rx_start <= 1'b1;
		else if(data_count == 5'd9 && count == baud_count - 1)
			rx_start <= 1'b0;
		else
			rx_start <= rx_start;
	end
end

当接收到起始位,将接收标志位置位,表明接收开始;当接收到停止位,当波特率计数器计数到最后一位,接收标志复位。

3.波特率计数器

always @(posedge clk or negedge nrst) begin
	if(!nrst) begin
		count <= 16'd0;
	end
	else begin
		if(rx_start) begin
			if(count < baud_count)
				count <= count + 16'b1;
			else
				count <= 16'd0;
		end
		else 
			count <= 16'd0;
	end
end

当接收标志位值位,波特率计数器开始计数。

4.接收数据计数器

always @(posedge clk or negedge nrst) begin
	if(!nrst) begin
		data_count <= 5'd0;
	end
	else begin
		if(count == baud_count - 1) begin
			if(data_count < 5'd9)
				data_count <= data_count + 1'b1;
			else
				data_count <= 5'd0;
		end
		else 
			data_count <= data_count;
	end
end

波特率计数器每记满一次,数据计数器加一,数据计数器记录了发送数据的个数。

5.使能信号处理

always @(posedge clk or negedge nrst) begin
	if(!nrst) begin
		en <= 1'b0;
	end
	else begin
		if(data_count == 5'd9 && count == 16'd0)
			en <= 1'b1;
		else 
			en <= 1'b0;
	end
end

这里使能信号的处理非常特殊。由于接收是一个串行转并行的过程,使能信号的发出需要在FIFO满以后才可以发出
当FIFO存满以后使能信号持续一个时钟的高电平告知存满。

6.数据接收

always @(posedge clk or negedge nrst) begin
	if(!nrst) begin
		data <= 8'b0;
	end
	else begin
		if(count == baud_count / 2)
		case(data_count)
			5'd1:data[0]<=rx_in;
			5'd2:data[1]<=rx_in;
			5'd3:data[2]<=rx_in;
			5'd4:data[3]<=rx_in;
			5'd5:data[4]<=rx_in;
			5'd6:data[5]<=rx_in;
			5'd7:data[6]<=rx_in;
			5'd8:data[7]<=rx_in;
			default:data<=data;
		endcase 
		else 
			data <= data;
	end
end

这是串行转并行的核心。

环回测试

在顶层模块中例化了发送与接收两个模块。

module top_module(
input 					clk,
input 					nrst,
input					rx,
output 					tx
);

wire [7:0] data;
wire en;

tx_module 
#(.baud(921600))
tx_module_inst(
.clk					(clk),
.nrst					(nrst),
.en						(en),
.data					(data),
.tx						(tx)
);

rx_module 
#(.baud(921600))
rx_module_inst(
.clk					(clk),
.nrst					(nrst),
.rx						(rx),
.en						(en),
.data					(data)
);
endmodule 

这里设置了一个较高的波特率921600以测试收发模块。
对于一个通信系统,将发送与接收端短接进行测试,业界称为环回测试(loopback),笔者看到不少朋友都错写成了回环测试,这个说法其实是不正确的。

在这里插入图片描述
为了测试效果,发了1000位圆周率,对比接收正确。至此异步串口接收与环回测试成功。

### FPGA单片机之间通过异步串口进行通讯的方法 #### 1. 异步串口通信基础 异步串口通信的核心在于 UART(Universal Asynchronous Receiver/Transmitter),其功能是在数据发送时将并行数据转换为串行数据传输,而在接收时则将接收到的串行数据重新转化为并行数据[^4]。这种机制使得两个设备可以通过简单的信号线实现高效的数据交换。 #### 2. 单片机中的UART模块 对于单片机而言,通常已经集成了硬件级别的UART模块,可以直接用于串口通信。例如51系列单片机内部自带UART,能够轻松完成其他设备的串口通信任务[^1]。这意味着在单片机端无需额外设计复杂的逻辑电路即可支持基本的串口操作。 #### 3. FPGA中的UART实现 相比之下,FPGA并不具备内置的UART单元,因此需要开发者自行构建相应的UART模块来满足需求[^2]。具体来说,这涉及编写VHDL或者Verilog HDL代码定义发送器和接收器的功能行为模式,并确保它们按照预定参数正常运作。例如,在设定好特定波特率之后(如常见的9600bps),就需要精确计算每比特持续时间以便正确同步收发两端的信息流[^3]。 #### 4. 数据帧结构考虑 无论是由FPGA还是单片机构建起来的UART系统都遵循相同的标准数据帧格式来进行有效载荷传递。典型情况下包括起始位、若干个数据位(一般为7至8位)、可能存在的奇偶校验位以及结束通信过程所需的停止位组成整个消息包。这些组成部分共同决定了最终实际传输所需占用带宽大小及其可靠性水平等方面特性。 #### 5. 软件驱动层面上的支持 除了硬件连接之外还需要注意软件层面如何配置各自平台上的相应寄存器等内容以激活启用各自的UART外设资源;同时也要考虑到可能出现的各种异常情况处理策略等问题解决方案的设计实施细节部分工作量也不容忽视。 ```verilog module uart_tx #(parameter BAUD_RATE = 9600, parameter CLOCK_FREQ = 50_000_000)( input wire clk, input wire reset_n, input wire tx_start, input wire [7:0] data_in, output reg tx_done, output reg tx_out); // Internal signals declaration and logic implementation here... endmodule ``` 以上展示了一个简化版的UART发送方RTL级描述示例片段,展示了关键要素之一——定时计数器控制流程图的一部分概念示意形式。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大困困瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值