【Verilog】UART异步串口的verilog实现

目录

一、UART介绍

二、Verilog代码

2.1 TOP

2.2 发送模块

2. 3 接收模块


一、UART介绍

        UART是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

        UART串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。UART在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位停止位,如图所示。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错。 奇校验时,发送方应使数据位中1的个数与校验位中1的个数之和为奇数;接收方在接收数据时, 对1的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。同样,偶校验则检查1 的个数是否为偶数。

【Verilog】UART异步串口的verilog实现_第1张图片

         UART通信过程中的数据格式及传输速率是可设置的,为了正确的通信,收发双方应约定并遵循同样的设置。数据位可选择为5、6、7、8位,其中8位数据位是最常用的,在实际应用中一般都选择8位数据位;校验位可选择奇校验、偶校验或者无校验位;停止位可选择1位(默认),1.5或2位。串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是bps(位 /秒),常用的波特率有9600、19200、38400、57600以及115200等。

        在设置好数据格式及传输速率之后,UART负责完成数据的串并转换,而信号的传输则由外部驱动电路实现。电信号的传输过程有着不同的电平标准和接口规范,针对异步串行通信的接口标准有RS232、RS422、RS485等,它们定义了接口不同的电气特性,如RS-232是单端输入输出,而RS-422/485为差分输入输出等。 RS232接口标准出现较早,可实现全双工工作方式,即数据发送和接收可以同时进行。在传输距离较短时(不超过15m),RS232是串行通信最常用的接口标准。

        RS-232标准的串口最常见的接口类型为DB9,样式如图 16.1.2所示,工业控制领域中用到的工控机一般都配备多个串口,很多老式台式机也都配有串口。但是笔记本电脑以及较新一点的台式机都没有串口,它们一般通过USB转串口线(图 16.1.3)来实现与外部设备的串口通信。

【Verilog】UART异步串口的verilog实现_第2张图片

         DB9接口定义以及各引脚功能说明如下图所示,我们一般只用到其中的2(RXD)、3 (TXD)、5(GND)引脚,其他引脚在普通串口模式下一般不使用。

【Verilog】UART异步串口的verilog实现_第3张图片

 

二、Verilog代码

        串口数据环回实验系统框图:

        【Verilog】UART异步串口的verilog实现_第4张图片

         在编写代码之前,我们首先要确定串口通信的数据格式及波特率。在这里我们选择串口比

较常用的一种模式,数据位为8位,停止位为1位,无校验位,波特率为9600bps。则传输一
帧数据的时序图如图 所示:
        

 

2.1 TOP

module	uart_top(
	input			sys_clk,
	input			sys_rst_n,
	input			uart_rxd,	
	output			uart_txd
);

parameter			FREQ	=	50000000;
parameter			BPS		=	9600;

wire				en;
wire		[7:0]	uart_data;

uart_recv	 #(                          //串口接收模块
    .FREQ       	(FREQ),       //设置系统时钟频率
    .BPS       		(BPS))
u_uart_recv(
	.sys_clk        (sys_clk),
	.sys_rst_n      (sys_rst_n),
	.uart_rxd       (uart_rxd),
	
	
	.uart_txd		(uart_data),
	.rx_done		(en)
);

uart_send	 #(                          //串口发送模块
    .FREQ       	(FREQ),       //设置系统时钟频率
    .BPS       		(BPS))
u_uart_send(
	.sys_clk        (sys_clk),
	.sys_rst_n      (sys_rst_n),
	.uart_din		(uart_data),
	.tx_en			(en),
	.uart_txd		(uart_txd)
);

endmodule 

2.2 发送模块

module	uart_send(
	input			sys_clk,
	input			sys_rst_n,
	input	[7:0]	uart_din,
	input			tx_en,
	
	output	reg		uart_txd
);

parameter			BPS		=		9600;
parameter			FREQ	=		50000000;
localparam			BPS_CNT	=		FREQ / BPS;

wire				start_flag;
	
reg					tx_en_d0;	
reg					tx_en_d1;
reg					tx_flag;		//发送过程标志
reg		[15:0]		bps_cnt;
reg		[3:0]		tx_cnt;
reg		[7:0]		tx_data;

assign		start_flag	=		tx_en	&&	(~tx_en_d1);

always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)begin
		tx_en_d0		<=		1'b0;
		tx_en_d1		<=		1'b0;
		end
	else	begin
		tx_en_d0		<=		tx_en;
		tx_en_d1		<=		tx_en_d0;
		end
end

always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)begin
		bps_cnt			<=		16'd0;
		tx_cnt			<=		4'd0;
		end
	else	if(bps_cnt	<	BPS_CNT-1)begin
			bps_cnt			<=		bps_cnt+1'b1;
			tx_cnt			<=		tx_cnt;
			end
	else	begin
		bps_cnt			<=		16'd0;
		tx_cnt			<=		tx_cnt+1'b1;
		end
end

