FPGA_UART串口通信

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:在学习stm32的时候,UART串口这个东西大家肯定没少用吧,在32标准库开发中我们只需要站在巨人的肩膀上对串口进行简单配置,就可以开始使用。那在FPGA我们怎么才能写个串口了?


提示:以下是本篇文章正文内容,下面案例可供参考

一、UART串口是什么?

通用异步收发传输器(Universal Asynchronous Receiver / Transmitter,UART)是一种异步收发传输器。它将要传输的数据在串行通信与并行通信之间加以转换,将发送时将并行数据转换成串行数据来传输,在接收时将串行数据转换成并行数据,UART串口通信属于全双工通信。

虽然是异步传输,但是规定了传输速度(波特率),数据位数,停止位,才保证通讯的可靠性。

二、串口收发

1.串口收

代码如下(示例):

module uart_rx_path(
	input clk_i,
	input uart_rx_i,
	
	output [7:0] uart_rx_data_o,
	output uart_rx_done,
	output baud_bps_tb			//for simulation
 );
  
parameter [13:0] BAUD_DIV     = 14'd10416;//波特率时钟,9600bps,100Mhz/9600=10416
parameter [13:0] BAUD_DIV_CAP = 14'd5208;//波特率时钟中间采样点,100Mhz/9600/2=5208

reg [13:0] baud_div=0;				//波特率设置计数器
reg baud_bps=0;					//数据采样点信号
reg bps_start=0;					//波特率启动标志
always@(posedge clk_i)
begin
	if(baud_div==BAUD_DIV_CAP)		//当波特率计数器计数到采样点时,产生采样信号baud_bps
		begin
			baud_bps<=1'b1;
			baud_div<=baud_div+1'b1;
		end
	else if(baud_div<BAUD_DIV && bps_start)//当波特率计数器启动时,计数器累加
		begin
			baud_div<=baud_div+1'b1;
			baud_bps<=0;
		end
	else
		begin
			baud_bps<=0;
			baud_div<=0;
		end
end

reg [4:0] uart_rx_i_r=5'b11111;			//数据接收缓存器
always@(posedge clk_i)
begin
	uart_rx_i_r<={uart_rx_i_r[3:0],uart_rx_i};
end
//数据接收缓存器,当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号
wire uart_rx_int=uart_rx_i_r[4] | uart_rx_i_r[3] | uart_rx_i_r[2] | uart_rx_i_r[1] | uart_rx_i_r[0];

reg [3:0] bit_num=0;	//接收数据个数计数器
reg uart_rx_done_r=0;	//数据接收完成寄存器
reg state=1'b0;

reg [7:0] uart_rx_data_o_r0=0;//数据接收过程中,数据缓存器
reg [7:0] uart_rx_data_o_r1=0;//数据接收完成,数据寄存器

