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

相关推荐
千宇宙航3 天前
闲庭信步使用图像验证平台加速FPGA的开发:第三十课——车牌识别的FPGA实现(2)实现车牌定位
图像处理·计算机视觉·fpga开发·车牌识别
灵风_Brend3 天前
最大最小延时约束
fpga开发
li星野3 天前
打工人日报#20250930
笔记·程序人生·fpga开发·学习方法
9527华安3 天前
FPGA实现SRIO图像视频传输,基于Serial Rapidlo Gen2,提供6套工程源码和技术支持
图像处理·fpga开发·音视频·srio·xilinx
ThreeYear_s3 天前
【FPGA+DSP系列】——(1)CCS创建工程+LED点亮
fpga开发
范纹杉想快点毕业4 天前
单片机开发中的队列数据结构详解,队列数据结构在单片机软件开发中的应用详解,C语言
c语言·数据库·stm32·单片机·嵌入式硬件·mongodb·fpga开发
小灰灰的FPGA4 天前
9.9元奶茶项目:matlab+FPGA的cordic算法(向量模式)计算相位角
算法·matlab·fpga开发
电子凉冰4 天前
FPGA强化-DDS信号发生器
fpga开发
上园村蜻蜓队长4 天前
ARM芯片架构之DAP:AXI-AP 技术详解
arm开发·fpga开发·架构·rtl
一只嵌入式爱好者4 天前
Xilinx FPGA上电和配置
fpga开发·硬件架构·xilinx