0
点赞
收藏
分享

微信扫一扫

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章 UART串口通信实验​

UART串口通信实验

串口是“串行接口”的简称,即采用串行通信方式的接口。串行通信将数据字节分成一位一位的形式在一条数据线上逐个传送,其特点是通信线路简单,但传输速度较慢。因此串口广泛应用于嵌入式、工业控制等领域中对数据传输速度要求不高的场合。本章我们将使用DFZU2EG/4EV MPSoC开发板上的UART串口完成上位机与MPSoC PL的通信。

本章包括以下几个部分:

  1. UART口简介
  2. 实验任务
  3. 硬件设计
  4. 程序设计
  5. 下载验证


UART串口简介

串行通信为两种方式:同步串行通信和异步串行通信。步串行通信需要通信双方在同一时钟的控制下,同步传输数据;异步串行通信是指通信双方使用各自的时钟控制数据的送和接收过程

UART是一种采用异步串行通信方式的通用异步收发传输器universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

UART通信线来实现,于串口发送,另外一根负责串口接收UART在发送或接收过程中的一帧数据由4部分组成起始数据位奇偶校验停止位,如所示。其中,起始位标志着一帧数据开始,停止位标志着一帧数据的结束数据位是一帧数据的有效数据校验位校验和偶校验用于检验数据传输过程是否出错校验时,发送方应使数据位中1的个数校验位中1个数之和为奇数;接收方在接收数据对1的个数进行检查,若不为数,则说明数据在传输过程中出了差错,偶校验检查1的个数是否为偶数。

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_系统时钟


14.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接口标准出现较早,实现双工作方式,即数据发送接收可以同时进行。在传输距离较不超过15mRS232是串行通信常用的接口标准,本章主要介绍针对RS-232标准UART串口通信。

RS-232标准的串口最常见的接口类型DB9,样式如,工业控制领域用到的工机一般都配多个串口,很多老式台式机也都配有串口。但记本电脑以及新一点的都没串口,它们一般通过USB转串口线14.1.4来实现外部设备串口通信

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_数据_02


14.1.2 DB9接口

