FPGA(二)——基于FPGA的UART收发模块设计

一. UART协议基本原理

1. UART协议介绍

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART。它将要传输的资料在串行通信与并行通信之间加以转换。作为把并行输入信号转成串行输出信号的芯片,UART通常被集成于其他通讯接口的连结上。

具体实物表现为独立的模块化芯片,或作为集成于微处理器中的周边设备。一般是RS-232C规格的,与类似Maxim的MAX232之类的标准信号幅度变换芯片进行搭配,作为连接外部设备的接口。在UART上追加同步方式的序列信号变换电路的产品,被称为USART(Universal Synchronous Asynchronous Receiver Transmitter)。

2. UART协议传输规则

在串口通信中,数据在1位宽的单条线路上进行传输,一个字节的数据要分为8次,由低位到高位按顺序一位一位的进行传送,这个过程称为数据的"串行化(serialized)"过程。由于串口通信是一种异步通信协议,并没有时钟信号随着数据一起传输,而且空闲状态(没有数据传输的状态)的时候,串行传输线为高电平1,所以发送方发送一个字节数据之前会先发送一个低电平0,接收方收到这个低电平0以后就知道有数据要来了,准备开始接收数据从而实现一次通信。串口通信的时序如下图所示:

FPGA(二)——基于FPGA的UART收发模块设计_第1张图片

串口通信的规范如下:

1、空闲状态(没有数据传输的状态)下,串行传输线上为高电平1

2、发送方发送低电平0表示数据传输开始,这个低电平表示传输的起始位

3、8-bit的数据位(1 Byte)是从最低位开始发送,最高位最后发送

4、数据位的最高位发送完毕以后的下一位是奇偶校验位,这一位可以省略不要,同时,当不发送奇偶校验位的时候接收方也相应的不接收校验位

5、最后一位是停止位,用高电平1表示停止位

下面以发送字节0x55为例来说明整个的发送过程:

先把0x55转化成二进制为:01010101。显然0x55的最低位bit 0是1,次低位bit 1是0,………,最高位bit 7是0,由于串口是从最低位开始发送一个字节,所以0x55各个位的发送顺序是1-0-1-0-1-0-1-0,波形如下图所示:

在这里插入图片描述

下面在给出一个波形,根据上面的规则也可以很容易判断这是发送字节0x13的波形:

在这里插入图片描述

接下来的最后一个问题是:串口传输的速度是多少?

实际上,串口传输的速度用波特率(baudrate)来指定。波特率表示的是每秒发送的比特数,单位是bps(bits-per-seconds),例如,1000 bauds表示1秒钟发送了1000个比特,或者说每个比特持续的时间是1ms。关于串口发送的波特率是有一组标准的规定的,并不是随便一个数字。常用的波特率标准有:

  1. 1200 bps
  2. 9600 bps (常用)
  3. 38400 bps
  4. 115200 bps (常用,而且通常情况下是我们能用的最快的波特率)

波特率为115200 bps时,每个比特持续的时间为(1/115200)=8.7us,所以发送8个bit(1 Byte)需要的时间是88.7us=69us。在不考虑奇偶校验位的情况下,发送一个字节还需要发送额外的1个起始位和1个停止位,所以发送1个字节实际所需要的最少时间是108.7us=87us,这意味着1s(1000000us)中能发送的字节数为(1000000/87) = 11494,所以在波特率为115200bps的情况下,串口传输数据的速率约为11.5KB/s。而有些电脑的串口有时候需要一个更长的停止位,比如1.5位或2位的停止位,那么发送一个字节所需要的时间比只有一个比特停止位的情况所耗费的时间更长,在这种情况下,串口的传输速率会低于10.5KB/s。

通过上面一系列的总结以后,可以得出FPGA与PC之间的串口通信主要包括三个模块:波特率产生模块、发射模块和接收模块。

二. URAT收发模块案例与Verilog实现

1. 案例描述

设计一个UART发送模块和接收模块,具体要求如下:

  1. 设计一个UART发送模块,该模块接收一个8位输入数据并采用UART协议发送,UART 采用10位传输协议,即一位起始位,8位数据位,一位终止位。发送数据时先发低位数据,最后发高位数据。要求波特率为1K。模块的定义如下:
