ARTIX-7 XC7A35T实验项目之 串口发送

刷题推荐

链接: Verilog刷题

1,实验简介

UART关键参数及时序图
UART通信在使用前需要做多项设置,最常见的设置包括数据位数、波特率大小、奇偶校验类型和停止位数。
数据位(Data bits):该参数定义单个UART数据传输在开始到停止发送的数据位数。可选择5、6、7、8;
波特率(Baud):指从一设备发送到另一设备的波特率,即每秒钟可以通信的数据比特个数。典型的波特率有300,1200,2400,9600,19200,115200等。
奇偶校验类型(Parity Type):用来校验数据的正确性。
停止位(Stop bits):在每个字节的数据位发送完成后,发送停止位来标志一次数据传输完成,同时用来帮助接受信号方的硬件重同步。可选择1、1.5或者2位。
在RS-232标准中,最常用的配置是8N1,即8个数据位,无奇偶校验,1个停止位。
按照一个完整的字节包括1位起始位,8位数据位,1位停止位,总共10位数据来算,想要完整的实现这10位数据的发送,就需要11个波特率时钟脉冲,第1个脉冲标记一次传输的开始,第11个脉冲标记一次传输的结束。

2,实验环境

vivado 2019.1
黑金AX7035开发板

3,实验原理

黑金开发板采用USB转串口方案,其电路图如下图所示:
请添加图片描述

4,程序设计

由上面分析可以得到计数器模块的接口图及接口功能描述请添加图片描述

信号名称I/O功能描述
sys_clkI模块工作时钟50MHZ
rst_nI模块复位
data_byteI待传输8bit数据
send_enI发送使能信号
baud_setI波特率设置信号
uart_txO串口发送信号输出
tx_doneO发送结束信号,一个时钟高电平
uart_stateO发送状态,处于发送状态时为1

在波特率时钟生成模块中,计数器需要的计数值与波特率关系如下表所示,其中系统时钟为20ns。

baud_set波特率波特率周期波特率分频计数值50MHZ计数值
09600104167ns104167/205028-1
11920052083ns52083/202604-1
23840026041ns26041/201302-1
35760017361ns17361/20868-1
41152008680ns8680/20434-1

4.1,vivado工程创建【跳过】

4.2,编写计数器的Verilog代码

