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

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

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

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

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

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

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

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

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

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

对于FPGA而言使用同步逻辑设计的串口接收模块采样率固定为 f c l k f_{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位圆周率,对比接收正确。至此异步串口接收与环回测试成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大困困瓜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值