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编译、仿真和查看波形了,

相关推荐
ShiMetaPi21 小时前
操作【GM3568JHF】FPGA+ARM异构开发板 使用指南:蓝牙
arm开发·嵌入式硬件·fpga开发·rk3568
知识充实人生1 天前
静态时序分析详解之时序路径类型
fpga开发·时序路径·关键路径
9527华安2 天前
Xilinx系列FPGA实现DP1.4视频收发,支持4K60帧分辨率,提供2套工程源码和技术支持
fpga开发·音视频·dp1.4·4k60帧
cycf2 天前
高速接口基础
fpga开发
forgeda2 天前
从Vivado集成Lint功能,看FPGA设计的日益ASIC化趋势
fpga开发·vivado·lint·eco·静态检查功能
hexiaoyan8273 天前
国产化FPGA开发板:2050-基于JFMK50T4(XC7A50T)的核心板
fpga开发·工业图像输出·vc709e板卡·zynq 通用计算平台·模拟型号处理
雨洛lhw3 天前
The Xilinx 7 series FPGAs 设计PCB 该选择绑定哪个bank引脚,约束引脚时如何定义引脚电平标准?
fpga开发·bank·电平标准
红糖果仁沙琪玛3 天前
FPGA ad9248驱动
fpga开发
minglie13 天前
XSCT/Vitis 裸机 JTAG 调试与常用命令
fpga开发