目录

银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-12-09 gingko 初次建立

实验十六:串口通信实验——基于FPGA实现UART通信

一、 实验目的与意义

  1. 学习UART通信协议的原理和时序。
  2. 掌握UART接口的Verilog编程实现方法。

二、 实验设备及平台

  1. iCore3L 双核心板;
  2. iTool3Pro(串口TTL)或带有相同功能的串口模块;
  3. XiST USB-CABLE(或相同功能)仿真器;
  4. Micro USB线缆;
  5. 装有HqFpga开发软件的电脑一台。

三、 实验原理

图16-1 串口时序示意图

UART接线原则:RX↔TX、TX↔RX,如图16-2所示。 图16-2 UART信号连接示意图

四、 代码讲解

//发送数据,由低位到高位发送
always @ (posedge clk2 or negedge rst_n)
begin
	if(!rst_n)
		begin
			tx_cnt <= 10'd659;
			tx_r <= 1'd1;
			send_over_r <= 1'd0;
		end
	else 
		begin
			case(tx_cnt)
				10'd659:begin
					if(receive_over)
						begin
							tx_cnt <= 10'd0;
						end
					else
						begin
							tx_cnt <= tx_cnt;
						end
				end
				10'd0:begin
					tx_cnt 	<= tx_cnt + 1'd1;
					tx_r 	<= 1'd0;//发送起始位
				end
				10'd60:begin			
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= tx_data[0];//发送数据0位
				end
				10'd120:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= tx_data[1];//发送数据1位
				end
				10'd180:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= tx_data[2];//发送数据2位
				end
				10'd240:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= tx_data[3];//发送数据3位
				end	
				10'd300:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= tx_data[4];//发送数据4位
				end
				10'd360:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= tx_data[5];//发送数据5位
				end
				10'd420:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= tx_data[6];//发送数据6位
				end
				10'd480:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= tx_data[7];//发送数据7位
				end
				10'd540:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= 1'bz;		//发送校验位	
				end
				10'd600:begin
					tx_cnt <= tx_cnt + 1'd1;
					tx_r <= 1'd1;		//发送停止位
				end	
				10'd655:begin
					tx_cnt <= tx_cnt + 8'd1;
					tx_r <= 1'd1;		//一帧数据发送结束   
				end
				10'd656:begin
					tx_cnt <= tx_cnt + 8'd1;
					send_over_r <= 1'd1;
				end
				10'd658:begin
					tx_cnt <= 10'd659;
					send_over_r <= 1'd0;
				end
				default:begin
					tx_cnt <= tx_cnt + 8'd1;
					tx_r <= tx_r;
				end
			endcase
		end
end
	always @ (posedge clk2 or negedge rst_n)
	begin
		if(!rst_n)
			begin
				rx_cnt <= 10'd0;;
				uart_rdata_r <= 8'd0;
				uart_rdata <= 8'd0;
				parity_bit <= 1'd0;
				receive_over_r <= 1'd0;
			end
		else 
			begin
				case(rx_cnt)//判断信号线是否有变化,RX信号线正常情况下为高电平,有低电平表示可能是起始位
					10'd0:begin
						if(!rxd)
							rx_cnt <= rx_cnt + 1'd1;
						else
							rx_cnt <= rx_cnt;		
					end
					10'd30:begin//判断是否为起始位信号(信号线稳定后仍为低电平,为真正的数据起始位)
						if(!rxd)
							rx_cnt <= rx_cnt + 1'd1;
						else
							rx_cnt <= 8'd0;
					end
					//接收数据0位
					10'd90:begin
						uart_rdata_r[0] <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收数据1位
					10'd150:begin
						uart_rdata_r[1] <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收数据2位
					10'd210:begin
						uart_rdata_r[2] <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收数据3位
					10'd270:begin
						uart_rdata_r[3] <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收数据4位
					10'd330:begin
						uart_rdata_r[4] <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收数据5位
					10'd390:begin
						uart_rdata_r[5] <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收数据6位
					10'd450:begin
						uart_rdata_r[6] <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收数据7位
					10'd510:begin
						uart_rdata_r[7] <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收校验位(无校验)
					10'd570:begin
						parity_bit <= rxd;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//接收停止位
					10'd630:begin
						rx_cnt <= rx_cnt + 1'd1;
						uart_rdata <= uart_rdata_r;
					end
					//接收完成标志拉高两个时钟
					10'd650:begin
						receive_over_r <= 1'd1;
						rx_cnt <= rx_cnt + 1'd1;
					end
					10'd655:begin
						receive_over_r <= 1'd0;
						rx_cnt <= rx_cnt + 1'd1;
					end
					//判断是否发送完成
					10'd659:begin
						if(send_over)
							rx_cnt <= 10'd0;
						else
							rx_cnt <= rx_cnt;
					end
					default:begin
						rx_cnt <= rx_cnt + 1'd1;					
					end
				endcase
			end
	end