DB9接口定义及各引脚功能说明如所示,我们一般只用到其中的2(RXD)、3(TXD、5(GND引脚,其他引脚在普通串口模式下一般不使用,如果大家想了解,可以自行百度下。

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_数据_03

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_串口_04


14.1.3 DB9接口定义

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_串口_05


14.1.4 USB Type-C接口

于传统的DB9接口体积较大,会占用开发板过多空间,在DFZU2EG/4EV MPSoC开发板上我们采用的是Type-C接口,样式如14.1.4所示,另一端直接和电脑USB相连。连接示意图如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_串口_06


14.1.5 USB串口通信示意图

实验任务

本节实验任务是上位机通过串口调试助手发送数据给MPSoC,MPSoC PL端通过UART串口接收数据将接收到的数据发送给上位机,完成串数据环回。

硬件设计

DFZU2EG/4EV MPSoC开发板上UART串口部分的原理图如下所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_串口_07


14.3.1 MPSoC UART串口原理图

原理图中CH340的工作电压为3.3V,所以需要加电平转换芯片XC6206P332MR将PL_VUSB转换为PL_UART_3V3给CH340模块使用。

14.3.1中可以看到,MPSoC的FPGA芯片的PL_UART1_TX连接到CH340RXD管脚,MPSoC的FPGA芯片的PL_UART1_RX连接到CH340TXD管脚,CH340的PL_CH340_P/N分别连接到开发板的USB Type-C插座D+/-上。CH340具体的工作原理是:发送的时候把FPGA的PL_UART1_TX管脚送出的信号由TTL电平转换为USB差分电平并送到USB Type-C插座,接收的时候则把USB Type-CB插座送来的USB差分电平转换为TTL电平送给FPGA的PL_UART1_RX管脚,同时我们通过USB数据线将USB Type-C插座D+/-直连到PC上相应的USB口的D+/-,当然也包括电源和地,这样就完成了USB串口通信的硬件准备工作。

本实验中,系统时钟、按键复位以及串口的接收、发送端口的管脚分配如下表所示:

14.3.1 串口通信实验管脚分配

信号名

方向

管脚

端口说明

电平标准

sys_clk_p

input

AE5

系统差分输入时钟

DIFF_HSTL_I_12

sys_clk_n

input

AF5

系统差分输入时钟

DIFF_HSTL_I_12

sys_rst_n

input

AH11

系统复位低有效

LVCMOS33

uart_rxd

input

AB9

口接收

LVCMOS33

uart_txd

output

AB10

口发送

LVCMOS33

对应的XDC约束语句如下所示:

#IO管脚约束
#时钟周期约束
create_clock -name sys_clk_p -period 10.000 [get_ports sys_clk_p]
#时钟
set_property IOSTANDARD DIFF_HSTL_I_12 [get_ports sys_clk_p]
set_property IOSTANDARD DIFF_HSTL_I_12 [get_ports sys_clk_n]
set_property PACKAGE_PIN AE5 [get_ports sys_clk_p]
set_property PACKAGE_PIN AF5 [get_ports sys_clk_n]
#复位
set_property -dict {PACKAGE_PIN AH11 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN AB9 IOSTANDARD LVCMOS33} [get_ports uart_rxd]
set_property -dict {PACKAGE_PIN AB10 IOSTANDARD LVCMOS33} [get_ports uart_txd]

程序设计

根据实验任务,我们不难想象本系统应该有一个串口接收模块,用来接收上位机发送的数据;还要有一个串口发送模块,用于将数据发回上位机;另外还应该有一个对数据进行环回控制的模块,它负责把从串口接收模块接收到的数据送给串口发送模块,以实现串口数据的环回。由此可以画出本次实验的系统图,如下

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_数据_08


14.4.1 系统框图

由系统总体框图知,MPSoC PL部分包括四个模块,顶层模块接收模块发送模块和数据环回模块其中在顶层模块中完成另外个模块的,顶层模块原理图如下所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_系统时钟_09


14.4.2 顶层模块原理图

中,uart_recv为串口接收模块,从串口接收端口uart_rxd来接收上位机发送的串行数据,并在一帧数据接收结束后给出通知信号uart_done

uart_send为串口发送模块,以uart_en为发送使能信号。uart_en的上升沿将启动一次串口发送过程,将uart_din接口上的数据通过串口发送端口uart_txd发送出去。

uart_loop模块负责完成串口数据的环回功能。它在uart_recv模块接收完成后,将接收到的串口数据发送到uart_send模块,并通过send_en接口给出一个上升沿,以启动发送过程。

在编写代码之前,们首先要确定串口通信的数据格式及波特率。在这里我们选择串口比较常用的一种模式,数据位为8位停止位为1位,无校验位,波特率为115200bps。则传输一帧数据的时序图如下图

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_串口_10


14.4.3 口通信时序图

顶层模块的代码如下:

1 module uart_loopback_top(
2 input sys_clk_p , //系统差分输入时钟
3 input sys_clk_n , //系统差分输入时钟
4 input sys_rst_n, //外部复位信号,低有效
5
6 (*mark_debug = "true"*)input uart_rxd, //UART接收端口
7 (*mark_debug = "true"*)output uart_txd //UART发送端口
8 );
9
10 //parameter define
11 parameter CLK_FREQ = 100000000; //定义系统时钟频率
12 parameter UART_BPS = 115200; //定义串口波特率
13
14 //wire define
15 (*mark_debug = "true"*)wire uart_recv_done; //UART接收完成
16 (*mark_debug = "true"*)wire [7:0] uart_recv_data; //UART接收数据
17 (*mark_debug = "true"*)wire uart_send_en; //UART发送使能
18 (*mark_debug = "true"*)wire [7:0] uart_send_data; //UART发送数据
19 (*mark_debug = "true"*)wire uart_tx_busy; //UART发送忙状态标志
20
21 //*****************************************************
22 //** main code
23 //*****************************************************
24
25 //转换差分信号
26 IBUFDS diff_clock
27 (
28 .I (sys_clk_p), //系统差分输入时钟
29 .IB(sys_clk_n), //系统差分输入时钟
30 .O (sys_clk) //输出系统时钟
31 );
32
33 //串口接收模块
34 uart_recv #(
35 .CLK_FREQ (CLK_FREQ), //设置系统时钟频率
36 .UART_BPS (UART_BPS)) //设置串口接收波特率
37 u_uart_recv(
38 .sys_clk (sys_clk),
39 .sys_rst_n (sys_rst_n),
40
41 .uart_rxd (uart_rxd),
42 .uart_done (uart_recv_done),
43 .uart_data (uart_recv_data)
44 );
45
46 //串口发送模块
47 uart_send #(
48 .CLK_FREQ (CLK_FREQ), //设置系统时钟频率
49 .UART_BPS (UART_BPS)) //设置串口发送波特率
50 u_uart_send(
51 .sys_clk (sys_clk),
52 .sys_rst_n (sys_rst_n),
53
54 .uart_en (uart_send_en),
55 .uart_din (uart_send_data),
56 .uart_tx_busy (uart_tx_busy),
57 .uart_txd (uart_txd)
58 );
59
60 //串口环回模块
61 uart_loop u_uart_loop(
62 .sys_clk (sys_clk),
63 .sys_rst_n (sys_rst_n),
64
65 .recv_done (uart_recv_done), //接收一帧数据完成标志信号
66 .recv_data (uart_recv_data), //接收的数据
67
68 .tx_busy (uart_tx_busy), //发送忙状态标志
69 .send_en (uart_send_en), //发送使能信号
70 .send_data (uart_send_data) //待发送数据
71 );
72
73 endmodule

