FPGA学习之串口发送模块设计与验证

FPGA学习之串口发送模块设计与验证

1.实验目的:

实现一个串口输出,通过上位机PC查看接收到的是否是串口发送的数据。

2.实验介绍:

学习UART通信原理及其硬件电路设计,使用FPGA实现UART通信中的数据发送部分设计,并使用ISSP工具来进行板级验证。

3.实验原理

(1)异步串行通信原理

串行通信是指利用一条传输线将数据一位位的顺序传送,每位数据占据固定时间长度。

异步通信以一个字符为传输单位,通信中两个字符之间的时间间隔多少是不固定的,但同一个字符中的两个相邻位之间的时间间隔是固定的。
FPGA学习之串口发送模块设计与验证_第1张图片
即下图中10位数据之间时间间隔是固定的,这个固定时间称为波特率,指每秒钟可以通信的数据比特个数,典型的波特率有1200bps,4800bps,9600bps,19200bps,115200bps等,一般通信两端波特率要一致。

FPGA学习之串口发送模块设计与验证_第2张图片

(2)系统框架图

对于整个串口发送模块,基于上述原理,理解如下输入输出接口:

1.首先作为发送模块,必然有一个输出发送数据端口Rx232_Tx;

2.对于一个字符发送完成,必须要告诉上位机字符发送结束,启动下一次发送,即发送结束信号标志Tx_Done,当发送完成,输出一个时钟周期高电平。

3.当串口模块在发送数据期间,需要存在一个输出标志串口状态位uart_state,即处于发送状态时为1,告诉上位机目前模块正忙。

4.输入肯定有时钟Clk和复位输入Rst_n。
FPGA学习之串口发送模块设计与验证_第3张图片

5.在上图中波特率固然有常用的几个,但是如何去选择对应上位机的波特率,来实现不同上位机通信,因此这里需要一个波特率选择输入端口baud_set[3:0]。

6.我们要发送一个数据,必须要控制模块在需要数据时刻发送,不能无休止的一直发送,因此必须有发送控制信号即发送使能信号send_en

7.待发送的数据输入data_byte[7:0];

那这些输入输出端口之间存在怎样的逻辑电路来将数据发送出去呢?

数据输出格式

对于要输出1字节10位数据,其中数据发送格式已知,即起始位+8位数据位+停止位(校验位默认无),那么这里需要10选一多路器,这样我们可以来选择究竟将哪一位数据发送出去。

FPGA学习之串口发送模块设计与验证_第4张图片
由于RS232是异步收发器,因此为了保证发送的数据在时钟到来时处于稳定,需要对输入数据进行寄存。

8位有效数据经过寄存器发送到10选一多路中,再由sel选择信号选择输出哪位数据,选择好数据后,传输到data_reg寄存器中。

那么上图中sel选择信号怎么产生呢?

波特率时钟沿计数输出10选一多路器控制

首先sel选择信号选择的是哪位数据输出?由下图可知,我们可以将BPS_CLK的时钟沿进行计数,当每来一个上升沿就让计数器bps_cnt自加1,然后根据计数结果来选择数据位数输出,例如计数器计到7时,选择信号就会选择发送BIT[5]数据。

FPGA学习之串口发送模块设计与验证_第5张图片

而且这个计数器最大只能计数到11(一个字节发送完毕),计数到11时,就需要产生清零clr信号。

这个清零clr信号根据上述输出端口Tx_Done而来,当结束信号标志Tx_Done输出一个时钟周期高电平,认定一个字节传输完毕。

当计数器bps_cnt_q等于11,结果为真,连接到clr清零信号线上,来实现清零动作。

同时比较结果又经过寄存器传输到Tx_Done信号上,告诉上位机字符发送结束。

FPGA学习之串口发送模块设计与验证_第6张图片
查看上图可得,bps_cnt的输入BPS_Clk信号其实波特率对应的时钟信号。

分频计数器输出波特率时钟

FPGA学习之串口发送模块设计与验证_第7张图片
因为一个字符相邻位的时间间隔对应的波特率,而每一位的传输是基于bps_clk的上升沿来的,因此我们需要不同的bps_clk来对应不同波特率(1200bps,4800bps,9600bps,115200bps)。

