FPGA串口接收单字节程序

对FPGA串口接收程序用自己的理解做一个总结,有问题还请指正。UART即通用异步收发器,俗称串口。总结几个要点。1、信号线个数,从电路层面来看总共有两根线,分别是接收数据线rx和发送数据线tx。2、数据格式,如果不算上奇偶校验位等等,串口数据常用格式为1位起始位(为0)+8位数据位+1位停止位(为1)。rx和tx空闲态为1,当从1拉低到0时就认为数据传输开始了。3、串口波特率,简单来说就是传输一个bit所需要的时间,比如波特率为9600,那么传输一个bit的时间就是1秒钟/9600。4、串口先传输低位,再传输高位。

知道了串口协议的构成,就可以开始写FPGA串口接收程序了。整体思路如下:1、在检测到接收数据线rx产生了一个下降沿,即从空闲态1拉低成了0,就认为有数据传输过来了。2、在检测到下降沿后就开始10个bit的接收,每个bit的时间根据系统时钟频率和串口波特率即可算出一个bit需要多少个系统时钟周斯,定义一个计数器对一个bit的传输时间计时,并选择在最大计数值的中间值进行采样(中间的话数据相对稳定)。3、再定义一个计数器对传输的位数进行计数,每当一个bit时间计数器达到最大计数值就加1,当加到9(0-9共10个bit)说明一个字节接收完毕。在传输位数计数器的值为1-8且bit传输时间计数器为中间值时,把采样值赋给一个8位reg的对应位即可。

以下是自己手敲的程序,写了些注释,程序功能已验证。

module uart_rx
#(
	parameter CLK_FRE = 50_000_000,//系统时钟频率为50M
	parameter BPS = 115200//串口波特率
)
(
	input wire sys_clk,//系统时钟
	input wire sys_rst_n,//系统复位
	input wire rx,//串口接收数据线
	
	
	output reg [7:0] rx_byte,//缓存接收到的一个字节数据
	output reg rx_done//串口接收完成信号
);
reg [3:0] bit_cnt;//用于记录接收的bit数
reg [19:0] clk_cnt;//用于记录传输一个bit所需的系统时钟数
reg start_rx_flag;//开始串口接收标志信号
reg rx_r0;
reg rx_r1;
reg rx_r2;//对rx打拍,防止亚稳态问题,以及捕捉下降沿

wire down_edge;//用于表示rx有下降沿产生

localparam CLK_CNT = CLK_FRE/BPS; //传输一个bit所需要的系统时钟个数
//1/BPS:传输一个bit所需要的时间
//1/CLK_FRE:一个系统时钟周期所占的时间,1/50M=20ns
//传输一个bit所需要的系统时钟个数CLK_CNT=(1/BPS)/(1/CLK_FRE)=(CLK_FRE/BPS)


assign down_edge = (rx_r2)&(!rx_r1);//在下降沿的时候,rx_r2寄存的是rx上一个状态为1
									//rx_r1寄存的是当前状态为0
									//这样有下降沿的时候,down_edge就为1
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		rx_r0 <= 1'b1;
		rx_r1 <= 1'b1;
		rx_r2 <= 1'b1;//初值赋值为1,因为rx空闲态为1
	end
	else begin
		rx_r0 <= rx;
		rx_r1 <= rx_r0;
		rx_r2 <= rx_r1;//对rx打了三拍,前两拍是为了防止亚稳态,多加了一拍是为了捕捉下降沿
	end
end

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		start_rx_flag <= 1'b0;
	end
	else if(down_edge)begin//捕捉到下降沿,开始串口接收标志信号为1
		start_rx_flag <= 1'b1;
	end
	else if(rx_done)begin//一个字节接收完成才拉低串口接收标志信号start_rx_flag
		start_rx_flag <= 1'b0;
	end
	else begin
		start_rx_flag <= start_rx_flag;
	end
end


always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
	clk_cnt <= 20'd0;
	end
	
//clk_cnt清零的条件要放在start_rx_flag的条件下判断,否则clk_cnt不会清零
//当start_rx_flag拉高时,系统时钟计数器开始计数,每个系统时钟周期加1,并且在达到计数最大值减1时清零
	else if(start_rx_flag)begin
	if(clk_cnt == (CLK_CNT - 1'b1))
	clk_cnt <= 20'd0;
	else
	clk_cnt <= clk_cnt + 1'b1;
	end
	
	else begin
	clk_cnt <= 20'd0;//不在接收状态,系统时钟计数器就始终为0
	end
end

//接收bit数计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
	bit_cnt <= 4'd0;
	end
	else if(clk_cnt == (CLK_CNT - 1'b1))begin//每当系统时钟计数器达到计数最大值减1时就认为接收完了一个bit,接收bit数计数器就加1
	bit_cnt <= bit_cnt +1'b1;
	end
	else if(bit_cnt == 4'd9)begin//接收bit数计数器在等于9时清零,一个字节10bit接收完毕
	bit_cnt <= 4'd0;
	end
	else begin
	bit_cnt <= bit_cnt;
	end
end

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
	rx_byte <= 8'h0;
	else begin
	case(bit_cnt)//根据接收bit数计数器bit_cnt的值,在达到CLK_CNT的中间值时就采样rx并赋给rx_byte的对应位
	1:begin //比如bit_cnt=0时,是一个字节的起始位,我们不需要,bit_cnt=1时就是该字节的最低位,所以赋值给rx_byte的最低位
	if(clk_cnt == (CLK_CNT>>1))
	rx_byte[0] <= rx_r2;
	end
	2:
	begin
	if(clk_cnt == (CLK_CNT>>1))	
	rx_byte[1] <= rx_r2;
	end
	3:begin
	if(clk_cnt == (CLK_CNT>>1))
	rx_byte[2] <= rx_r2;
	end
	4:begin
	if(clk_cnt == (CLK_CNT>>1))
	rx_byte[3] <= rx_r2;
	end
	5:begin
	if(clk_cnt == (CLK_CNT>>1))
	rx_byte[4] <= rx_r2;
	end
	6:begin
	if(clk_cnt == (CLK_CNT>>1))
	rx_byte[5] <= rx_r2;
	end
	7:begin
	if(clk_cnt == (CLK_CNT>>1))
	rx_byte[6] <= rx_r2;
	end
	8:begin
	if(clk_cnt == (CLK_CNT>>1))
	rx_byte[7] <= rx_r2;
	end
	default:;
	endcase
	end
end

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
	rx_done <= 1'b0;
	end
	else if(bit_cnt == 4'd9)begin//当bit_cnt == 9时,认为一个字节的数据接收完成,拉高接收完成标志信号
	rx_done <= 1'b1;
	end
	else begin
	rx_done <= 1'b0;
	end
end

endmodule

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值