always@(posedge clk_i)
begin
	uart_rx_done_r<=1'b0;
	case(state)
		1'b0 : 
			if(!uart_rx_int)//当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号,启动波特率时钟
				begin
					bps_start<=1'b1;
					state<=1'b1;
				end
		1'b1 :			
			if(baud_bps)	//每次等待波特率采样中心时,接收数据,放入数据缓存器中
				begin
					bit_num<=bit_num+1'b1;
					if(bit_num<4'd9)	//接收1bit起始信号,8bit有效信号,1bit结束信号
						uart_rx_data_o_r0[bit_num-1]<=uart_rx_i;
				end
			else if(bit_num==4'd10) //接收完成时候,接收数据个数计数器清零,产生接收完成标志位,并将数据写入数据寄存器,关闭波特率时候
				begin
					bit_num<=0;
					uart_rx_done_r<=1'b1;
					uart_rx_data_o_r1<=uart_rx_data_o_r0;
					state<=1'b0;//进入状态0,再次循环检测
					bps_start<=0;
				end
		default:;
	endcase
end
assign baud_bps_tb=baud_bps;//for simulation
assign uart_rx_data_o=uart_rx_data_o_r1;		
assign uart_rx_done=uart_rx_done_r;
endmodule

波特率部分,我们设置的是固定波特率9600,也就是1秒发送9600个数据,每个数据所用时间是1/9600秒,输入时钟周期是1/100M秒,则计数器要计100M/9600个数。
波特率计数器从起始信号开始计数从0记到BAUD_DIV,然后置0,在中间时刻对数据进行采集。也就是起始信号开始计数从0记到BAUD_DIV,只接受一位数据

always@(posedge clk_i)
begin
	if(baud_div==BAUD_DIV_CAP)		//当波特率计数器计数到采样点时,产生采样信号baud_bps
		begin
			baud_bps<=1'b1;
			baud_div<=baud_div+1'b1;
		end
	else if(baud_div<BAUD_DIV && bps_start)//当波特率计数器启动时,计数器累加
		begin
			baud_div<=baud_div+1'b1;
			baud_bps<=0;
		end
	else
		begin
			baud_bps<=0;
			baud_div<=0;
		end
end

串口接受代码
开始接受:当连续接收到五个低电平时即uart_rx_int=0,同时启动波特率计 数器开始计数
每当计数器记计到中值,接收数据,并将其存入数据缓冲寄存器中,当数据接受完,数据缓冲寄存器赋值于数据寄存器,并关闭波特率寄存器,为下次接受数据做准备

reg [4:0] uart_rx_i_r=5'b11111;			//数据接收缓存器
always@(posedge clk_i)
begin
	uart_rx_i_r<={uart_rx_i_r[3:0],uart_rx_i};
end
//数据接收缓存器,当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号:当连续接收到五个低电平时即
wire uart_rx_int=uart_rx_i_r[4] | uart_rx_i_r[3] | uart_rx_i_r[2] | uart_rx_i_r[1] | uart_rx_i_r[0];

reg [3:0] bit_num=0;	//接收数据个数计数器
reg uart_rx_done_r=0;	//数据接收完成寄存器
reg state=1'b0;

reg [7:0] uart_rx_data_o_r0=0;//数据接收过程中,数据缓存器
reg [7:0] uart_rx_data_o_r1=0;//数据接收完成,数据寄存器

always@(posedge clk_i)
begin
	uart_rx_done_r<=1'b0;
	case(state)
		1'b0 : 
			if(!uart_rx_int)//当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号,启动波特率时钟
				begin
					bps_start<=1'b1;
					state<=1'b1;
				end
		1'b1 :			
			if(baud_bps)	//每次等待波特率采样中心时,接收数据,放入数据缓存器中
				begin
					bit_num<=bit_num+1'b1;
					if(bit_num<4'd9)	//接收1bit起始信号,8bit有效信号,1bit结束信号
						uart_rx_data_o_r0[bit_num-1]<=uart_rx_i;
				end
			else if(bit_num==4'd10) //接收完成时候,接收数据个数计数器清零,产生接收完成标志位,并将数据写入数据寄存器,关闭波特率时候
				begin
					bit_num<=0;
					uart_rx_done_r<=1'b1;
					uart_rx_data_o_r1<=uart_rx_data_o_r0;
					state<=1'b0;//进入状态0,再次循环检测
					bps_start<=0;
				end
		default:;
	endcase
end
assign baud_bps_tb=baud_bps;//for simulation
assign uart_rx_data_o=uart_rx_data_o_r1;		
assign uart_rx_done=uart_rx_done_r;

2.串口发

代码如下(示例):

module uart_tx_path(
	input clk_i,

	input [7:0] uart_tx_data_i,	//待发送数据
	input uart_tx_en_i,			//发送发送使能信号
	
	output uart_tx_o
);

parameter BAUD_DIV     = 14'd10416;//波特率时钟,9600bps,100Mhz/9600=10416,波特率可调
parameter BAUD_DIV_CAP = 14'd5208;//波特率时钟中间采样点,100Mhz/9600/2=5208,波特率可调

reg [13:0] baud_div=0;			//波特率设置计数器
reg baud_bps=0;				//数据发送点信号,高有效
reg [9:0] send_data=10'b1111111111;//待发送数据寄存器,1bit起始信号+8bit有效信号+1bit结束信号
reg [3:0] bit_num=0;	//发送数据个数计数器
reg uart_send_flag=0;	//数据发送标志位
reg uart_tx_o_r=1;		//发送数据寄存器,初始状态位高

always@(posedge clk_i)
begin
	if(baud_div==BAUD_DIV_CAP)	//当波特率计数器计数到数据发送中点时,产生采样信号baud_bps,用来发送数据
		begin
			baud_bps<=1'b1;
			baud_div<=baud_div+1'b1;
		end
	else if(baud_div<BAUD_DIV && uart_send_flag)//数据发送标志位有效期间,波特率计数器累加,以产生波特率时钟
		begin
			baud_div<=baud_div+1'b1;
			baud_bps<=0;	
		end
	else
		begin
			baud_bps<=0;
			baud_div<=0;
		end
end

always@(posedge clk_i)
begin
	if(uart_tx_en_i)	//接收数据发送使能信号时,产生数据发送标志信号
		begin
			uart_send_flag<=1'b1;
			send_data<={1'b1,uart_tx_data_i,1'b0};//待发送数据寄存器装填,1bit起始信号0+8bit有效信号+1bit结束信号
		end
	else if(bit_num==4'd10)	//发送结束时候,清楚发送标志信号,并清楚待发送数据寄存器内部信号
		begin
			uart_send_flag<=1'b0;
			send_data<=10'b1111_1111_11;
		end
end

always@(posedge clk_i)
begin
	if(uart_send_flag)	//发送有效时候
		begin
			if(baud_bps)//检测发送点信号
				begin
					if(bit_num<=4'd9)
						begin
							uart_tx_o_r<=send_data[bit_num];	//发送待发送寄存器内数据,从低位到高位
							bit_num<=bit_num+1'b1;
						end
				end
			else if(bit_num==4'd10)
				bit_num<=4'd0;
		end
	else
		begin
			uart_tx_o_r<=1'b1;	//空闲状态时,保持发送端位高电平,以备发送时候产生低电平信号
			bit_num<=0;
		end
end

assign uart_tx_o=uart_tx_o_r;

endmodule

波特率部分与接受相似

串口发送部分
将数据转换成:起始位+数据+停止位
然后再一位一位传输出去

always@(posedge clk_i)
begin
	if(uart_tx_en_i)	//接收数据发送使能信号时,产生数据发送标志信号
		begin
			uart_send_flag<=1'b1;
			send_data<={1'b1,uart_tx_data_i,1'b0};//待发送数据寄存器装填,1bit起始信号0+8bit有效信号+1bit结束信号
		end
	else if(bit_num==4'd10)	//发送结束时候,清楚发送标志信号,并清楚待发送数据寄存器内部信号
		begin
			uart_send_flag<=1'b0;
			send_data<=10'b1111_1111_11;
		end
end

always@(posedge clk_i)
begin
	if(uart_send_flag)	//发送有效时候
		begin
			if(baud_bps)//检测发送点信号
				begin
					if(bit_num<=4'd9)
						begin
							uart_tx_o_r<=send_data[bit_num];	//发送待发送寄存器内数据,从低位到高位
							bit_num<=bit_num+1'b1;
						end
				end
			else if(bit_num==4'd10)
				bit_num<=4'd0;
		end
	else
		begin
			uart_tx_o_r<=1'b1;	//空闲状态时,保持发送端位高电平,以备发送时候产生低电平信号
			bit_num<=0;
		end
end

assign uart_tx_o=uart_tx_o_r;


## 顶层模块 实现效果:将接受的数据再发发送出去 ```c module uart_top( input clk_i, input rst_n_i,
input uart_rx_i,

output uart_tx_o
);

wire [7:0] uart_rx_data_o;
wire uart_rx_done;

uart_rx_path uart_rx_path_u (
.clk_i(clk_i),
.uart_rx_i(uart_rx_i),

.uart_rx_data_o(uart_rx_data_o), 
.uart_rx_done(uart_rx_done)
);

uart_tx_path uart_tx_path_u (
.clk_i(clk_i),
.uart_tx_data_i(uart_rx_data_o),
.uart_tx_en_i(uart_rx_done),
.uart_tx_o(uart_tx_o)
);

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值