本实验要求熟悉串行接口通信的基本原理,实现利用串行接口与计算机的通信,通过键盘输入的数据通过PC上的串行接口发送给FPGA,FPGA收到数据以后再将该数据通过串行接口发送给计算机,并通过超级终端显示出来,如图所示。 图14-1 串口通信实验要求

图14-1 串口通信实验要求



串行接口(简称串口)通信是目前最常用的一种低速短距离通信方式,常用于对各种设备进行操作的控制台,是UART(Universal Asynchronous Receiver/Transmitter,通用异步接收/发送装置)的一种具体实现方式,最常用的就是计算机上具备的RS232接口(图14-1)。本实验的采用的就是RS232接口,RS232接口由9针,其各引脚定义如表14-1所示。通常只使用其中的2、3、5三个管脚。 图14-2 RS232接口

图14-2 RS232接口

表14-1 RS232管脚定义

1载波检测 DCD
2接收数据 RXD
3发送数据 TXD
4数据终端准备好 DTR
5信号地 SG
6数据准备好 DSR
7请求发送 RTS
8清除发送 CTS
9振铃提示 RI

串口是异步通信方式,通信的发送方和接收方各自有独立的时钟,传输的速率由双方约定。UART的通信协议十分简单,以低电平作为起始位,高电平作为停止位,中间可传输5~8比特数据和1比特奇偶校验位,奇偶校验位的有无和数据比特的长度由通信双方约定。一帧数据传输完毕后可以继续传输下一帧数据,也可以继续保持为高电平,两帧之间保持高电平,持续时间可以任意长。本实验采用不添加校验位的方法,以提高数据传输效率。发送端发送数据时先发一低电平,然后发送8比特数据,之后马上把信号拉高,从而完成一帧数据传送。接收端接收到低电平时开始计数,然后接收8比特信息位后如果检测到高电平即认为已接收完一帧数据,继续等待下一帧起始信号低电平的到来,若接收完8比特数据后没有检测到高电平则认为这不是一帧有效数据,将其丢弃,继续等待起始信号。时序关系如图14-3所示,收发可同时进行,互不干扰。本实验的波特率设为9600。 图14-3 RS-232接口的工作时序

图14-3 RS-232接口的工作时序



3.1 总体架构

本实验的要求时在计算机串口控制台上显示输入的按键,即程序在收到从计算机发来的用户按键后将其原封不动地发回给计算机,计算机收到后将显示其收到的数据。因此,本程序的核心?榫褪谴诘氖辗⒛?椋绦蚣芄谷缤14-4所示。顶层?槲猆ART(UART.v),其中只包含一个?閙iniUART,miniUART的功能是实现数据在串口上的收发。UART?橹恍杞淙胧葑魑涑鍪菟透鴐iniUART。 图14-4 程序总体架构

图14-4 程序总体架构

UART?榈亩酝饨涌诜浅<虻ィ挥4根信号:

 
input	clk;			//异步复位信号		
input	reset;			//时钟信号,50MHZ
input	Rxd;			//UART串行数据输入端口
output	Txd;			//UART串行数据输出端口

3.2 miniUART?(miniUART.v)

miniUART是程序的核心?椋瓿纱谏鲜莸慕邮蘸头⑺停溆3个子?樽槌桑ㄍ14 5):
1. 波特率发生器clkUnit,波特率发生器产生发送和接收所需要的时钟信号。
2. 接收?镽xUnit负责串口上数据的接口。
3. 发送?門x
Unit负责串口上数据的接收。 图14-5 miniUART?榻峁

图14-5 miniUART?榻峁

miniUART的输入输出接口如下:

 
input	clk;				//时钟信号,50MHZ
input	reset;				//异步复位信号		
input	Rxd;				//接收?榇惺菔淙攵丝
output	Txd;				//发送?榇惺菔涑龆丝
input	[7:0]DataIn;		//串行?榻邮盏降氖
output	rx_data_rdy			//接收数据使能信号,为高表示DataIn有效。
output	[7:0]DataOut;		//串行?榻⑺偷氖
output	tx_data_en;			//发送数据使能信号,为高表示DataOut有效。

3.3 ClkUnit?(clkUnit.v)