//-----------------------------------------------//
//通过识别接收到的字符,控制LED的显示状态,
//只有接收数据和ASCII"r,g,b"的十进制值相同时,才会改变LED的状态
reg	[2:0]	led;
always@(posedge clk2	or negedge rst_n)
	if(!rst_n)
		led<=3'b111;
	else case(uart_rdata)
			8'd114	:	led<=3'b011;//"r",led显示为红色
			8'd103	:	led<=3'b101;//"g",led显示为绿色
			8'd98	: 	led<=3'b110;//"b",led显示为蓝色
		default:
			led<=3'b111;			//如果非“r,g,b”,LED灭
	endcase
assign	fpga_led=led;

五、 实验步骤及实验结果

1、点击左侧栏“新建工程”按钮,设置工程名称为uart,目标器件型号为SL2S-25E-8U213C,设计输入选择“RTL描述”,然后点击“下一步”,弹出窗口直接点击“完成”按钮。

2、将之前实验中编写的rst_n.v文件复制到工程文件夹下。点击左侧栏“设计管理”按钮,弹出窗口中点击“新建文件”按钮,并在编辑区输入工程代码;然后点击保存并给文件命名。

3、以同样的方式,分别建立uart_rxd.v文件、uart_txd.v文件以及上层文件uart_ctrl.v;建立顶层文件uart.v并在顶层模块中例化各个子模块。

4、点击主界面的“IP管理”按钮,创建PLL IP核,或者直接将工程文件夹下的PLL IP核文件pll_frac.v文件复制到工程文件夹并添加进工程;在uart_ctrl.v文件中例化PLL IP 核。

5、点击左侧边栏的“工程属性”按钮,弹出窗口中点击“源文件”栏下的“+”号,将建立的源文件及IP核文件添加到工程中。然后点击“确定”按钮。

6、点击主界面的“RTL综合”按钮,综合完成后如果无报错,点击左侧栏的“物理约束”按钮,弹出窗口中选择“约束编辑器”,点击“确定”按钮。

7、在端口信号“位置”栏通过点击分别添加引脚信息,添加完成后点击“保存”按钮,然后点击“退出”。这里的rxd和txd引脚可以是FPGA引出来的任意两个引脚信号,本实验中使用了L6和M6两个引脚,如图16-3红框中所示;接线时注意FPGA的RX信号和串口功能模块的TX相连,同样,FPGA的TX和串口功能模块的RX相连。

8、点击左侧栏的“全部运行”按钮,进行全编译,并生成下载用的bit文件。

9、将XiST下载器连接到开发板的JTAG口,将ARM下载器连接到开发板的SWD接口,给iCore3L 供电。

10、点击HqFpga界面中的“下载/编程”按钮,进入FPGA下载界面;选定生成的bit文件,然后点击“下载”按钮,将程序烧录进FPGA中。 图16-3 添加物理约束绑定信号引脚

11、打开串口软件;在“设备管理器”中查看和iCore3L核心板相连的串口端口号,并将串口软件中的Port: 配置成此端口号(本人使用的COM4),波特率设置为115200,数据位8位;然后点击右上角的“open Port”按钮,打开端口。在发送区输入对应字符(小写字符“r、g、b”),然后点击右侧“(s)Send”按钮,即可看到开发板上FPGA-LED对应的颜色亮起;同时接收区显示接收到的字符(图16-4接收区中蓝色字样)。

12、至此,本实验成功完成。 图16-4 串口软件设置及串口通信测试

六、 拓展实验