Zynq开发实践(FPGA之uart接收)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

uart之所以被称之为最简单的通信协议,一方面是因为它没有时钟信号,另外一方面是因为接收和发送是两条线分开来的。它不像一些协议,例如iic、sdio等等,数据位既可以用做输入、也可以用做输出。uart这样设计起来,就会简单很多。不过,很多sdio协议,也支持spi输入输出,当然这是后话了。

1、uart接收的顺序

前面说过,uart的发送一般是10个bit,即起始位、数据位和停止位,默认没有校验位。起始位就是从1拉到0,而停止位则是恢复为1。数据位的发送,是从低到高依次发送。那么这里接收其实也是一样的,首先接收起始位,然后是数据位,最后是停止位。数据位也是从低到高接收。

2、uart接收的时机

因为没有clock作为保护,所以这里这能靠数据的边沿触发作为接收的一个时机。平时uart的tx都是高电平,因此接收的时候如果发现从高到低拉低,那就是代表数据开始进行发送,这边就要做好接收的准备了。

3、uart接收的波特率

即使双方商量好了接收的起始时间,但是接收的速度还是没有办法定下来的,因此这种发送、接收的速度只能靠提前的约定来进行处理。比如大家都约定好,发送、接收的帧率是4800、9600,或者是115200。如果没有按照约定的频率来发送、接收,就会出现两种情况,要么采样慢了漏掉数据,要么是采样快了,采样了多余的数据。

4、采样的时机

实际uart的采样频率要比fpga时钟慢很多,一个周期内理论上什么时候采样都可以。不过为了保证数据的稳定性,我们一般还是在采样周期的中间那个点,去获取对应的bit信息,这是最好的处理方式。

5、uart接收的实现

本身uart的实现不复杂。需要注意的主要就是状态机和开始接收、接收时机的处理。首先是设计好状态机,

复制代码
always@(posedge clk or negedge rst)
	if(!rst)
		state <= 3'h0;
	else
		state <= next_state;

always@(*)
	if(!rst)
		next_state = 3'h0;
	else if(state == 3'h0 && (rx2 && !rx1))
		next_state = 3'h1;
	else if(state == 3'h1 && counter == HALF_CYCLE) // skip first data
		next_state = 3'h2;
	else if(state == 3'h2 && counter == HALF_CYCLE &&  num == 7) // middle 8 data
		next_state = 3'h3;
	else if(state == 3'h3 && counter == HALF_CYCLE) // last data
		next_state = 3'h0;
	else
		next_state = state;

处理好了状态机,就要弄清楚什么时候开始接收,并且接收的周期是多少,

复制代码
always@(posedge clk or negedge rst)
	if(!rst) begin
		rx1 <= 1'b1;
		rx2 <= 1'b1;
	end 
	else begin
		rx1 <= rx;
		rx2 <= rx1;
	end

always @(posedge clk or negedge rst)
	if(!rst) // reset state
		counter <= 16'h0;
	else if(state != 3'h0) begin // first data and middle 8 data
		if((counter == FULL_CYCLE) || (state == 3'h3 && counter == HALF_CYCLE))
			counter <= 16'h0;
		else
			counter <= counter + 1;
	end

这些都定下来之后,最后就是接收数据、接收数量以及提示什么时候数据可用。

复制代码
always @(posedge clk or negedge rst)
	if(!rst)
		num <= 3'h0;
	else if(state == 3'h2 && counter == HALF_CYCLE) // total 8 data, select data from the middle of the wave
		num <= num + 1;

always @(posedge clk or negedge rst)
	if(!rst)
		out_data <= 8'h0;
	else if(state == 3'h2 && counter == HALF_CYCLE) // save data to reg
		out_data[num] <= rx2;

always @(*)
	if(!rst)
		data_valid = 1'b0;
	else if(state == 3'h3 && counter == HALF_CYCLE)
		data_valid = 1'b1;
	else
		data_valid = 1'b0;

一切都没有问题之后,就可以添加testbench,做好仿真测试即可。这是准备的testbench文件,最重要的部分就是rx下降沿的设计,因为只有有了明显的下降沿,才会触发状态机的运转。

复制代码
`timescale 1ns/1ps

module test();

reg clk;
reg rst;
reg rx;
wire data_valid;
wire[7:0] data;

initial begin 
	clk = 0;
	rst = 1;
	rx = 1;
	#10 rst = 0;
	#30 rst = 1;
	#50 rx = 0;
	#300 rx = 1;
	#10000 $finish;
end

initial begin
	while(1)	
		clk = #5 ~clk;
end

uart_rx uart_rx(
	.clk(clk),
	.rst(rst),
	.rx(rx),
	.data_valid(data_valid),
	.out_data(data)
);

initial begin
	$dumpfile("hello.vcd");
	$dumpvars(0, test);
end

endmodule

没有什么问题之后,就可以直接利用iverilog、vvp、gtkwave编译、仿真和查看波形了,

相关推荐
Hello-FPGA16 小时前
基于RFSOC FPGA ZCU216 的CoaXPress 采集demo
fpga开发
9527华安18 小时前
FPGA基于ADV7182解码PAL视频,Video Processing Subsystem去隔行,提供6套工程源码和技术支持
fpga开发·音视频·pal·adv7182
hahaha601618 小时前
数字图像时域滤波或者3D降噪算法FPGA实现
算法·计算机视觉·fpga开发
GateWorld19 小时前
Xilinx 7系列FPGA时钟网络详解
fpga开发·时钟网络·xilinx 7系列
szxinmai主板定制专家1 天前
X86 工控机 vs RK3588 异构主控:AMR 控制器 BOM、散热、长期运维对比
运维·stm32·单片机·嵌入式硬件·fpga开发·am62x
szxinmai主板定制专家2 天前
AMR 项目落地:机器人控制器硬件定制化需求复盘
arm开发·单片机·fpga开发·系统架构·机器人·am62x
FPGA小迷弟3 天前
FPGA 与卫星:从星载硬件到在轨智能的完整技术解析
fpga开发·架构·fpga·1024程序员节·无线电
NamoAmitabha1283 天前
CPU 内部结构是什么?---deepseek
fpga开发
abc light3 天前
ARM 裸机开发(6)——时钟树与定时器
arm开发·单片机·fpga开发
156082072193 天前
JFMZQ28DR、JFMZQ68DR、RFsoc XCZU47DR、XCZU48DR 信号处理板
fpga开发·信号处理