module uart_rx ( 	
				module uart_tx ( 	clk_40k,		//clock signal, 40kHz
				rst_n, 		//reset signal, active low
				din,			//the input data which will be sent by the UART module, 8 bit width
				send_start,	//the start enable signal, active high, the width is one clock period
				bit_out		//the serial output data 
				); 
  1. 设计一个UART接收模块,模块的定义如下:
module uart_rx ( 	
				clk_40k,		//clock signal, 40kHz
				rst_n, 		//reset signal, active low
				bit_in,		//the input serial bit,
				dout_vld,		//the output valid signal, active high,the dout is valid when this signal is high.
				dout		//received data, 8 bit width 
					);
  1. 设计一个testbench,对发送模块和接收模块进行测试,测试过程如下,testbench产生一个随机数,然后启动uart_tx模块发送至uart_rx模块,当uart_rx模块接收到有效数据后,自动判断接收的数据是否正确。

2. 设计思路

  1. 首先,十位传输不存在奇偶校验位,因此只需确定起始位0和结束位1即可;
  2. 其次,传输是从最低位开始到最高结束,传输方向不能搞反;
  3. 同时,要求传输波特率为1k,单位bps,即表示每秒传送1k bit;相应的时钟为40kHz,即表示时钟每秒波动40次。因此,每40个时钟传送1 bit;
  4. 利用分频的方法,每40次传送1 bit,需要注意的是,40位的数组是从0开始的,因此数40次后应为39,即当cnt为十位制下的39时便需要归零。

3. Verilog源码

Verilog实现代码如下:

1.发送模块(uart_tx):


module uart_tx ( 	clk_40k,		//clock signal, 40kHz
					rst_n, 		//reset signal, active low
					din,			//the input data which will be sent by the UART module, 8 bit width
					send_start,	//the start enable signal, active high, the width is one clock period
					bit_out		//the serial output data 
					);

input [7:0] din;
input clk_40k;
input rst_n;
input send_start;

output bit_out;

reg flag;
reg tx_flag;
reg [6:0] cnt;
reg [5:0] tx_cnt;
reg [9:0] din_temp;

always @ (posedge clk_40k)
begin
	if(~rst_n)
		flag <= 1'b0;
	else if(send_start == 1'b1)
		flag <= 1'b1;
	else if(tx_flag == 1'b1)
		flag <= 1'b0;
end

always @ (posedge clk_40k)
begin
	if(~rst_n)
		tx_flag <= 1'b0;
	else if(flag == 1'b1 && din_temp[0] == 1'b0)
		tx_flag <= 1'b1;
	else if(tx_cnt == 7'd10)
		tx_flag <= 1'b0;
end

always @ (posedge clk_40k)
begin
	if(~rst_n)
		cnt <= 7'b0;
	else if(tx_flag == 1'b1 && cnt != 7'd39)
		cnt <= cnt + 1'b1;
	else 
		cnt <= 7'b0;
end

always @ (posedge clk_40k)
begin
	if(~rst_n)
		tx_cnt <= 6'b0;
	else if(tx_flag == 1'b1 && cnt == 7'd39)
		tx_cnt <= tx_cnt + 1'b1;
	else if(tx_flag == 1'b0)
		tx_cnt <= 6'b0;
end

always @ (posedge clk_40k)
begin
	if(~rst_n)
		din_temp <= 10'b1111111111;	
	else if(flag == 1'b1 || send_start == 1'b1)
		din_temp <= {1'b1,din,1'b0};
	else if(tx_flag == 1'b1 && cnt == 7'd39)
		din_temp <= {1'b1,din_temp[9:1]};
end

assign bit_out = din_temp[0];

endmodule

2.接收模块(uart_rx):


module uart_rx ( 	clk_40k,		//clock signal, 40kHz
			rst_n, 		//reset signal, active low
			bit_in,		//the input serial bit,
			dout_vld,		//the output valid signal? active high?the dout is valid when this signal is high.
			dout		//received data, 8 bit width 
			);

input bit_in;
input clk_40k;
input rst_n;

output reg dout_vld;
output reg [7:0] dout;

reg rx_flag;
reg [6:0] cnt;
reg [5:0] rx_cnt;
reg [7:0] dout_temp;

always @ (posedge clk_40k)
begin
	if(~rst_n)
		rx_flag <= 1'b0;
	else if(bit_in == 1'b0)
		rx_flag <= 1'b1;
	else if(rx_cnt == 6'd9)
		rx_flag <= 1'b0;
end

always @ (posedge clk_40k)
begin
	if(~rst_n)
		cnt <= 7'b0;
	else if(rx_flag == 1'b1 && cnt != 7'd39)
		cnt <= cnt + 1'b1;
	else if(rx_flag == 1'b0 || cnt == 7'd39)
		cnt <= 7'b0;
end

always @ (posedge clk_40k)
begin
	if(~rst_n)
		rx_cnt <= 6'b0;
	else if(rx_flag == 1'b1 && cnt == 7'd39)
		rx_cnt <= rx_cnt + 1'b1;
	else if(rx_flag == 1'b0)
		rx_cnt <= 6'b0;
end

always @ (posedge clk_40k)
begin
	if(~rst_n)
		dout_temp <= 8'b0;
	else if(rx_flag == 1'b1 && cnt == 7'd39)
	begin
		dout_temp[7] <= bit_in;
		dout_temp[6:0] <= dout_temp[7:1];
	end
end

always @ (posedge clk_40k)
begin
	if(~rst_n)
		dout_vld <= 1'b0;
	else if(rx_cnt == 6'd9 && cnt == 7'b0)
	begin
		dout <= dout_temp;
		dout_vld <= 1'b1;
	end
	else 
		dout_vld <= 1'b0;
end

endmodule

3.testbench(tb):


`timescale 1us/1us

module tb();

	reg clk_40k;
	reg rst_n;
	reg [7:0] din;
	reg send_start;

	wire bit_out;
	wire bit_in;
	wire dout_vld;
	wire [7:0] dout;
	assign bit_in = bit_out;

	uart_tx i_uart_tx(
 		.clk_40k        (clk_40k   ),
                .rst_n          (rst_n     ),
                .din            (din       ),
                .send_start     (send_start),
                .bit_out        (bit_out   )
			);

	uart_rx i_uart_rx(
		.clk_40k        (clk_40k   ),
                .rst_n          (rst_n     ),
                .bit_in         (bit_in    ),
                .dout_vld       (dout_vld  ),
                .dout           (dout      )
			);

	initial 
	begin
		rst_n = 1'b0;
	#10	rst_n = 1'b1;
	end


	initial
	begin
		clk_40k = 1'b0;
		forever
	#1	clk_40k = ~clk_40k;
	end


	initial 
	begin
		send_start = 1'b0;
		din = 8'd0;
		forever
		begin
			#1000
			din = $random()%256;
			send_start = 1'b1;
			#2
			send_start = 1'b0;
		end
	end

endmodule

你可能感兴趣的:(FPGA,单片机,stm32,嵌入式硬件)