首先第一步是产生不同的波特率,因为系统时钟是50Mhz,系统时钟周期为20ns,这里采用依然采用分频计数器来实现不同波特率时钟,下表是常见波特率对应的计数器关系:

FPGA学习之串口发送模块设计与验证_第8张图片
上表可知所谓波特率的生成,无非就是利用一个定时器来定时,产生频率与对应波特率时钟频率。

例如当我们使用9600bps时,则需要产生一个频率为9600Hz的时钟信号。那么如何去产生这个信号呢?

在这里,我们先将9600Hz的时钟信号周期计算出来,1秒钟为1000_000_000ns,因此9600bps波特率时钟的周期为1000_000_000/9600≈104166.6,即9600Hz时钟信号的一个周期为104166.6ns,每当定时时间当来,就产生一个系统时钟周期长度的高脉冲信号即可,由于系统时钟周期为20ns(50MHz),因此只需计数104166.6/20个系统时钟,即可获取到101166.6的定时。

FPGA学习之串口发送模块设计与验证_第9张图片

分频计数器计数使能控制

对应一个分频计数器而言,在系统时钟运行过程中,什么时候开始计数即使能计数信号?计数什么时候开始工作呢?

本次设计采用两个二选一多路器来产生使能计数信号,首先这里有一个优先级的问题,因为当send_en发送使能信号为高电平时,那么我们的分频计数器也就要开始计数了。

然而send_en信号只是维持一个时钟周期的高电平,其余时间为低电平,这时就需要一个二选一多路器来实现当send_en信号为高电平时,输出“1”表示计数开始。

如何实现在send_en为低电平时,分频计数器还在计数呢?计数结束的标志是什么呢?

由上文可知在数据传输结束后会产生一个Tx_Done标志信号,这个信号是计数器bps_cnt等于11时产生的,在与11的比较结果会连接到计数器bps_cnt清零clr信号上,那么也可以将该信号作为另一个二选一多路器的选择端上。

FPGA学习之串口发送模块设计与验证_第10张图片

因此send_en信号为低电平时会选择第二个二选一多路器MUX2_2作为输出,MUX2_2的选择端与clr同源,因此当比较结果为假时,则MUX2_2的输出即是原来的输入UART_state,即保持UART_state状态不变,依然表示分频计数器还在计数。

当比较结果为真时,即发送数据结束,MUX2_2会输出“0”,这时UART_state会输出低电平,使能计数en_cnt会控制分频计数器停止计数。

不同波特率查找计数值输出

如何产生选择不同波特率时钟信号对应的计数值bps_DR[15:0]呢?

FPGA学习之串口发送模块设计与验证_第11张图片

这里利用查找表LUT的方式来实现baud_set信号选择不同的波特率,输出不同的系统时钟计数值。

系统框架图

因此可以得出串口发送模块基本逻辑的系统框架图:
FPGA学习之串口发送模块设计与验证_第12张图片

4.代码实现

根据上文描述系统框架图来分别实现相应部分功能:

数据输出格式

FPGA学习之串口发送模块设计与验证_第13张图片

波特率时钟沿计数输出10选一多路器控制

FPGA学习之串口发送模块设计与验证_第14张图片

分频计数器输出波特率时钟

FPGA学习之串口发送模块设计与验证_第15张图片

分频计数器计数使能控制

FPGA学习之串口发送模块设计与验证_第16张图片

不同波特率查找计数值输出

FPGA学习之串口发送模块设计与验证_第17张图片

总体代码:

//--------------------------------------------------------------------------------------------
//      Component name  : uart_Tx	
//      Author          : 硬件嘟嘟嘟
//      time            : 2020.04.21
//      Description     : 串口发送模块
//      src             : FPGA系统设计与验证实战指南_V1.2
//--------------------------------------------------------------------------------------------

module uart_tx(Clk,Rst_n,send_en,baud_set,data_byte,Rx232_Tx,Tx_Done,uart_state);

		input Clk,Rst_n;
		input send_en;  //发送使能信号输入
		
		input [2:0] baud_set;  //波特率选择输入
		input [7:0] data_byte; //待发送数据
		//
		output Rx232_Tx;    //发送数据输出端口
		output Tx_Done;     //一字节发送结束标志位
		output uart_state;   //发送数据状态
		

		