在顶层模块中完成了对其余各个子模块的例化。需要注意的是,顶层模块中第11、12行定义了两个变量:系统时钟频率CLK_FREQ与串口波特率UART_BPS,使用时可以根据不同的系统时钟频率以及所需要的串口波特率设置这两个变量。我们可以尝试将串口波特率UART_BPS设置为其他值(如9600),在模块例化时会将这个变量传递到串口接收与发送模块中,从而实现不同速率的串口通信。

串口接收模块的代码如下所示:

1 module uart_recv(
2 input sys_clk, //系统时钟
3 input sys_rst_n, //系统复位,低电平有效
4
5 input uart_rxd, //UART接收端口
6 output reg uart_done, //接收一帧数据完成标志
7 output reg [7:0] uart_data //接收的数据
8 );
9
10 //parameter define
11 parameter CLK_FREQ = 50000000; //系统时钟频率
12 parameter UART_BPS = 9600; //串口波特率
13 localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,
14 //需要对系统时钟计数BPS_CNT次
15 //reg define
16 reg uart_rxd_d0;
17 reg uart_rxd_d1;
18 reg [15:0] clk_cnt; //系统时钟计数器
19 reg [ 3:0] rx_cnt; //接收数据计数器
20 reg rx_flag; //接收过程标志信号
21 reg [ 7:0] rxdata; //接收数据寄存器
22
23 //wire define
24 wire start_flag;
25
26 //*****************************************************
27 //** main code
28 //*****************************************************
29 //捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
30 assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
31
32 //对UART接收端口的数据延迟两个时钟周期
33 always @(posedge sys_clk or negedge sys_rst_n) begin
34 if (!sys_rst_n) begin
35 uart_rxd_d0 <= 1'b0;
36 uart_rxd_d1 <= 1'b0;
37 end
38 else begin
39 uart_rxd_d0 <= uart_rxd;
40 uart_rxd_d1 <= uart_rxd_d0;
41 end
42 end
43
44 //当脉冲信号start_flag到达时,进入接收过程
45 always @(posedge sys_clk or negedge sys_rst_n) begin
46 if (!sys_rst_n)
47 rx_flag <= 1'b0;
48 else begin
49 if(start_flag) //检测到起始位
50 rx_flag <= 1'b1; //进入接收过程,标志位rx_flag拉高
51 //计数到停止位中间时,停止接收过程
52 else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2))
53 rx_flag <= 1'b0; //接收过程结束,标志位rx_flag拉低
54 else
55 rx_flag <= rx_flag;
56 end
57 end
58
59 //进入接收过程后,启动系统时钟计数器
60 always @(posedge sys_clk or negedge sys_rst_n) begin
61 if (!sys_rst_n)
62 clk_cnt <= 16'd0;
63 else if ( rx_flag ) begin //处于接收过程
64 if (clk_cnt < BPS_CNT - 1)
65 clk_cnt <= clk_cnt + 1'b1;
66 else
67 clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零
68 end
69 else
70 clk_cnt <= 16'd0; //接收过程结束,计数器清零
71 end
72
73 //进入接收过程后,启动接收数据计数器
74 always @(posedge sys_clk or negedge sys_rst_n) begin
75 if (!sys_rst_n)
76 rx_cnt <= 4'd0;
77 else if ( rx_flag ) begin //处于接收过程
78 if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期
79 rx_cnt <= rx_cnt + 1'b1; //此时接收数据计数器加1
80 else
81 rx_cnt <= rx_cnt;
82 end
83 else
84 rx_cnt <= 4'd0; //接收过程结束,计数器清零
85 end
86
87 //根据接收数据计数器来寄存uart接收端口数据
88 always @(posedge sys_clk or negedge sys_rst_n) begin
89 if ( !sys_rst_n)
90 rxdata <= 8'd0;
91 else if(rx_flag) //系统处于接收过程
92 if (clk_cnt == BPS_CNT/2) begin //判断系统时钟计数器计数到数据位中间
93 case ( rx_cnt )
94 4'd1 : rxdata[0] <= uart_rxd_d1; //寄存数据位最低位
95 4'd2 : rxdata[1] <= uart_rxd_d1;
96 4'd3 : rxdata[2] <= uart_rxd_d1;
97 4'd4 : rxdata[3] <= uart_rxd_d1;
98 4'd5 : rxdata[4] <= uart_rxd_d1;
99 4'd6 : rxdata[5] <= uart_rxd_d1;
100 4'd7 : rxdata[6] <= uart_rxd_d1;
101 4'd8 : rxdata[7] <= uart_rxd_d1; //寄存数据位最高位
102 default:;
103 endcase
104 end
105 else
106 rxdata <= rxdata;
107 else
108 rxdata <= 8'd0;
109 end
110
111 //数据接收完毕后给出标志信号并寄存输出接收到的数据
112 always @(posedge sys_clk or negedge sys_rst_n) begin
113 if (!sys_rst_n) begin
114 uart_data <= 8'd0;
115 uart_done <= 1'b0;
116 end
117 else if(rx_cnt == 4'd9) begin //接收数据计数器计数到停止位时
118 uart_data <= rxdata; //寄存输出接收到的数据
119 uart_done <= 1'b1; //并将接收完成标志位拉高
120 end
121 else begin
122 uart_data <= 8'd0;
123 uart_done <= 1'b0;
124 end
125 end
126
127 endmodule

串口接收模块程序中2942行是一个经典的边沿检测电路,通过检测串口接收端uart_rxd的下降沿来捕获起始位。一旦检测到起始位,输出一个时钟周期的脉冲start_flag,并进入串口接收过程。串口接收状态用rx_flag来标志,rx_flag为高标志着串口接收过程正在进行,此时启动系统时钟计数器clk_cnt与接收数据计数器rx_cnt

由第13行的公式BPS_CNT = CLK_FREQ/UART_BPS可知,BPS_CNT为当前波特率下,串口传输一位所需要的系统时钟周期数。因此clk_cnt从零计数到BPS_CN-1时,串口刚好完成一位数据的传输。由于接收数据计数器rx_cnt在每次clk_cnt计数到BPS_CN-1时加1,因此由rx_cnt的值可以判断串口当前传输的是第几位数据。第87行至第109行就是根据clk_cnt的值将uart接收端口的数据寄存到接收数据寄存器对应的位,从而实现接收数据的串并转换。其中第92行选择clk_cnt计数至BPS_CNT/2时寄存接收端口数据,是因为计数到数据中间时的采样结果最稳定。

程序中需要额外注意的地方是串口接收过程结束条件的判定,由第52行可知,在计数到停止位中间时,标志位rx_flag就已经拉低。这样做是因为虽然此时一帧数据传输还没有完成(停止位只传送到一半),但是数据位已经寄存完毕。而在连续接收数据时,提前半个波特率周期结束接收过程可以为检测下一帧数据的起始位留出充足的时间。

我们使用上位机通过串口向开发板发送16进制数55,在串口接收过程中ILA抓取的波形图如下所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_系统时钟_11


14.4.4 串口接收过程波形图

14.4.4中红色的触发线标识出了串口接收端uart_rxd的起始位,在整个接收过程中rx_flag保持为高电平,同时rx_cnt对串口数据进行计数。当rx_cnt计数到9时,串口数据接收完成,uart_done拉高,同时uart_data给出接收到的数据。从图中可以看到,接收模块能够正确接收串口数据并完成串并转换。

串口发送模块代码如下所示:

1 module uart_send(
2 input sys_clk, //系统时钟
3 input sys_rst_n, //系统复位,低电平有效
4
5 input uart_en, //发送使能信号
6 input [7:0] uart_din, //待发送数据
7 output uart_tx_busy, //发送忙状态标志
8 output reg uart_txd //UART发送端口
9 );
10
11 //parameter define
12 parameter CLK_FREQ = 50000000; //系统时钟频率
13 parameter UART_BPS = 9600; //串口波特率
14 localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次
15
16 //reg define
17 reg uart_en_d0;
18 reg uart_en_d1;
19 reg [15:0] clk_cnt; //系统时钟计数器
20 reg [ 3:0] t x _ cnt; //发送数据计数器
21 reg tx_flag; //发送过程标志信号
22 reg [ 7:0] tx_data; //寄存发送数据
23
24 //wire define
25 wire en_flag;
26
27 //*****************************************************
28 //** main code
29 //*****************************************************
30 //在串口发送过程中给出忙状态标志
31 assign uart_tx_busy = tx_flag;
32
33 //捕获uart_en上升沿,得到一个时钟周期的脉冲信号
34 assign en_flag = (~uart_en_d1) & uart_en_d0;
35
36 //对发送使能信号uart_en延迟两个时钟周期
37 always @(posedge sys_clk or negedge sys_rst_n) begin
38 if (!sys_rst_n) begin
39 uart_en_d0 <= 1'b0;
40 uart_en_d1 <= 1'b0;
41 end
42 else begin
43 uart_en_d0 <= uart_en;
44 uart_en_d1 <= uart_en_d0;
45 end
46 end
47
48 //当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程
49 always @(posedge sys_clk or negedge sys_rst_n) begin
50 if (!sys_rst_n) begin
51 tx_flag <= 1'b0;
52 tx_data <= 8'd0;
53 end
54 else if (en_flag) begin //检测到发送使能上升沿
55 tx_flag <= 1'b1; //进入发送过程,标志位tx_flag拉高
56 tx_data <= uart_din; //寄存待发送的数据
57 end
58 //计数到停止位结束时,停止发送过程
59 else if ((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT -(BPS_CNT/16))) begin
60 tx_flag <= 1'b0; //发送过程结束,标志位tx_flag拉低
61 tx_data <= 8'd0;
62 end
63 else begin
64 tx_flag <= tx_flag;
65 tx_data <= tx_data;
66 end
67 end
68
69 //进入发送过程后,启动系统时钟计数器
70 always @(posedge sys_clk or negedge sys_rst_n) begin
71 if (!sys_rst_n)
72 clk_cnt <= 16'd0;
73 else if (tx_flag) begin //处于发送过程
74 if (clk_cnt < BPS_CNT - 1)
75 clk_cnt <= clk_cnt + 1'b1;
76 else
77 clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零
78 end
79 else
80 clk_cnt <= 16'd0; //发送过程结束
81 end
82
83 //进入发送过程后,启动发送数据计数器
84 always @(posedge sys_clk or negedge sys_rst_n) begin
85 if (!sys_rst_n)
86 tx_cnt <= 4'd0;
87 else if (tx_flag) begin //处于发送过程
88 if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期
89 tx_cnt <= tx_cnt + 1'b1; //此时发送数据计数器加1
90 else
91 tx_cnt <= tx_cnt;
92 end
93 else
94 tx_cnt <= 4'd0; //发送过程结束
95 end
96
97 //根据发送数据计数器来给uart发送端口赋值
98 always @(posedge sys_clk or negedge sys_rst_n) begin
99 if (!sys_rst_n)
100 uart_txd <= 1'b1;
101 else if (tx_flag)
102 case(tx_cnt)
103 4'd0: uart_txd <= 1'b0; //起始位
104 4'd1: uart_txd <= tx_data[0]; //数据位最低位
105 4'd2: uart_txd <= tx_data[1];
106 4'd3: uart_txd <= tx_data[2];
107 4'd4: uart_txd <= tx_data[3];
108 4'd5: uart_txd <= tx_data[4];
109 4'd6: uart_txd <= tx_data[5];
110 4'd7: uart_txd <= tx_data[6];
111 4'd8: uart_txd <= tx_data[7]; //数据位最高位
112 4'd9: uart_txd <= 1'b1; //停止位
113 default: ;
114 endcase
115 else
116 uart_txd <= 1'b1; //空闲时发送端口为高电平
117 end
118
119 endmodule

串口发送模块与串口接收模块异曲同工,代码中也给出了详尽的注释,此处不再赘述。需要注意的是,在程序的59行,我们将tx_flag提前1/16个停止位拉低,是为了确保发送模块发送数据的时间略小于接收模块接收数据的时间,否则当连续传输大量数据时,发送数据的时间会不断累积,最终导致在做串口环回实验时丢失数据。

尽管串口发送数据只是接收数据的反过程,理论上在传输的时间上是一致的,考虑到我们模块里计算波特率会有较小的偏差,并且串口对端的通信设备(如电脑等)收发数据的波特率同样可能会出现较小的偏差,因此这里为了确保环回实验的成功,这里将发送模块的停止位略微提前结束。

需要说明的是,较小偏差的波特率在串口通信时是允许的,同样可以保证数据可靠稳定的传输。

另外我们在代码的第31行将用于标志串口发送过程的tx_flag信号赋值给uart_tx_busy,并通过模块端口输出。这样其他模块就可以通过检测uart_tx_busy信号是否为低电平,从而判断串口发送模块是否处于空闲状态。若uart_tx_busy为高电平,那么uart_send正处于发送过程,外部模块需要等待当前发送过程结束之后,才能通过uart_en信号的上升沿来启动新的发送过程。

14.4.5为串口发送过程中ILA抓取的波形图,我们使用开发板通过串口向上位机向发送16进制数55。图中绿色的触发线标识出了串口发送使能信号uart_en的上升沿。在检测到uart_en的上升沿后,en_flag会拉高一个时钟周期,此时将uart_din端口上的待发送数据寄存到tx_data中,并进入串口发送过程。

在整个发送过程中tx_flag保持为高电平,t x _cnt对串口数据进行计数,同时tx_data的各个数据位依次通过串口发送端uart_txd发送出去。当t x _cnt计数到9时,串口数据发送完成,开始发送停止位。在一个波特率周期的停止位发送完成后,串口发送过程结束,uart_tx_busy信号拉低,表明串口发送模块进入空闲状态。

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_系统时钟_12


14.4.5 串口发送过程波形图

14.4.5中可以看出串口发送模块能够完成并串转换并正确发送串口数据。

在介绍完串口的接收和发送模块后,最后我们来看一下串口环回模块的代码:

1 module uart_loop(
2 input sys_clk, //系统时钟
3 input sys_rst_n, //系统复位,低电平有效
4
5 input recv_done, //接收一帧数据完成标志
6 input [7:0] recv_data, //接收的数据
7
8 input tx_busy, //发送忙状态标志
9 output reg send_en, //发送使能信号
10 output reg [7:0] send_data //待发送数据
11 );
12
13 //reg define
14 reg recv_done_d0;
15 reg recv_done_d1;
16 reg tx_ready;
17
18 //wire define
19 wire recv_done_flag;
20
21 //*****************************************************
22 //** main code
23 //*****************************************************
24
25 //捕获recv_done上升沿,得到一个时钟周期的脉冲信号
26 assign recv_done_flag = (~recv_done_d1) & recv_done_d0;
27
28 //对发送使能信号recv_done延迟两个时钟周期
29 always @(posedge sys_clk or negedge sys_rst_n) begin
30 if (!sys_rst_n) begin
31 recv_done_d0 <= 1'b0;
32 recv_done_d1 <= 1'b0;
33 end
34 else begin
35 recv_done_d0 <= recv_done;
36 recv_done_d1 <= recv_done_d0;
37 end
38 end
39
40 //判断接收完成信号,并在串口发送模块空闲时给出发送使能信号
41 always @(posedge sys_clk or negedge sys_rst_n) begin
42 if (!sys_rst_n) begin
43 tx_ready <= 1'b0;
44 send_en <= 1'b0;
45 send_data <= 8'd0;
46 end
47 else begin
48 if(recv_done_flag)begin //检测串口接收到数据
49 tx_ready <= 1'b1; //准备启动发送过程
50 send_en <= 1'b0;
51 send_data <= recv_data; //寄存串口接收的数据
52 end
53 else if(tx_ready && (~tx_busy)) begin //检测串口发送模块空闲
54 tx_ready <= 1'b0; //准备过程结束
55 send_en <= 1'b1; //拉高发送使能信号
56 end
57 end
58 end
59
60 endmodule

uart_loop模块的代码比较简单,首先代码的25至38行实现了上升沿检测功能。当检测到recv_done信号的上升沿时,recv_done_flag输出一个时钟周期的高电平,标志着串口接收模块接收到了一帧数据。在代码的48至52行,在判断到recv_done_flag为高电平时,寄存接收到的数据recv_data到send_data中;同时将tx_ready信号拉高,表示已经准备好了待发送的数据。另外还要将send_en信号拉低,为接下来产生一个上升沿作准备。

uart_loop模块还有一个输入信号tx_busy,它是由串口发送模块所输出,该信号为高电平表示串口发送模块正处于发送过程中。在代码的第53行,当tx_ready信号为高电平时,如果检测到tx_busy为低电平,则说明串口发送模块处于空闲状态。此时将send_en信号拉高,由此产生一个上升沿,以启动串口发送模块的发送过程,将寄存到send_data中的数据发送出去。于此同时,将tx_ready信号拉低,等待下一个串口接收数据的到来。

下载验证

编译工程并生成比特流.bit文件。接下来我们下载程序,验证上位机与开发板通过UART串口进行串口数据环回功能。

首先我们需要准备一个Type-C接口的USB转串口线,将USB接口一端插入电脑上的USB一端Type-C接口与开发板上的PL_UART接口相连接.

然后分别连接JTAG接口和电源线,最后拨动开关按键给开发板上电。

硬件连接如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_系统时钟_13


14.5.1 开发板PL端串口连接

注意上位机第一次使用USB转串口线与FPGA开发板连接时,需要安装USB串口驱动。在开发板随附的资料中找到“6_软件资料/1_软件/CH340驱动(USB串口驱动)”的文件,双击打开文件的“SETUP.EXE”进行安装,驱动安装界面所示。界面中提示INF文件为CH341SER.INF,我们不需要理会(CH341,CH340驱动共用的),直接点安装即可。

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_数据_14


14.5.2 USB口驱动驱动安装界面

开发板电源打开后,将本次实验的bit文件下载到开发板中。

接下来打开口助手。口助手是上位机中用于辅助串口调试小工,可以选择安装使用开发板随附资料中6_软件资料/1_软件/串口调试助手文件夹中提供的XCOM串口助手。在上位机中打开串口助手XCOM V2.0,如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_系统时钟_15


14.5.3 口助手操作界面

串口助手中选择与开发板相连接的CH340虚拟串,具体的端口号(这里是COM4)需要根据实际情况选择计算机设备中查看,如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》第十四章   UART串口通信实验​_数据_16


14.5.4 电脑的设备管理器窗口

在串口助手中设置波特率为115200,数据位为8,停止位为1,无校验位,最后确认打开串口。

串口打开后,在发送文本框中输入数据“5A”并点击发送,可以看到串口助手中接收到数据“5A”,如所示。串口助手接收到的数据与发送的数据一致,说明程序所实现的口数据功能验证成功。


举报

相关推荐

0 条评论