• 【小月电子】FPGA开发板(XLOGIC_V1)系统学习教程-LESSON7


    串口通信例程讲解

    在这里插入图片描述

    若要观看该博客配套的视频教程,可点击此链接

    根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤。比如非常简单的项目,我们可以省去虚线框里面的步骤,但是我们的入门级课程,即使再简单,也按照这12个步骤来进行讲解。

    1. 需求解读

    1.1 需求

    通过串口控制8个LED灯,波特率9600,比如通过串口下发8’h55,开发板上亮4盏LED灯,灭4盏LED灯。同时串口助手上会收到8’h55

    1.2 知识背景

        串口是“串行接口”的简称,即采用串行通信方式的接口。串行通信将数据字节分成一位一位的形式在一条数据线上逐个传送,其特点是通信线路简单,但传输速度较慢。因此串口广泛应用于嵌入式、工业控制等领域中对数据传输速度要求不高的场合。
        串行通信分为两种方式:同步串行通信和异步串行通信。同步串行通信需要通信双方在同一时钟的控制下,同步传输数据;异步串行通信是指通信双方使用各自的时钟控制数据的发送和接收过程。
        UART是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
        UART串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。 UART在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、 奇偶校验位和停止位,具体时序如图1所示。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错。奇校验时,发送方应使数据位中1的个数与校验位中1的个数之和为奇数;接收方在接收数据时,对1的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。同样,偶校验则检查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标准的UART串口通信。
        RS-232标准的串口最常见的接口类型为DB9,样式如图2所示,工业控制领域中用到的工控机一般都配备多个串口,很多老式台式机也都配有串口。但是笔记本电脑以及较新一点的台式机都没有串口,它们一般通过USB转串口线(图3)来实现与外部设备的串口通信。
    在这里插入图片描述

    图1. 异步串口时序图

    在这里插入图片描述

    图2. DB9接头

    在这里插入图片描述

    图3. USB串口线

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

    图4. DB9接口定义
    波特率9600bps:每秒传输9600bit。 传输1bit的时间为:1/9600(秒)=104167ns 也就是5208个时钟周期,需要采集1次 以上只是介绍串口相关的知识,我们XLOGIC开发板上板载了USB转TTL芯片(CH340),只用一根USB线即可与电脑进行串口通信。

    1.3 硬件设计

    在这里插入图片描述

    图5.有源晶振

    在这里插入图片描述

    图6.串口芯片电路

    在这里插入图片描述

    图7.对应的FPGA管脚

    1.4 接口说明

    信号名方向FPGA管脚号说明
    CLK50M输入B10时钟信号,50MHZ
    FPGA_RX输入H14串口信号输入
    FPGA_TX输出F14串口信号输出

        总结:通过上述说明,可以将需求解读成, 串口助手按波特率9600下发一个数,通过串口发送端口发送给FPGA,FPGA根据串口时序将这个数解析出来,然后将这个数赋值给8个LED即可。同时,FPGA再通过串口发送端口将这个数发送出去,这样串口助手上便可显示发送出去的数据。

    2. 绘制理论波形图

    在这里插入图片描述

    程序框图

    在这里插入图片描述

    串口接收理论波形图

    在这里插入图片描述

    串口发送理论波形图

    3.新建ISE工程

    为了让工程看起来整洁,同时方便工程移植。我们新建4个文件夹,分别是Project,Source,Sim,Doc。
    Project — 工程文件夹,里面放的ISE工程
    Source — 源代码文件夹,里面放的工程源码(.v文件或.vhd文件)
    Sim — 仿真文件夹,里面放的仿真相关的文件
    Doc — 存放相关资料,比如数据手册,需求文档等

    4.编写代码

    4.1 串口接收模块代码

    ///
    //QQ:3181961725
    //TEL/WX:13540738439
    //作者:Mr Wang
    //模块介绍:实现异步串口接收功能
    ///
    module async_uart_rev(
    	input				rst_n	,//复位信号,低电平有效
    	input				clk		,//时钟信号,50MHZ
    	input				rxd		,//串行接收数据
    	output	reg	[7:0]	rev_data,//并行数据
    	output	reg			rev_dvld //并行数据有效标志
    	);
    	parameter	baud_num=5207;//1/9600*1000000000/20
    	parameter	IDLE		=4'd0;
    	parameter	START_ST    =4'd1;
    	parameter	STOP_ST     =4'd2;
    	reg	[12:0]	baud_cnt;
    	reg			baud_cnt_en;
    	wire		sample_en;
    	reg	[3:0]	sample_num;
    	reg			rxd_ff1;
    	reg			rxd_ff2;
    	reg	[3:0]	curr_st;
    	//打两拍操作
    	always@(posedge clk)rxd_ff2<=rxd_ff1;
    	always@(posedge clk)rxd_ff1<=rxd;
    	assign	sample_en=(baud_cnt==baud_num[12:1])?1'b1:1'b0;
    	//状态机跳转程序
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)
    			curr_st<=IDLE;
    		else case(curr_st)
    			IDLE:begin
    				if(rxd_ff2==0)
    					curr_st<=START_ST;
    				else;
    			end
    			START_ST:begin
    				if(sample_num==8&&sample_en)
    					curr_st<=STOP_ST;
    				else;
    			end
    			STOP_ST:begin
    				if(rxd_ff2==1&&sample_en)
    					curr_st<=IDLE;
    				else;
    			end
    			default:;
    		endcase
    	end
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)
    			baud_cnt<=0;
    		else if(curr_st==START_ST||curr_st==STOP_ST)begin
    			if(baud_cnt==baud_num)
    				baud_cnt<=0;
    			else 
    				baud_cnt<=baud_cnt+1;
    		end else
    			baud_cnt<=0;
    	end
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)	
    			sample_num<=0;
    		else if(sample_en&&sample_num==9)
    			sample_num<=0;
    		else if(sample_en)
    			sample_num<=sample_num+1;
    		else;
    	end
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)
    			rev_data<=0;
    		else if(sample_en)
    			case(sample_num)
    			1:rev_data[0]<=rxd_ff2;
    			2:rev_data[1]<=rxd_ff2;
    			3:rev_data[2]<=rxd_ff2;
    			4:rev_data[3]<=rxd_ff2;
    			5:rev_data[4]<=rxd_ff2;
    			6:rev_data[5]<=rxd_ff2;
    			7:rev_data[6]<=rxd_ff2;
    			8:rev_data[7]<=rxd_ff2;
    			default:;
    		endcase
    	end
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)	
    			rev_dvld<=0;
    		else if(sample_num==9&&sample_en)
    			rev_dvld<=1;
    		else
    			rev_dvld<=0;
    	end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    4.2 串口发送模块代码

    ///
    //QQ:3181961725
    //TEL/WX:13540738439
    //作者:Mr Wang
    //模块介绍:实现异步串口发送功能
    ///
    module async_uart_tran(
    	input			rst_n		,//复位信号,低电平有效
    	input			clk			,//时钟,50MHZ
    	input	[7:0]	tran_data	,//输入的并行数据
    	input			tran_dvld	,//输入的并行数据有效标志
    	output	reg		txd          //串行输出数据
    	);
    	parameter	baud_num=5207;//1/9600*1000000000/20
    	parameter	IDLE		=4'd0;
    	parameter	DATA_ST    =4'd1;
    	parameter	START_ST    =4'd2;
    	parameter	STOP_ST     =4'd3;
    	reg	[12:0]	baud_cnt;
    	reg			baud_cnt_en;
    	wire		sample_en;
    	reg	[3:0]	sample_num;
    	reg	[3:0]	curr_st;
    
    	assign	sample_en=(baud_cnt==baud_num)?1'b1:1'b0;
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)
    			curr_st<=IDLE;
    		else case(curr_st)
    			IDLE:begin
    				if(tran_dvld==1)
    					curr_st<=START_ST;
    				else;
    			end
    			START_ST:begin
    				if(sample_en==1)
    					curr_st<=DATA_ST;
    			end
    			DATA_ST:begin
    				if(sample_en&&sample_num==8)
    					curr_st<=STOP_ST;
    				else;
    			end
    			STOP_ST:begin
    				if(sample_en==1)
    					curr_st<=IDLE;
    				else;
    			end
    			default:;
    		endcase
    	end
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)
    			baud_cnt<=0;
    		else if(curr_st==START_ST||curr_st==DATA_ST||curr_st==STOP_ST)begin
    			if(baud_cnt==baud_num)
    				baud_cnt<=0;
    			else 
    				baud_cnt<=baud_cnt+1;
    		end else
    			baud_cnt<=0;
    	end
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)	
    			sample_num<=0;
    		else if(curr_st==IDLE)
    			sample_num<=0;
    		else if(sample_en)
    			sample_num<=sample_num+1;
    		else;
    	end
    	always@(posedge clk or negedge rst_n)begin
    		if(!rst_n)
    			txd<=1;
    		else if(sample_en)
    			case(sample_num)
    			0:txd<=1'b0;
    			1:txd<=tran_data[0];
    			2:txd<=tran_data[1];
    			3:txd<=tran_data[2];
    			4:txd<=tran_data[3];
    			5:txd<=tran_data[4];
    			6:txd<=tran_data[5];
    			7:txd<=tran_data[6];
    			8:txd<=tran_data[7];
    			9:txd<=1'b1;
    			default:txd<=1;
    		endcase
    	end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    4.3 顶层模块代码

    ///
    //QQ:3181961725
    //TEL/WX:13540738439
    //作者:Mr Wang
    //模块介绍:顶层模块,例化接收和发送模块
    ///
    module async_uart_top(
    	input	clk			,//时钟,50MHZ
    	input	rst_n		,//复位信号,低电平有效
    	input	rxd			,//串行接收数据
    	output	txd			,//串行发送数据
    	output	[7:0] led
    	);
    	wire	[7:0]	rev_data;
    	wire			rev_dvld;
    	assign	led=rev_data;
    	async_uart_rev Uasync_uart_rev(
    	.rst_n		(rst_n),
    	.clk		(clk),
    	.rxd		(rxd),
    	.rev_data	(rev_data),
    	.rev_dvld   (rev_dvld)
    	);
    	async_uart_tran async_uart_tran(
    	.rst_n		(rst_n),
    	.clk		(clk),
    	.tran_data	(rev_data),
    	.tran_dvld	(rev_dvld),
    	.txd        (txd)
    	);
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    5.编写仿真测试激励文件

    在这里插入图片描述

    仿真框图

    仿真测试激励文件(TB文件)

    `timescale 1ns/1ns
    module async_uart_top_tb;
    	reg					clk		;
    	reg					rst_n	;
    	wire				rxd		;
    	wire				tran_dvld;
    	reg	[17:0]	cnt=0;
    initial
    begin
    	clk = 0;
    	rst_n=0;
    	#1000
    	rst_n=1;
    end
    always #10 clk=~clk;
    always@(posedge clk)cnt<=cnt+1;
    assign	tran_dvld=(cnt==100)?1'b1:1'b0;
    async_uart_tran Uasync_uart_tran(
    	.rst_n		(rst_n),
    	.clk		(clk),
    	.tran_data	(8'h55),
    	.tran_dvld	(tran_dvld),
    	.txd        (rxd)
    	);
    async_uart_top async_uart_top(
    	.clk	(clk),
    	.rst_n	(rst_n),
    	.rxd	(rxd),
    	.txd	(),
    	.led    ()
    	);
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    6.Modelsim仿真

    Modelsim仿真一般有两种方法

    1. 图形化界面仿真,即所有的操作都是在Modelsim软件界面上来完成,该方式的优点是,简单易学,适用于简单的项目,缺点是操作步骤繁琐。

    2. 批处理仿真,这种方式在仿真前需要编写相应的脚本文件,该方式的优点是,一键即可完成仿真,省时省力,缺点是前期需要编写脚本文件。前两讲采用的是图形化界面仿真的方式;为了更贴近工程实际,从第三讲开始,我们就采用批处理方式仿真。具体操作步骤可参考我们的视频教程
      仿真出的波形如下图所示:
      在这里插入图片描述

    7.对比波形图

    将第二步绘制的理论波形图与第六步Modelsim仿真出来的波形图进行对比,结果一致,说明我们的逻辑设计是正确的。如果发现比对结果不一致,就需要找到不一致的原因,最终要保证对比结果一致。通过对比,理论波形与仿真波形一致,说明功能符合设计要求。

    8.绑定管脚(编写UCF文件)

    NET "clk" TNM_NET = "clk";
    TIMESPEC TS_sys_clk_i = PERIOD "clk" 20 ns HIGH 50 %;
    NET "clk" 			LOC = B10	| IOSTANDARD = LVCMOS33 ;
    
    NET "rst_n" 		LOC = B3	| IOSTANDARD = LVCMOS33;
    NET "rxd" 			LOC = H14	| IOSTANDARD = LVCMOS33;
    NET "txd" 			LOC = F14	| IOSTANDARD = LVCMOS33;
    NET "led[7]" 		LOC = L12	| IOSTANDARD = LVCMOS33;
    NET "led[6]" 		LOC = T13	| IOSTANDARD = LVCMOS33;
    NET "led[5]" 		LOC = R12	| IOSTANDARD = LVCMOS33;
    NET "led[4]" 		LOC = T12	| IOSTANDARD = LVCMOS33;
    NET "led[3]" 		LOC = P11	| IOSTANDARD = LVCMOS33;
    NET "led[2]" 		LOC = J11	| IOSTANDARD = LVCMOS33;
    NET "led[1]" 		LOC = K11	| IOSTANDARD = LVCMOS33;
    NET "led[0]" 		LOC = G11	| IOSTANDARD = LVCMOS33;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    9.添加.v和.ucf文件

    在这里插入图片描述

    10.编译综合,同时将未使用管脚设置为悬空状态

    1.设置未使用管脚为悬空状态
    在这里插入图片描述
    在这里插入图片描述

    2.编译综合
    在这里插入图片描述

    11.下载BIT文件

    编译综合成功后便可以将生成的BIT文件下载到开发板(记得插上下载器,同时开发板上电)
    1.打开IMPACT
    在这里插入图片描述

    2.搜索器件

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    3.选择bit文件

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    下载成功后,便可以观察到开发板上的实验现象,如果实验现象与设计需求相符,那说明我们的设计是没有问题的,即可进行下一步生成MCS文件

    12.生成MCS文件,同时固化到配置芯片中

    FPGA有一个特性,就是掉电后配置信息会丢失,所以我们需要将配置信息存储在配置芯片(FLASH)中,待开发板上电后,FPGA便会读取配置芯片中的配置信息,这样开发板掉电再上电后同样可正常工作。要将程序固化到配置芯片,需要先生成MCS文件。
    BIT文件转换成MCS文件步骤:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    固化MCS文件
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    固化成功后,开发板断电再重新上电,可以观察到开发板仍然可以执行刚刚的功能。

  • 相关阅读:
    Python测试框架Pytest的基础入门
    C++ 两种实例化方式
    ros-python学习样例笔记
    使用Pytorch构建神经网络
    java计算机毕业设计智能化管理的仓库管理源码+mysql数据库+系统+lw文档+部署
    mysql的行锁和间隙锁
    异常数据检测 | Python基于Hampel的离群点检测
    [SpringBoot]基于jasypt-spring-boot-starter对配置加解密
    Python3用OpenCV4连接图像
    树状图怎么画?推荐这个好用的在线树状图软件!
  • 原文地址:https://blog.csdn.net/Moon_3181961725/article/details/126712213