always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)begin
		tx_flag			<=		1'b0;
		tx_data			<=		8'd0;
		end
	else if(start_flag)begin
			tx_flag		<=		1'b1;
			tx_data		<=		uart_din;
			end
	else if((tx_cnt==4'd9)&&(bps_cnt==BPS_CNT/2))begin
			tx_flag		<=		1'b0;
			tx_data		<=		8'd0;
			end
		else begin	
			tx_flag		<=		tx_flag;
			tx_data		<=		tx_data;
			end
end

always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)
		uart_txd		<=		1'b1;		//空闲状态,发送端为高电平
	else if(tx_flag)
			case(tx_cnt)
				4'd0	:	uart_txd	<=	1'b0;//起始位
				4'd1	:	uart_txd	<=	tx_data[0];
				4'd2	:	uart_txd	<=	tx_data[1];
				4'd3	:	uart_txd	<=	tx_data[2];
				4'd4	:	uart_txd	<=	tx_data[3];
				4'd5	:	uart_txd	<=	tx_data[4];
				4'd6	:	uart_txd	<=	tx_data[5];
				4'd7	:	uart_txd	<=	tx_data[6];
				4'd8	:	uart_txd	<=	tx_data[7];
				4'd9	:	uart_txd	<=	1'b1;//停止位
				default	:	uart_txd	<=	1'b1;
				endcase
		else
			uart_txd	<=	1'b1;
end
				
endmodule

2. 3 接收模块

module	uart_recv(
	input			sys_clk,
	input			sys_rst_n,
	input			uart_rxd,
	
	output	reg[7:0]	uart_txd,
	output	reg		rx_done
);
parameter			BPS		=		9600;
parameter			FREQ	=		50000000;
localparam			BPS_CNT	=		FREQ / BPS;

wire				start_flag;		//对uart_rxd下降沿进行检测

reg					uart_rxd_d0;
reg					uart_rxd_d1;
reg					rx_flag;		//接收数据标志信号
reg		[3:0]		rx_cnt;			//对接收数据计数	
reg		[15:0]		bps_cnt;		//对时钟的计数
reg		[7:0]		tx_data;		//对接收数据进行寄存

assign	start_flag	=	uart_rxd_d1	&	(~uart_rxd_d0);		//下降沿检测电路

//对uart_rxd延迟两个时钟单位
always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)begin
		uart_rxd_d0		<=		1'b0;
		uart_rxd_d1		<=		1'b0;
		end
	else	begin
		uart_rxd_d0		<=		uart_rxd;
		uart_rxd_d1		<=		uart_rxd_d0;
		end
end

//当start_flag来临时,开始接收数据
always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)
		rx_flag			<=		1'b0;
	else	
		if(start_flag)
			rx_flag		<=		1'b1;
		else if((bps_cnt == BPS_CNT/2)  &&	(rx_cnt	== 4'd9))  //接收了8个bit后,接收状态关闭
			rx_flag		<=		1'b0;
		else
			rx_flag		<=		rx_flag;
end

//进入接收过程后,启动系统时钟计数器与接收数据计数器
always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)begin
		bps_cnt			<=		16'd0;
		rx_cnt			<=		4'd0;
		end
	else if(rx_flag)
		if(bps_cnt	<	BPS_CNT -1'b1)begin
			bps_cnt		<=		bps_cnt	+	1'b1;
			rx_cnt		<=		rx_cnt;
			end
		else	begin
			bps_cnt		<=		16'd0;
			rx_cnt		<=		rx_cnt	+	1'b1;
			end
	else	begin
		bps_cnt			<=		16'd0;
		rx_cnt			<=		4'd0;
		end
end

//通过计数器来寄存接收到的数据,tx_data
always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)
		tx_data			<=		8'd0;
	else	if(rx_flag)		
			if(bps_cnt ==	BPS_CNT/2)begin
				case(rx_cnt)
					4'd1	:	tx_data[0]	<=	uart_rxd_d1;
					4'd2	:	tx_data[1]	<=	uart_rxd_d1;
					4'd3	:	tx_data[2]	<=	uart_rxd_d1;
					4'd4	:	tx_data[3]	<=	uart_rxd_d1;
					4'd5	:	tx_data[4]	<=	uart_rxd_d1;
					4'd6	:	tx_data[5]	<=	uart_rxd_d1;
					4'd7	:	tx_data[6]	<=	uart_rxd_d1;
					4'd8	:	tx_data[7]	<=	uart_rxd_d1;
					default	:	;
				endcase
				end
			else
				tx_data		<=		tx_data;
	else
		tx_data		<=		8'd0;
end
	
//接收数据寄存到uart_txd,并输出接收完成标志位
always@(posedge	sys_clk	or	negedge	sys_rst_n)begin
	if(!sys_rst_n)begin
		rx_done		<=		1'b0;
		uart_txd	<=		8'd0;
		end
	else	if(rx_cnt == 4'd9)begin
				rx_done		<=		1'b1;
				uart_txd	<=		tx_data;
			end
	else	begin
			rx_done		<=		1'b0;
			uart_txd	<=		8'd0;
			end
end

endmodule

你可能感兴趣的:(Verilog,FPGA接口篇,fpga开发)