clkUnit?椴糜诖涫莺徒邮帐莸氖敝樱德史直鹗9.6KHz和9.6×8KHz,对应的输出是clkTx和clkRx,正脉冲宽度为1个clk(50MHz)时钟周期。

3.4 Rx_Unit?椋║ART_Rx_Unit.v)

Rx_Unit完成串行数据的接口,其接口定义如下:

 
input	clk;			//时钟信号,50MHz。
input	rst;			//异步复位端
input	serial_in;		//串口输入。
input  	sample_clk;		//串口接收时钟,9.6K×8Hz。
output 	[7:0]data_out; 	//从串口接收到的数据通过data_out输出给其他?椤
output	data_rdy_out;	//dataout使能信号,为高时表示dataout数据有效,宽度为1个clk时钟周期。

接收?橛梢韵录父瞿?樽槌桑ㄍ14 6):metastablestateeliminator消除亚稳定,由于串口是异步输出,通过多级触发器消除亚稳态;starttrigger该?榧觳獯谙呱鲜欠裼惺淙耄籦itRx该?楦涸鸾邮盏ケ忍厥荩籨atarx该?楦涸鸾邮照鲎纸诘氖;rxdataenregulation?榻隹砦桓鰏ample_clk的数据输出使能信号整形为一个clk时钟周期。

接收?橹谐藃xdataenregulation?橥猓渌际且詓ampleclk作为工作时钟。

图14-6 接收?槟诓靠蛲

图14-6 接收?槟诓靠蛲

1.metastablestateeliminator?

metastablestateeliminator?橥ü郊禗触发器来消除亚稳态,同时输出两路串行输出serial2delay和serial3delay,分别对serial_in延时了两个时钟周期和3个时钟周期。

 
reg serial_1_delay, serial_2_delay,serial_3_delay;
always @ ( posedge sample_clk  or negedge rst ) begin : metastable_state_eliminator
	if( !rst ) begin
		serial_1_delay <= 0;
		serial_2_delay <= 0;
	end 
	else begin
		serial_1_delay <= serial_in;		//第一级延迟。
		serial_2_delay <= serial_1_delay;	//第二级延迟。
		serial_3_delay <= serial_2_delay;	//第三级延迟。
	end
end
  1. starttrigger?
    start
    trigger?榕卸鲜淙氲膕erial2delay是否为低电平。如果serial2delay为低电平,同时判断datarx?榈淖刺琑ecvstate是否处于空闲状态,如果两者皆满足,则有可能有新的数据到来,发出新数据到来的使能信号newdatain给datarx?椋ㄖ猟atarx开始接收数据。 <code verilog>

reg newdatain; always @ ( posedge sampleclk or negedge rst ) begin : starttrigger

  if( !rst ) begin
new_data_in <= 0;
  end 
else begin
    new_data_in <= 0; //确保new_data_in为一个sample_clk周期。
    //当recv_state在空闲状态,且serial_2_delay为0时,
          //置new_data_in为高。
    if( Recv_state == idle && ! serial_2_delay ) begin 
	new_data_in <= 1;
    end
  end

end

</code>

  1. data_rx?

datarx?橛蒼ewdatain触发工作,datarx的由状态机Recvstate控制工作,状态机如图14 7所示。缺省情况下,datarx处于idle状态,当有数据到来时newdatain变高,触发datarx进入startx状态。在starting状态,datarx接收数据,recvcounter是接收比特计数,当接收到9个比特的数据时,datarx回到idle状态。Datarx?樾枰鷊etbiten信号通知bitrx接收一个比特的数据。当收到bitrx?榉⒗吹慕邮沾砦笾甘緀rrorin时,也立刻反馈idle状态。 图14-7 Data_rx?樽刺