//TOP
`timescale 1ns / 1ps
module uart_byte_test(
    input sys_clk,
	input rst_n,

	output uart_tx,
	output led    
    );
    assign rst=~rst_n;

	wire send_en;    //发送使能
	wire [7:0]data_byte;  //待传输8bit数据
	wire test_en;    //按键标志信号
	reg test_en_dly1;
	reg test_en_dly2;

	always@(posedge sys_clk)
	begin
		test_en_dly1 <= test_en;
		test_en_dly2 <= test_en_dly1;
	end

  //VIO输出测试信号控制发送使能
	assign send_en = test_en_dly1 & !test_en_dly2;
	
	uart_byte_tx uart_byte_tx(
		.sys_clk(sys_clk),
		.rst_n(rst_n),

		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(3'd0),
		
		.uart_tx(uart_tx),
		.tx_done( ),
		.uart_state(led)
	);
endmodule
//Module
module uart_byte_tx(
    input sys_clk,
    input rst_n,
    input [7:0] data_byte,
    input send_en,
    input [2:0]baud_set,
    
    output reg uart_tx,
    output reg tx_done,
    output reg uart_state
    );
    assign rst = ~rst_n;
    localparam START_BIT = 1'b0;    //不可用于参数传递
    localparam STOP_BIT = 1'b1;
    
    reg bps_clk;                //波特率时钟
    reg [15:0] div_cnt;         //分频计数器
    reg [15:0] bps_DR;          //分频计数最大值
    reg [3:0] bps_cnt;          //波特率时钟计数器
    reg [7:0] data_byte_reg;    //data_byte寄存器
    
/*
产生数据传输状态信号,正常传输的时候uart_state信号为高电平,其他情况均为低电平。
*/ 
    always@(posedge sys_clk or posedge rst)
    if(rst)
        uart_state <= 1'b0;
    else if(send_en)
        uart_state <= 1'b1;
    else if(bps_cnt)
        uart_state <= 1'b0;
    else
        uart_state <= uart_state;
    
/*
由于串口是一个异步收发器,为了保证发送的数据在是时钟到来的时候是稳定的,需要
对输入数据进行寄存
*/     
    always@(posedge sys_clk or posedge rst)
    if(rst)
        data_byte_reg <= 8'd0;
    else if(send_en)
        data_byte_reg <= data_byte;
    else
        data_byte_reg <= data_byte_reg;

 /*
为了保证模块的复用性,当需要不同的波特率时,只需设置不同的波特率时钟计数器的
计数值,使用查找表即可实现。
*/    
    always@(posedge sys_clk or posedge rst)
    if(rst)
        bps_DR <= 16'd5027;
    else begin
        case(baud_set)
            0:bps_DR <= 16'd5027;    
            1:bps_DR <= 16'd2603;
			2:bps_DR <= 16'd1301;
			3:bps_DR <= 16'd867;
			4:bps_DR <= 16'd433;
			default:bps_DR <= 16'd5207;			
		endcase
    end
    
 /*
利用计数器来生成波特率时钟
*/  
    always@(posedge sys_clk or posedge rst)
	if(rst)
		div_cnt <= 16'd0;
	else if(uart_state)begin
		if(div_cnt == bps_DR)
			div_cnt <= 16'd0;
		else
			div_cnt <= div_cnt + 1'b1;
	end
	else
		div_cnt <= 16'd0;
    // bps_clk gen
	always@(posedge sys_clk or posedge rst)
	if(rst)
		bps_clk <= 1'b0;
	else if(div_cnt == 16'd1)
		bps_clk <= 1'b1;
	else
		bps_clk <= 1'b0;
	
 /*
通过对波特率时钟的计数,来确认数据bit位发送的数据量
*/  	
	always@(posedge sys_clk or posedge rst)
	if(rst)	
		bps_cnt <= 4'd0;
	else if(bps_cnt == 4'd11)
		bps_cnt <= 4'd0;
	else if(bps_clk)
		bps_cnt <= bps_cnt + 1'b1;
	else
		bps_cnt <= bps_cnt;
		
 /*
为了模块可以对其他模块进行控制或者调用,这里产生一个bit传输结束后tx_done输出
一个时钟的高电平
*/  			
	always@(posedge sys_clk or posedge rst)
	if(rst)
		tx_done <= 1'b0;
	else if(bps_cnt == 4'd11)
		tx_done <= 1'b1;
	else
		tx_done <= 1'b0;		

 /*
使用一个十选一多路器,作用是根据bps_cnt的值来确认数据传输的状态,在不同的波特率
时钟计数时,有对应的传输数据位
*/  		
	always@(posedge sys_clk or posedge rst)
	if(rst)
		uart_tx <= 1'b1;
	else begin
		case(bps_cnt)
			0:uart_tx <= 1'b1;
			1:uart_tx <= START_BIT;
			2:uart_tx <= data_byte_reg[0];
			3:uart_tx <= data_byte_reg[1];
			4:uart_tx <= data_byte_reg[2];
			5:uart_tx <= data_byte_reg[3];
			6:uart_tx <= data_byte_reg[4];
			7:uart_tx <= data_byte_reg[5];
			8:uart_tx <= data_byte_reg[6];
			9:uart_tx <= data_byte_reg[7];
			10:uart_tx <= STOP_BIT;
			default:uart_tx <= 1'b1;
		endcase
	end	
endmodule

4.3,vivado仿真验证

`timescale 1ns/1ns
`define CLK_PERIOD 20
module vtf_uart_txreg;
    reg clk;
	reg reset_n;
	reg [7:0]data_byte;
	reg send_en;
	reg [2:0]baud_set;
	
	wire uart_tx;
	wire tx_done;
	wire uart_state;
	
	uart_byte_tx uart_byte_tx(
		.clk(clk),
		.reset_n(reset_n),

		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(baud_set),

		.uart_tx(uart_tx),
		.tx_done(tx_done),
		.uart_state(uart_state)
	);
	
	initial clk = 1;
	always#(`CLK_PERIOD/2)clk = ~clk;
	
	initial begin
		reset_n = 1'b0;
		data_byte = 8'd0;
		send_en = 1'd0;
		baud_set = 3'd4;
		#(`CLK_PERIOD*500 + 1 )
		reset_n = 1'b1;
		#(`CLK_PERIOD*50);
    
    //send first byte
		data_byte = 8'haa;
		send_en = 1'd1;
		#`CLK_PERIOD;
		send_en = 1'd0;

		@(posedge tx_done)
		#(`CLK_PERIOD*5000);
    
    //send second byte
		data_byte = 8'h55;
		send_en = 1'd1;
		#`CLK_PERIOD;
		send_en = 1'd0;

		@(posedge tx_done)
		#(`CLK_PERIOD*5000);
		$stop;	
	end
endmodule

可以看出在复位和使能信号有效之前输出信号uart_tx均为1,在复位结束以及使能后输出信号才开始发送一组串口数据,且当待发送数据位0xaa时,串行输出信号依次为1,0(起始位),01010101b(LSB),1(停止位)。同时在发送串行数据过程中uart_state处于发送状态时为1,每个字节数据发送完成后,tx_done产生一个时钟周期脉冲,与设计的期望一致,仿真通过。
请添加图片描述

4.4,VIO控制串口发送

为了实现串口发送的目标,使用vivado在线调试工具VIO产生发送数据和控制使能信号,完成FPGA通过串口发送到PC端,通过串口工具显示。
VIO全称Virtual Input/Output,虚拟输入输出信号,主要用于上板调试时,建立一个虚拟的输入输出信号,可以对需要调试的模块的输出信号的数值进行在线查看,方便调试查找问题和验证模块的实际上板的正确性。