//数据输入寄存,输出格式及输出寄存模块

//8位有效数据寄存
reg [7:0] r_data_byte;
always@(posedge Clk,negedge Rst_n)
			if(!Rst_n)
					r_data_byte <= 8'b0;
			else if(send_en)
					r_data_byte <= data_byte;
			else
					r_data_byte <= r_data_byte;

//10选1多路器模块
reg [3:0] bps_cnt_q;
reg Rx232_Tx;
localparam START_BIT = 1'b0,
           STOP_BIT  = 1'b1;
always@(posedge Clk,negedge Rst_n) 
		if(!Rst_n)
			Rx232_Tx = 1'b1;
		else begin 
			case(bps_cnt_q)
				0 : Rx232_Tx <= 1'b1;
				1 : Rx232_Tx <= START_BIT;
				2 : Rx232_Tx <= r_data_byte[0];
				3 : Rx232_Tx <= r_data_byte[1];
				4 : Rx232_Tx <= r_data_byte[2];
				5 : Rx232_Tx <= r_data_byte[3];
				6 : Rx232_Tx <= r_data_byte[4];
				7 : Rx232_Tx <= r_data_byte[5];
				8 : Rx232_Tx <= r_data_byte[6];
				9 : Rx232_Tx <= r_data_byte[7];
				10 : Rx232_Tx <= STOP_BIT;
				default : Rx232_Tx = 1'b1;
			endcase
		end
		
		

//波特率时钟沿计数输出10选一多路器控制
//10选1多路器控制信号产生
reg bps_clk;
always@(posedge Clk,negedge Rst_n)
			if(!Rst_n)
					bps_cnt_q <= 4'd0;
			else if(bps_clk)
					bps_cnt_q <= bps_cnt_q + 1'b1;
			else if(bps_cnt_q == 4'd11)
					bps_cnt_q <= 4'd0;
			else 
					bps_cnt_q <= bps_cnt_q;

//发送数据完成标志位
reg Tx_Done;
always@(posedge Clk,negedge Rst_n)
		if(!Rst_n)
			Tx_Done <= 1'b0;
		else if(bps_cnt_q == 4'd11)
			Tx_Done <= 1'b0;
		else 
			Tx_Done <= 1'b1;

//分频计数器输出波特率时钟

//分频计数
reg [15:0] bps_DR;
reg [15:0] div_cnt;
reg uart_state ;//en_cnt信号由uart_cnt寄存器寄存
always@(posedge Clk,negedge Rst_n)
		if(!Rst_n)
				div_cnt <= 16'd0;
		else if(uart_state)begin
			if(div_cnt == bps_DR)
				div_cnt <= 16'd0;
		else 
				div_cnt <= div_cnt + 1'b1;
		end
		else
		div_cnt <= 16'd0;
//输出波特率时钟信号模块
always@(posedge Clk,negedge Rst_n)
		if(!Rst_n)
				bps_clk <= 1'b0;
		else if(div_cnt == 16'd1)
				bps_clk <= 1'b1;
		else 
				bps_clk <= 1'b0;

//分频计数器计数使能控制
always@(posedge Clk ,negedge Rst_n)
		if(!Rst_n)
				uart_state <= 1'b0;
		else if(send_en)
				uart_state <= 1'b1;
		else if(bps_cnt_q == 4'd11)
				uart_state <= 1'b0;
		else 
				uart_state <= uart_state;

//不同波特率查找计数值输出
always@(posedge Clk,negedge Rst_n)
if(!Rst_n)
bps_DR <= 16'd5207;
else begin
			case (baud_set)
				0:bps_DR <= 16'd10416;
				1:bps_DR <= 16'd5207;
				2:bps_DR <= 16'd2603;
				3:bps_DR <= 16'd1301;
				4:bps_DR <= 16'd433;
				default : bps_DR <= 16'd5207;
			endcase
		end
endmodule

你可能感兴趣的:(FPGA)