图14-7 Data_rx?樽刺

 
reg Recv_state;
reg [3:0] Recv_counter; 
always @ ( posedge sample_clk or negedge rst ) begin: data_rx
	if( !rst ) begin
		RCV_shftreg <= 8'hff;
		Recv_state <= 0;
		Recv_counter <= 0;
		Data_rdy <= 0;
		get_bit_en <= 0;	
		Data_out <= 0;
	end
	else begin
		Data_rdy <= 0;		
		get_bit_en <= 0;		//使得使能脉冲为一个sample周期。
		if( error_in ) begin	//如果bit_tx接收数据发现错误,则进入ilde状态。
			RCV_shftreg <= 8'hff;
			Recv_state <= 0;	//idle。
			Recv_counter <= 0;
			Data_rdy <= 0;
		end
		else begin
			case(Recv_state)
				3'd0: begin	//idle
					if( new_data_in ) begin //有新数据到来,进入starting状态。
						get_bit_en <= 1;	//通知bit_rx接收数据。
						Recv_state <= 1; 	//starting。
					end
				end
				3'd1: begin				//starting。
					if( bit_in_en ) begin	//如果有数据接收完毕。
						if( Recv_counter == 4'd9 ) begin //如果接收数据完毕。
							//如果停止位为1,则将接收到的数据RCV_shftreg
							//赋给Data_out,并置使能信号Data_rdy。
							if( bit_in ) begin			
								Data_out <= RCV_shftreg;
								Data_rdy <= 1;
							End
							//回到idle状态。
							RCV_shftreg <= 8'hff;
							Recv_state <= 0;
							Recv_counter <= 0;							
						end
						else begin
							//将接收到的比特赋给RCV_shitreg的最高位,然后
							//通知bit_tx接收下一比特数据。
							RCV_shftreg <= {bit_in, RCV_shftreg[Datasize-1:1]};
							Recv_counter <= Recv_counter + 1;
							get_bit_en <= 1;
						end
					end
				end
			endcase
		end
	end
end
  1. bit_rx?

bitrx接收单比特数据,由getbiten触发工作,由状态机bitrxstate控制工作(图14 8)。初始时状态机处于waiting状态。当收到datarx发来的getbiten信号时,状态机进入confirming的状态。在confirming状态,bitrx对收到的数据进行确认,即连续4个时钟周期收到的数据都相同时,确认一个比特的数据接收正确。在停留到第6个时钟周期时,返回waiting状态,并产生bitinenable信号通知datarx一个比特数据接收完成●!H绻⑾至4个时钟周期收到的数据有不相同时,则认为数据接收错误,立即反馈waiting状态,并产生error_in信号。

图14-8 Bit_rx?樽刺

图14-8 Bit_rx?樽刺

 
reg  bit_in_old,bit_in, bit_in_en;
reg [3:0] checkcounter;
reg bit_Rx_state, get_bit_en, error_in;
always @ ( posedge sample_clk or negedge rst  ) begin : bit_Rx
	if( !rst ) begin
		bit_Rx_state <= waiting;
		bit_in_old <= 0;
		bit_in <= 0;			//接收到的1比特数据。
		checkcounter <= 0;		//表示对数据确认次数,4次接收的数据全一致则认为数据正确。
		error_in <= 0;		//指示数据接收错误。
		bit_in_en <= 0;		//1比特数据接收完成。
	end
	else begin
		error_in <= 0;
		bit_in_en <= 0;
 
		case( bit_Rx_state )
		waiting: begin
		//如果data_rx?橥ㄖ邮帐荩蚪隿onfirming状态。
			if( get_bit_en ) begin					
                                bit_Rx_state <= confirming;
				bit_in_old <= serial_3_delay;
				checkcounter <= 1;
			end
		end
		confirming: begin
			//如果在4次以内接收的数据有不一致的地方则认为数据接收错误,返回waiting
//状态并将error_in置位。
			if( checkcounter <= 4'd4 && bit_in_old != serial_3_delay ) begin
				bit_Rx_state <= waiting;
				bit_in_old <= 0;
				checkcounter <= 1;
				error_in <= 1;
			end 
			else begin
				//当checkcounter计数到6时,输出数据给data_rx。
				if( checkcounter == 4'd6 ) begin
					checkcounter <= 0;
					bit_in_en <= 1;
					bit_Rx_state <= waiting;
					bit_in_old <= 0;
				end
				else begin
					//如果4次数据确认都正确,则将收到数据赋给bit_in。
					if( checkcounter == 4'd4 ) begin
						bit_in <= bit_in_old;
					end 
					checkcounter <= checkcounter + 1;
				end
			end
		end
	endcase
    end
end

3.5 Rx_Unit?椋║ART_Rx_Unit.v)

Rx_Unit?橥瓿纱谑莸姆⑺停浣涌诙ㄒ迦缦拢

 
input clk;			//时钟信号,50MHz。
input rst;			//异步复位信号。
input clk_tx;			//9.6KHz的时钟脉冲信号。
input [7:0] data_in;	//要发送的数据。
input data_en;		//发送数据使能,为高表示data_in有效。
Out serial_out			//串行输出。

RxUnit?橛勺刺鶷Xstate控制(图14 9),TxState有四个状态:
1) Idle状态:在初始时,状态机处于idle状态。当有数据从串口输出,即data
en为高时,进入starting状态。
2) Starting状态: 在Starting状态,发送起始位0,之后进入sendingdata状态。
3) Sending
data状态:在sending_data状态,发送8比特数据。当8比特数据发送完成后,进入stopping状态。
4) 在stopping状态,发送停止位,而后回到idle状态。