VIO在vivado是以一个IP Core的形式存在。使用时首先要配置生成一个VIO IP Core。
1、点击IP Catalog,搜索vio,双击Virtual Input/Output
请添加图片描述
2、进入到VIO设置界面
请添加图片描述
(1) Documentation: IP 相关文档入口
(2) IP Location:生成 IP 的存放路径,可以通过点击 … 设置更换存放路径,默认是存放
在工程路径下的 uart_tx.srcs\ sources_1\ ip,这里我们就保持默认。
(3) Switch to Default:点击后所有的设置恢复到默认值
(4) Component Name:设置生成 IP Core 的名称,这里我们保持默认即可。
(5)这里是一个提示,提示通过该界面设置最多可设置 64 个探针,如果想设置更多的探针
需要使用 Tcl 脚本命令去设置,这里就不做详细描述,想要知道具体用法的,可以查询 IP 手
册。
6) VIO 模块输入探针个数,这里调试串口发送模块,需要 VIO 产生输出一个 8bit 数据信
号和一个 1bit 的控制信号,不需要 input probe,这里设置个数为 0;
(7) VIO 模块输出探针个数,根据(6)描述,这里需要设置为 2;
(8)使能 Input Probe 的 Active detectors 功能,每个 VIO Croe 输入都有额外的单元来捕获
输入信号的变化。由于设计时钟(待捕获的信号所处的时钟)可能比分析仪的采样时钟要快,
因此被监测的信号可能在连续采样之间多次变化。 Active detectors 功能就是用来捕获此行为,
让其结果与 vivado 逻辑分析器中的值一起显示。当 Input Probe Count 设置为 0 时,这里是
不需要设置的,当 Input Probe Count 设置不为 0 时,这里按默认勾选即可。
(9)设置 Input Probe 信号的位宽,当 Input Probe Count 设置为 0 时,没有这里的设置,当
Input Probe Count 设置不为 0 时,根据实际需求进行位宽设置即可。由于(6)中 Input Probe
设置为 0,这里没有设置的地方。
(10)设置 Output Probe 信号的位宽和初始值,当 Output Probe Count 设置为 0 时,没有这
里的设置,当 Output Probe Count 设置不为 0 时,根据实际需求进行位宽和初始值设置即可。
这里调试串口发送模块,需要 VIO 产生输出一个 8bit 数据信号和一个 1bit 的控制信号,将
PROBE_OUT0 位宽设置为 1, PROBE_OUT2 位宽设置为 8 即可。
3、对上述参数设置后的结果,点击OK
请添加图片描述
4、在Source窗口点击IP Sources可以查看生成的VIO Core文件。
请添加图片描述
5、打开 .veo文件,可以看到该模板代码,具体例化模板代码如下

vio_0 vio_0(
.clk(clk),
.probe_out0(test_en),
.probe_out1(data_byte)
);

该 VIO 模块有 3 个信号,一个时钟信号 clk,二个 Output Probe 信号,时钟信号就是采样时钟, Output Probe 信号就是前面通过 IP 生成界面设置的信号,位宽分别为 1bit 和 8bit。
新建一个用于上板测试的模块文件命名为 uart_tx_test.v,并将其设置为顶层计。将 VIO 例化模板和前面设计的 uart_byte_tx 模块复制粘贴到顶层模块进行修改进行例化。

4.4,添加XDC管脚约束文件【跳过】

4.5,下载和Flash固化【跳过】

1、程序下载完成后的界面:
请添加图片描述/2、点击右边vio界面的+
请添加图片描述
3、选择信号,点击OK
请添加图片描述4、设置VIO模块输出信号test_en输出模式,右键test_en,选择Active-High Button。
请添加图片描述
信号窗口中 test_en 在 Value 一栏就变成了默认值为 0 的类似按钮的形式,当用鼠标点击这个“按钮”时, test_en 的值变为 1,松开鼠标后值变为 0,其功能和按键类似,注意这里设置的是 Active-High Button,表示按下是高电平,松开或默认是低电平(注:如果设置为Active-Low Button 则正好相反,表示按下是低电平,松开或默认是高电平,如果设置为ToggleButton:鼠标点击信号“按钮”,每点击一次,则该输出信号值翻转一次)。通过 test_en 信号
的上升沿作为串口发送的使能信号,这样就可以实现点击一下test_en“按钮”就发送一次数据,发送的数据是设置的 data_byte 的数值。
5、将开发板上USB转串口通过数据线与PC机相接请添加图片描述
6、在PC机打开一个串口软件,设置好串口号、波特率9600、数据位8bit、停止位1bit,点击启动串口,然后点击test_en,可以更改其他发送数据,每点击一次test_en,就会使能FPGA将data_byte数据发送出去,在串口上看到接收的相关数据。
请添加图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值