图14-9 发送?樽刺

图14-9 发送?樽刺

 
reg [1:0] TX_state;
reg [7:0] data_sent;
always @ ( posedge clk or negedge rst ) begin
	if( !rst ) begin
		TX_state <= idle;
		serial_out <= 1;
		data_sent <= 0;	//要发送的数据。
		bitnumber <= 0;
	end
	else begin	
		case(TX_state )
		idle: begin
			if( data_en ) begin	//将data_en时,进入starting状态。
				TX_state <= starting;			
				data_sent <= data_in;
			end
		end
		starting: begin
			if( clk_Tx ) begin		//clk_tx为9.6KHz时钟,有clk_tx触发发送。
				serial_out <= 0;	//发送起始位0。
				TX_state <= sending_data;	//进入sending_data状态。
				bitnumber <= 0;	//发送比特计数0。
			end
		end
		sending_data : begin
			if( clk_Tx ) begin
				serial_out <= data_sent[0];	//发送1比特数据。
				//data_sent右移一位,下一发送比特赋给data_sent[0]。
				data_sent <= {1'b1, data_sent[7:1]}; 
				bitnumber <= bitnumber + 1;
				//当8比特发送完成后,进入stopping状态。
				if( bitnumber == 4'd7 ) begin
					TX_state <= stopping;
				end
			end
		end
		stopping : begin
			//在stopping状态,发送停止位,进入idle状态。
			if( clk_Tx) begin
				TX_state <= idle;
				serial_out <= 1;
				data_sent <= 0;
				bitnumber <= 0;
			end
		end		
	endcase
    end
end

3.6 程序使用及设置要求

1. 设备要求:核心板、串口线(图14 11),连接串口线时注意黑线接地。 图14-10 串口线

图14-10 串口线

  1. 使用步骤:把串口线连接好,把程序下载到实验板上以后,打开windows系统自带的串口调试工具超级终端,建立新连接,从键盘输入信息,实现计算机和FPGA之间的通信。具体如下:

“开始”—〉“所有程序”—〉“附件”—〉“通讯”—〉“超级终端”,出现如所图14-11图14-11示画面。

图14-11  建立串口连接

图14-11 建立串口连接

这里为新建连接起名字为“UART”,然后选择COM1,有的计算机有多于1个的串口,这时要注意自己连接的是哪一个串口(图14-12)。 图14-12 选择串口

图14-12 选择串口

设置串口属性时,如果不知道该设置什么样的值,可以直接按“还原默认值”,系统会自动设置最保守的符合要求的值(图14-13)。 图14-13 串口设置

图14-13 串口设置

设置完成后,下载程序,可以看到超级终端中会显示用户的按键(图14-14)。 图14-14 运行结果

图14-14 运行结果



文件名功能
UART.vUART?椤
ClkUnit.v产生发送数据和接收数据所需的时钟;在本实验中配置的时钟是9600波特,可以通过修改该?槔锩娴姆制凳道瓷柚貌ㄌ芈省
RxUnit.v|接收数据,把串行输入数据转换成并行数据后输出;| |TxUnit.v发送数据,把并行输入数据转换成串行数据后输出
miniUART.vminiUART?椤



由于本实验中使用了两个时钟:clk和clk_rx,因此需要在Quartus II中设置双时钟。具体设置如下:

Assignment→Setting→Timing Analysis Setting→Classic Timing Analyzer Settings→Individual Clocks,在里面分别添加时钟clk和clk_rx,频率分别设为50MHz和1MHz。