【小月电子】XILINX FPGA开发板(XLOGIC_V1)系统学习教程-LESSON9简易测试系统
测试系统例程讲解
若要观看该博客配套的视频教程,可点击此链接
我们先来看一下我们做实验的开发板长啥样,颜值还可以吧,如果想学习XLOGIC_V1开发板的工程文件和视频教程,可以直接联系技术工程师,感谢您的关注!
根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤。比如非常简单的项目,我们可以省去虚线框里面的步骤,但是我们的入门级课程,即使再简单,也按照这12个步骤来进行讲解。
1. 需求解读
1.1 需求
采用C#做一个上位机界面,通过异步串口可控制开发板上的数码管,LED灯,蜂鸣器,同时按下开发板上的按键,可在上位机界面上显示当前被按下是哪个按键。该实验即是一个最简单的工控设备,应用面非常广。
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 硬件设计
这个系统我们会用到开发板上基本所有的外设,原理图就不截取出来了,大家可以直接看我们提供的开发板原理图
1.4 接口说明
信号名 | 方向 | FPGA管脚号 | 说明 |
---|---|---|---|
CLK50M | 输入 | B10 | 时钟信号,50MHZ |
FPGA_RX | 输入 | H14 | 串口信号输入 |
FPGA_TX | 输出 | F14 | 串口信号输出 |
SMG_W0 | 输出 | E12 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W1 | 输出 | B15 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W2 | 输出 | E15 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W3 | 输出 | H11 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W4 | 输出 | K16 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W5 | 输出 | K14 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_A | 输出 | F13 | 数码管段选控制信号,低电平点亮该段 |
SMG_B | 输出 | B16 | 数码管段选控制信号,低电平点亮该段 |
SMG_C | 输出 | J16 | 数码管段选控制信号,低电平点亮该段 |
SMG_D | 输出 | J13 | 数码管段选控制信号,低电平点亮该段 |
SMG_E | 输出 | G14 | 数码管段选控制信号,低电平点亮该段 |
SMG_F | 输出 | E13 | 数码管段选控制信号,低电平点亮该段 |
SMG_G | 输出 | G12 | 数码管段选控制信号,低电平点亮该段 |
SMG_DP | 输出 | J14 | 数码管段选控制信号,低电平点亮该段 |
KEY1 | 输出 | E4 | 独立按键,按下低电平 |
KEY2 | 输出 | B4 | 独立按键,按下低电平 |
KEY3 | 输出 | C3 | 独立按键,按下低电平 |
KEY4 | 输出 | D3 | 独立按键,按下低电平 |
KEY5 | 输出 | E3 | 独立按键,按下低电平 |
KEY6 | 输出 | H4 | 独立按键,按下低电平 |
LED1 | 输出 | L12 | 与LED灯相连,低电平LED灯亮 |
LED2 | 输出 | T13 | 与LED灯相连,低电平LED灯亮 |
LED3 | 输出 | R12 | 与LED灯相连,低电平LED灯亮 |
LED4 | 输出 | T12 | 与LED灯相连,低电平LED灯亮 |
LED5 | 输出 | P11 | 与LED灯相连,低电平LED灯亮 |
LED6 | 输出 | J11 | 与LED灯相连,低电平LED灯亮 |
LED7 | 输出 | K11 | 与LED灯相连,低电平LED灯亮 |
LED8 | 输出 | G11 | 与LED灯相连,低电平LED灯亮 |
BEEP | 输出 | F14 | PWM方波,用于驱动无源蜂鸣器 |
1.5 通信协议
这一讲我们需要做一个测试系统,用上位机软件控制我们的开发板,那么就需要拟定控制协议。这个协议是自己定义的,只要保证上位机和下位机的程序都是按这个协议来写的程序就行,这样才能正确控制。
- 数码管控制协议-下发(上位机发给下位机)
2.LED灯控制协议-下发(上位机发给下位机)
3.无源蜂鸣器控制协议-下发(上位机发给下位机)
4.按键检测协议-回传(下位机发给上位机)
以上便是我们该项目的通信协议,一个很简单的通信协议,起到抛砖引玉的作用,当控制系统复杂时,我们在定协议的时候需要考虑很多的问题。像我们现在这个简单的控制系统,协议非常简单,仅用于大家学习,便于让大家明白一个控制系统是如何工作的。
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 key_xd(input clk,input rst_n,input key_in,output reg key_out);
reg [3:0] curr_st;
reg [31:0] wait_cnt;
reg key_in_ff1;
reg key_in_ff2;
parameter wait_time=100000;
//for sim
//parameter wait_time=8000;
parameter IDLE=4'd0,START=4'd1,WAIT=4'd2,KEY_VALID=4'd3,FINISH=4'd4;
always@(posedge clk)key_in_ff1<=key_in;
always@(posedge clk)key_in_ff2<=key_in_ff1;
always@(posedge clk or negedge rst_n)beginif(!rst_n)curr_st<=IDLE;elsecase(curr_st)IDLE:beginif(key_in_ff2==0)curr_st<=START;else;endSTART:beginif(key_in_ff2==1)curr_st<=IDLE;else if(wait_cnt==wait_time)curr_st<=WAIT;else;endWAIT:beginif(key_in_ff2==1)curr_st<=IDLE;else if(key_in_ff2==0)curr_st<=KEY_VALID;elsecurr_st<=IDLE;endKEY_VALID:curr_st<=FINISH;FINISH:beginif(key_in_ff2==1)curr_st<=IDLE;else;enddefault:curr_st<=IDLE;endcaseendalways@(posedge clk or negedge rst_n)beginif(!rst_n)wait_cnt<=0;else if(curr_st==START)wait_cnt<=wait_cnt+1;elsewait_cnt<=0;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)key_out<=0;else if(curr_st==KEY_VALID)key_out<=1;elsekey_out<=0;endendmodule
4.2 无源蜂鸣器驱动模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:产生PWM波,驱动无源蜂鸣器发声
///
module alarm_ctrl(input clk,input rst_n,output reg beep);reg [15:0] cnt=0;always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt<=0;else if(cnt==10000)cnt<=0;else cnt<=cnt+1;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)beep<=0;else if(cnt==0)beep<=~beep;end
endmodule
4.3 异步串口接收模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:串口接收模块,将串行数据转换成并行数据
///
module async_uart_rev(input rst_n ,//复位信号,低电平有效input clk ,//时钟信号,50MHZinput rxd ,//串行接收数据output reg [7:0] rev_data,//并行数据output reg rev_dvld //并行数据有效标志);parameter baud_num=5207;//1/9600*1000000000/20parameter 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)beginif(!rst_n)curr_st<=IDLE;else case(curr_st)IDLE:beginif(rxd_ff2==0)curr_st<=START_ST;else;endSTART_ST:beginif(sample_num==8&&sample_en)curr_st<=STOP_ST;else;endSTOP_ST:beginif(rxd_ff2==1&&sample_en)curr_st<=IDLE;else;enddefault:;endcaseendalways@(posedge clk or negedge rst_n)beginif(!rst_n)baud_cnt<=0;else if(curr_st==START_ST||curr_st==STOP_ST)beginif(baud_cnt==baud_num)baud_cnt<=0;else baud_cnt<=baud_cnt+1;end elsebaud_cnt<=0;endalways@(posedge clk or negedge rst_n)beginif(!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;endalways@(posedge clk or negedge rst_n)beginif(!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:;endcaseendalways@(posedge clk or negedge rst_n)beginif(!rst_n) rev_dvld<=0;else if(sample_num==9&&sample_en)rev_dvld<=1;elserev_dvld<=0;end
endmodule
4.4 异步串口发送模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:串口发送模块,将并行数据转换成串行数据
///
module async_uart_tran(input rst_n ,//复位信号,低电平有效input clk ,//时钟,50MHZinput [7:0] tran_data ,//输入的并行数据input tran_dvld ,//输入的并行数据有效标志output reg txd ,//串行输出数据output byte_txfini );parameter baud_num=5207;//1/9600*1000000000/20parameter 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 byte_txfini=(curr_st==STOP_ST&&sample_en)?1'b1:1'b0;assign sample_en=(baud_cnt==baud_num)?1'b1:1'b0;always@(posedge clk or negedge rst_n)beginif(!rst_n)curr_st<=IDLE;else case(curr_st)IDLE:beginif(tran_dvld==1)curr_st<=START_ST;else;endSTART_ST:beginif(sample_en==1)curr_st<=DATA_ST;endDATA_ST:beginif(sample_en&&sample_num==8)curr_st<=STOP_ST;else;endSTOP_ST:beginif(sample_en==1)curr_st<=IDLE;else;enddefault:;endcaseendalways@(posedge clk or negedge rst_n)beginif(!rst_n)baud_cnt<=0;else if(curr_st==START_ST||curr_st==DATA_ST||curr_st==STOP_ST)beginif(baud_cnt==baud_num)baud_cnt<=0;else baud_cnt<=baud_cnt+1;end elsebaud_cnt<=0;endalways@(posedge clk or negedge rst_n)beginif(!rst_n) sample_num<=0;else if(curr_st==IDLE)sample_num<=0;else if(sample_en)sample_num<=sample_num+1;else;endalways@(posedge clk or negedge rst_n)beginif(!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;endcaseend
endmodule
4.5 指令解析模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:将收到的指令按协议解析出来,分别控制数码管,LED灯和蜂鸣器模块
///
module cmd_ctrl(input clk,input rst_n,input rev_dvld,input [7:0] rev_data,output reg [3:0] smg_00,output reg [3:0] smg_01,output reg [3:0] smg_02,output reg [3:0] smg_03,output reg [3:0] smg_04,output reg [3:0] smg_05,output reg [7:0] led,output reg beep);parameter IDLE =4'd0;parameter CMD0_ST =4'd1;parameter SMG00_ST =4'd2;parameter SMG01_ST =4'd3;parameter SMG02_ST =4'd4;parameter SMG03_ST =4'd5;parameter SMG04_ST =4'd6;parameter SMG05_ST =4'd7;parameter LED_ST =4'd8;parameter ALARM_ST =4'd9;reg [3:0] curr_st;always@(posedge clk or negedge rst_n)beginif(!rst_n)begincurr_st<=IDLE;end else case(curr_st)IDLE:beginif(rev_dvld&&rev_data==8'haa)curr_st<=CMD0_ST;else;endCMD0_ST:beginif(rev_dvld&&rev_data==8'h00)//数码管curr_st<=SMG00_ST;else if(rev_dvld&&rev_data==8'h01)//LED灯curr_st<=LED_ST;else if(rev_dvld&&rev_data==8'h02)//蜂鸣器curr_st<=ALARM_ST;else;endSMG00_ST:beginif(rev_dvld)curr_st<=SMG01_ST;else;endSMG01_ST:beginif(rev_dvld)curr_st<=SMG02_ST;else;endSMG02_ST:beginif(rev_dvld)curr_st<=SMG03_ST;else;endSMG03_ST:beginif(rev_dvld)curr_st<=SMG04_ST;else;endSMG04_ST:beginif(rev_dvld)curr_st<=SMG05_ST;else;endSMG05_ST:beginif(rev_dvld)curr_st<=IDLE;else;endLED_ST:beginif(rev_dvld)curr_st<=IDLE;else;endALARM_ST:beginif(rev_dvld)curr_st<=IDLE;else;enddefault:;endcaseendalways@(posedge clk or negedge rst_n)beginif(!rst_n)beginsmg_00 <=0;smg_01 <=0;smg_02 <=0;smg_03 <=0;smg_04 <=0;smg_05 <=0;end else if(curr_st==SMG00_ST)beginsmg_00<=rev_data;end else if(curr_st==SMG01_ST)beginsmg_01<=rev_data;end else if(curr_st==SMG02_ST)beginsmg_02<=rev_data;end else if(curr_st==SMG03_ST)beginsmg_03<=rev_data;end else if(curr_st==SMG04_ST)beginsmg_04<=rev_data;end else if(curr_st==SMG05_ST)beginsmg_05<=rev_data;end else;endalways@(posedge clk or negedge rst_n)beginif(!rst_n)beginled<=8'hff;end else if(curr_st==LED_ST)led<=rev_data;else;end always@(posedge clk or negedge rst_n)beginif(!rst_n)beep<=0;else if(curr_st==ALARM_ST)beep<=rev_data[0];else;end
endmodule
4.6 通信协议生成模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:生成按键检测的通信协议
///
`timescale 1ns/1ps
module uart_data_gen(input clk,input rst_n,input key0,input key1,input key2,input key3,input key4,input key5,input byte_end,output reg [ 7:0] data,output reg data_valid);
parameter idle = 8'd0;
parameter word1 = 8'd1;
parameter word1_gap = 8'd2;reg [ 7:0] curr_st ;
reg [ 7:0] word_cnt ;
reg [ 7:0] title ;
reg byte_end_ff1;
wire byte_end_rise;
wire[7:0] data_tmp ;
assign byte_end_rise=byte_end&(!byte_end_ff1);
always@(posedge clk)byte_end_ff1<=byte_end;
always@(posedge clk or negedge rst_n)beginif(!rst_n)title<=8'hff;else if(key0)title<=8'h00;else if(key1)title<=8'h01;else if(key2)title<=8'h02;else if(key3)title<=8'h03;else if(key4)title<=8'h04;else if(key5)title<=8'h05;else;end
always@(posedge clk or negedge rst_n)beginif(!rst_n)curr_st<=idle;else case(curr_st)idle:beginif(key0||key1||key2||key3||key4||key5)curr_st<=word1;else;endword1:begin if(word_cnt==2)curr_st<=word1_gap;else;endword1_gap:curr_st<=idle;default:;endcase end
always@(posedge clk or negedge rst_n)beginif(!rst_n)word_cnt<=0;else if(curr_st==idle||curr_st==word1_gap)word_cnt<=0;else if((curr_st==word1)&byte_end)word_cnt<=word_cnt+1;else;end
always@(posedge clk or negedge rst_n)beginif(!rst_n)begindata<=0;data_valid<=0;endelse case({curr_st,word_cnt}){word1,8'h0}:begin data<=8'haa ;data_valid<=1;end{word1,8'h1}:begin data<=title;data_valid<=1;enddefault: begin data<=8'h00;data_valid<=0;endendcaseend
endmodule
4.7 数码管显示模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:数码管动态扫描
///
module smg(input clk ,//时钟input rst_n ,//复位,低电平有效input [3:0] swan ,//十万位数input [3:0] wan ,//万位数input [3:0] qian ,//千位数input [3:0] bai ,//百位数input [3:0] shi ,//十位数input [3:0] ge ,//个位数output reg [5:0] sm_bit ,//数码管选择输出引脚output reg [7:0] sm_seg //数码管段输出引脚);reg [ 3:0] disp_dat;//定义显示数据寄存器reg [24:0] count ;//定义计数寄存器
//扫描信号产生部分
always @(posedge clk or negedge rst_n)//定义clock上升沿触发
beginif(!rst_n)count<=0;else if(count == 25'd250000)count <= 25'd0; //计数器清零elsecount <= count + 1'b1;
end
//数码管动态扫描显示部分
always @(posedge clk or negedge rst_n)
beginif(!rst_n)disp_dat<=0;else case(count[15:13])//选择扫描显示数据3'd0:disp_dat <= ge ;//个位3'd1:disp_dat <= shi ;//十位3'd2:disp_dat <= bai ;//百位3'd3:disp_dat <= qian ;//千位3'd4:disp_dat <= wan ;//万位3'd5:disp_dat <= swan ;//十万位endcase
end
always @(posedge clk)
begincase(count[15:13]) //选择扫描显示数据3'd0:sm_bit <= 6'b111110;//选择第一个数码管显示3'd1:sm_bit <= 6'b111101;//选择第二个数码管显示3'd2:sm_bit <= 6'b111011;//选择第三个数码管显示3'd3:sm_bit <= 6'b110111;//选择第四个数码管显示3'd4:sm_bit <= 6'b101111;//选择第五个数码管显示3'd5:sm_bit <= 6'b011111;//选择第六个数码管显示endcase
end
always @(posedge clk)
begincase(disp_dat)4'h0:sm_seg <= 8'hc0; //显示04'h1:sm_seg <= 8'hf9; //显示14'h2:sm_seg <= 8'ha4; //显示24'h3:sm_seg <= 8'hb0; //显示34'h4:sm_seg <= 8'h99; //显示44'h5:sm_seg <= 8'h92; //显示54'h6:sm_seg <= 8'h82; //显示64'h7:sm_seg <= 8'hf8; //显示74'h8:sm_seg <= 8'h80; //显示84'h9:sm_seg <= 8'h90; //显示94'ha:sm_seg <= 8'hbf; //显示-default:sm_seg <= 8'hff;//不显示endcase
end
endmodule
4.8 顶层模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:顶层模块,例化各个功能模块,使工程看起来整洁清晰
///
module ctrl_system_top(input clk ,input rst_n ,input [5:0] key ,input uart_rxd ,output uart_txd ,output [5:0] sm_bit ,output [7:0] sm_seg ,output [7:0] led ,output alarm);
wire [7:0] rev_data;
wire [3:0] smg_00 ;
wire [3:0] smg_01 ;
wire [3:0] smg_02 ;
wire [3:0] smg_03 ;
wire [3:0] smg_04 ;
wire [3:0] smg_05 ;
wire [7:0] tran_data;
wire beep;
wire beep_en;
wire key0_out,key1_out,key2_out,key3_out,key4_out,key5_out;
assign alarm=(beep_en)?beep:1'b1;
key_xd Ukey_xd0(.clk (clk ),.rst_n (rst_n ),.key_in (key[0] ),.key_out(key0_out ));
key_xd Ukey_xd1(.clk (clk ),.rst_n (rst_n ),.key_in (key[1] ),.key_out(key1_out ));
key_xd Ukey_xd2(.clk (clk ),.rst_n (rst_n ),.key_in (key[2] ),.key_out(key2_out ));
key_xd Ukey_xd3(.clk (clk ),.rst_n (rst_n ),.key_in (key[3] ),.key_out(key3_out ));
key_xd Ukey_xd4(.clk (clk ),.rst_n (rst_n ),.key_in (key[4] ),.key_out(key4_out ));
key_xd Ukey_xd5(.clk (clk ),.rst_n (rst_n ),.key_in (key[5] ),.key_out(key5_out ));
async_uart_rev Uasync_uart_rev(.rst_n (rst_n),.clk (clk),.rxd (uart_rxd),.rev_data (rev_data),.rev_dvld (rev_dvld));
cmd_ctrl Ucmd_ctrl(.clk (clk ),.rst_n (rst_n ),.rev_dvld (rev_dvld ),.rev_data (rev_data ),.smg_00 (smg_00 ),.smg_01 (smg_01 ),.smg_02 (smg_02 ),.smg_03 (smg_03 ),.smg_04 (smg_04 ),.smg_05 (smg_05 ),.led (led ),.beep (beep_en ));
smg Usmg(.clk (clk ),//时钟.rst_n (rst_n ),//复位,低电平有效.swan (smg_00 ),//十万位数.wan (smg_01 ),//万位数.qian (smg_02 ),//千位数.bai (smg_03 ),//百位数.shi (smg_04 ),//十位数.ge (smg_05 ),//个位数.sm_bit (sm_bit ),//数码管选择输出引脚.sm_seg (sm_seg )//数码管段输出引脚);
alarm_ctrl Ualarm_ctrl(.clk (clk),.rst_n (rst_n),.beep (beep));
uart_data_gen Uuart_data_gen(.clk (clk),.rst_n (rst_n),.key0 (key0_out),.key1 (key1_out),.key2 (key2_out),.key3 (key3_out),.key4 (key4_out),.key5 (key5_out),.byte_end (byte_txfini),.data (tran_data),.data_valid (tran_dvld));
async_uart_tran Uasync_uart_tran(.rst_n (rst_n),//复位信号,低电平有效.clk (clk),//时钟,50MHZ.tran_data (tran_data),//输入的并行数据.tran_dvld (tran_dvld),//输入的并行数据有效标志.txd (uart_txd), //串行输出数据.byte_txfini(byte_txfini));
endmodule
5. 编写仿真测试激励文件
我们知道,写仿真激励文件时,只关心输入信号,在该项目中,输入信号有clk,rst_n,key[5:0],uart_rxd,所以我们只需要在TB文件里对这几个信号操作即可。clk,rst_n,key[5:0],这几个信号的激励比较简单,在上几讲里都有涉及到,只是uart_rxd这个信号的激励相对来说要麻烦一些。因为,我们必须按异步串口通信的协议给这个uart_rxd赋值,相当于我们在TB文件里面要写一个串口发送模块,这样才能产生一个正确的uart_rxd激励信号。
5.1 顶层仿真文件
`timescale 1ns/1ns
module async_uart_top_tb;reg clk ;reg rst_n ;reg [5:0] key ;wire rxd ;reg [17:0] cnt=0;wire[7:0] tran_data;
initial
beginclk = 0;rst_n=0;key=6'h3f;#1000rst_n=1;
end
always #10 clk=~clk;
always@(posedge clk)cnt<=cnt+1;
always@(posedge clk)beginif(cnt>10000&&cnt<20000)//按下KEY1key<=6'b111110;elsekey<=6'b111111;
end
//生成通信协议数据
uart_data_gen_sim Uuart_data_gen_sim(.clk (clk ),.rst_n (rst_n ),.byte_end (byte_txfini),.data (tran_data ),.data_valid (tran_dvld ));
//将通信协议以串口的形式发送出去
async_uart_tran Uasync_uart_tran(.rst_n (rst_n ),.clk (clk ),.tran_data (tran_data ),.tran_dvld (tran_dvld ),.txd (rxd ),.byte_txfini(byte_txfini));
//被仿真的工程顶层文件
ctrl_system_top Uctrl_system_top(.clk (clk ),.rst_n (rst_n ),.key (key ),.uart_rxd (rxd ),.uart_txd (),.sm_bit (),.sm_seg (),.led (),.alarm ());
endmodule
5.2 通信协议生成模块
该模块主要用于生成数码管控制协议,LED灯控制协议和蜂鸣器控制协议,在我们仿真时,一次只能仿真一个控制协议,如果我们仿真数码管控制协议就需要在程序中注释掉LED灯控制协议和蜂鸣器控制协议。
`timescale 1ns/1ps
module uart_data_gen_sim(input clk,input rst_n,input byte_end,output reg [ 7:0] data,output reg data_valid);
parameter idle = 8'd0;
parameter word1 = 8'd1;
parameter word1_gap = 8'd2;
parameter word_num =8;//控制数码管的协议为8个字节
//parameter word_num =3;//控制LED灯和蜂鸣器的协议为3个字节
reg [ 7:0] curr_st ;
reg [ 7:0] word_cnt ;
reg [ 7:0] title ;
reg byte_end_ff1;
wire byte_end_rise;
wire[7:0] data_tmp ;
assign byte_end_rise=byte_end&(!byte_end_ff1);
always@(posedge clk)byte_end_ff1<=byte_end;
always@(posedge clk or negedge rst_n)beginif(!rst_n)curr_st<=idle;else case(curr_st)idle:curr_st<=word1;word1:begin if(word_cnt==word_num)curr_st<=word1_gap;else;endword1_gap:;//curr_st<=idle;default:;endcase end
always@(posedge clk or negedge rst_n)beginif(!rst_n)word_cnt<=0;else if(curr_st==idle||curr_st==word1_gap)word_cnt<=0;else if((curr_st==word1)&byte_end)word_cnt<=word_cnt+1;else;end
always@(posedge clk or negedge rst_n)beginif(!rst_n)begindata<=0;data_valid<=0;endelse case({curr_st,word_cnt})/控制数码管/{word1,8'h0}:begin data<=8'haa;data_valid<=1;end{word1,8'h1}:begin data<=8'h00;data_valid<=1;end{word1,8'h2}:begin data<=8'h01;data_valid<=1;end//第一位数码管显示的值{word1,8'h3}:begin data<=8'h02;data_valid<=1;end//第二位数码管显示的值{word1,8'h4}:begin data<=8'h03;data_valid<=1;end//第三位数码管显示的值{word1,8'h5}:begin data<=8'h04;data_valid<=1;end//第四位数码管显示的值{word1,8'h6}:begin data<=8'h05;data_valid<=1;end//第五位数码管显示的值{word1,8'h7}:begin data<=8'h06;data_valid<=1;end//第六位数码管显示的值/控制LED灯/// {word1,8'h0}:begin data<=8'haa;data_valid<=1;end// {word1,8'h1}:begin data<=8'h01;data_valid<=1;end// {word1,8'h2}:begin data<=8'hfe;data_valid<=1;end//点亮第一个LED灯/控制蜂鸣器/// {word1,8'h0}:begin data<=8'haa;data_valid<=1;end// {word1,8'h1}:begin data<=8'h02;data_valid<=1;end// {word1,8'h2}:begin data<=8'h01;data_valid<=1;end//打开蜂鸣器default: begin data<=8'h00;data_valid<=0;endendcaseend
endmodule
6. Modelsim仿真
Modelsim仿真一般有两种方法,第一种是图形化界面仿真,即所有的操作都是在Modelsim软件界面上来完成,该方式的优点是,简单易学,适用于简单的项目,缺点是操作步骤繁琐。第二种是批处理仿真,这种方式在仿真前需要编写相应的脚本文件,该方式的优点是,一键即可完成仿真,省时省力,缺点是前期需要编写脚本文件。为了更贴近工程实际,从第三课开始采用批处理的方式进行仿真。
6.1 数码管控制仿真波形如下:
通过上面的仿真波形我们可以看到,smg_00等于1,smg_01等于2…smg_05等于6,在通信协议生成模块(uart_data_gen_sim.v)中,我们给数码管赋值也是123456,仿真结果与设计相符,逻辑功能正确。
6.2 LED灯控制
通过上面的仿真波形我们可以看到,led等于8’b11111110,在通信协议生成模块(uart_data_gen_sim.v)中,我们给LED灯赋值也是8’b11111110,仿真结果与设计相符,逻辑功能正确。
6.3 蜂鸣器控制
通过上面的仿真波形我们可以看到,alarm输出了PWM方波信号,在通信协议生成模块(uart_data_gen_sim.v)中,我们给蜂鸣器赋值也是8’h01,即打开蜂鸣器,仿真结果也看到了有PWM信号输出,与设计相符,逻辑功能正确。
7.对比波形图
由于我们没有绘制理论波形,所以只需要在仿真时,确保仿真波形没问题就可以
8.绑定管脚(编写UCF文件)
############################################################################
## Clock constraints
############################################################################
NET "clk" TNM_NET = "clk";
TIMESPEC TS_sys_clk_i = PERIOD "clk" 20 ns HIGH 50 %;
NET "clk" LOC = B10 | IOSTANDARD = LVCMOS33 ;
############################################################################NET "key[0]" LOC = E4 | IOSTANDARD = LVCMOS33;
NET "key[1]" LOC = B3 | IOSTANDARD = LVCMOS33;
NET "key[2]" LOC = C3 | IOSTANDARD = LVCMOS33;
NET "key[3]" LOC = D3 | IOSTANDARD = LVCMOS33;
NET "key[4]" LOC = E3 | IOSTANDARD = LVCMOS33;
NET "key[5]" LOC = H4 | IOSTANDARD = LVCMOS33;NET "alarm" LOC = M13 | IOSTANDARD = LVCMOS33;NET "sm_bit[5]" LOC = E12 | IOSTANDARD = LVCMOS33;
NET "sm_bit[4]" LOC = B15 | IOSTANDARD = LVCMOS33;
NET "sm_bit[3]" LOC = E15 | IOSTANDARD = LVCMOS33;
NET "sm_bit[2]" LOC = H11 | IOSTANDARD = LVCMOS33;
NET "sm_bit[1]" LOC = K16 | IOSTANDARD = LVCMOS33;
NET "sm_bit[0]" LOC = K14 | IOSTANDARD = LVCMOS33;
NET "sm_seg[0]" LOC = F13 | IOSTANDARD = LVCMOS33;
NET "sm_seg[1]" LOC = B16 | IOSTANDARD = LVCMOS33;
NET "sm_seg[2]" LOC = J16 | IOSTANDARD = LVCMOS33;
NET "sm_seg[3]" LOC = J13 | IOSTANDARD = LVCMOS33;
NET "sm_seg[4]" LOC = G14 | IOSTANDARD = LVCMOS33;
NET "sm_seg[5]" LOC = E13 | IOSTANDARD = LVCMOS33;
NET "sm_seg[6]" LOC = G12 | IOSTANDARD = LVCMOS33;
NET "sm_seg[7]" LOC = J14 | IOSTANDARD = LVCMOS33;NET "led[0]" LOC = L12 | IOSTANDARD = LVCMOS33;
NET "led[1]" LOC = T13 | IOSTANDARD = LVCMOS33;
NET "led[2]" LOC = R12 | IOSTANDARD = LVCMOS33;
NET "led[3]" LOC = T12 | IOSTANDARD = LVCMOS33;
NET "led[4]" LOC = P11 | IOSTANDARD = LVCMOS33;
NET "led[5]" LOC = J11 | IOSTANDARD = LVCMOS33;
NET "led[6]" LOC = K11 | IOSTANDARD = LVCMOS33;
NET "led[7]" LOC = G11 | IOSTANDARD = LVCMOS33;NET "uart_rxd" LOC = H14 | IOSTANDARD = LVCMOS33;
NET "uart_txd" LOC = F14 | IOSTANDARD = LVCMOS33;
9.添加.v和.ucf文件
10.编译综合,同时将未使用管脚设置为悬空状态
1.设置未使用管脚为悬空状态
2.编译综合
11.下载BIT文件
编译综合成功后便可以将生成的BIT文件下载到开发板(记得插上下载器,同时开发板上电)
1.打开IMPACT
2.搜索器件
3.选择bit文件
下载成功后,便可以与我们的上位机测试软件进行通信,具体操作步骤如下
1)安装串口驱动
2)将电源线一端接入电脑USB接口 ,一端接入开发板供电口
3)下载FPGA程序
4)打开《开发板测试平台.exe》可执行文件
5)如果串口驱动安装成功,上位机会自动识别端口号,波特率选择9600,然后开启连接,如下图:
6)连接成功后,即可进行开发板控制,可更改数码管显示的数字,设置好以后,点击设置按钮,开发板上便显示上位机设置的数字
7)按下开发板上的按键,可显示当前按下的按键,如下图:
8)LED灯控制时,当打开开发板上的LED时,对应的按钮会变成红色,表示打开了对应的LED灯,如下图表示LED0,LED2,LED4被点亮。
9)按下蜂鸣器按钮,蜂鸣器报警,再按一次,报警关闭。
以上测试没问题就说明我们的程序是OK的,可以生成MCS文件,并完成程序固化。
12.生成MCS文件,同时固化到配置芯片中
FPGA有一个特性,就是掉电后配置信息会丢失,所以我们需要将配置信息存储在配置芯片(FLASH)中,待开发板上电后,FPGA便会读取配置芯片中的配置信息,这样开发板掉电再上电后同样可正常工作。要将程序固化到配置芯片,需要先生成MCS文件。
BIT文件转换成MCS文件步骤:
固化MCS文件
固化成功后,开发板断电再重新上电,可以观察到开发板仍然可以执行刚刚的功能。
【小月电子】XILINX FPGA开发板(XLOGIC_V1)系统学习教程-LESSON9简易测试系统相关推荐
- Xilinx FPGA开发板 Digilent Spartan-3E 学习资料整理
很多人抱怨Xilinx FPGA的资料很难找,Digilent的板卡资料网上怎么就没有呢!针对这些问题写了如下的BLog,希望对大家有帮助. 最近几日在整理关于Xilinx FPGA和Digilent ...
- Xilinx FPGA开发板 Digilent Spartan-3E 学习资料
一.FPGA相关资料贴 EDK实验 base in spartan-3e 适合mircoblaze初学者 MicroBlaze嵌入式软核是一个被Xilinx公司优化过的可以嵌入在FPGA中的RISC ...
- VHDL编写多功能数字钟,spartan3 FPGA开发板硬件实现-学习笔记
VHDL编写多功能数字钟,spartan3 FPGA开发板硬件实现-学习笔记 多功能数字钟硬件测试视频: https://www.bilibili.com/video/av62501230 1.数字钟 ...
- 【小月电子】XILINX FPGA开发板(XLOGIC_V1)系统学习教程-LESSON7串口通信
串口通信例程讲解 若要观看该博客配套的视频教程,可点击此链接 根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤.比如非常简单的项目,我们可以 ...
- 【小月电子】XILINX FPGA开发板(XLOGIC_V1)系统学习教程-LESSON1点亮LED灯
点亮LED灯例程讲解 若要观看该博客配套的视频教程,可点击此链接 开发板实物图 图1.FPGA设计流程 根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去 ...
- Xilinx FPGA开发板
目录 一.Nexus 4 DDR开发板 二.主要外围接口电路 (1)Nexus4 DDR Artix-7FPGA引脚分配 (2)LED灯电路 (3)拨码开关电路 (4)按键电路 (5)数码管电路 一. ...
- Vivado将程序固化到Xilinx的FPGA开发板的flash芯片中
Vivado将程序固化到Xilinx的FPGA开发板 准备工作 开始 准备.mcs文件 在HardWare Manager界面操作 固化程序到flash中 对开发板进行操作 另外 准备工作 Vivad ...
- html抽奖源码_开源FPGA开发板OpenICE 介绍及抽奖
首先呢,先强调一遍,我做板子不是为了挣钱,因为国内目前的形式比较严峻,只是为了体验一下开源的工具和环境,也为了后人能对FPGA有个新的认识,所以不会触碰到任何人的蛋糕. 本来今天不准备发文了,还是熬夜 ...
- 开源FPGA开发板-OpenICE 介绍及抽奖
首先呢,先强调一遍,我做板子不是为了挣钱,因为国内目前的形式比较严峻,只是为了体验一下开源的工具和环境,也为了后人能对FPGA有个新的认识,所以不会触碰到任何人的蛋糕. 本来今天不准备发文了,还是熬夜 ...
- Digilent FPGA开发板的Boards file的添加——以Eclypse-Z7为例
Digilent FPGA开发板的Boards file的添加--以Eclypse-Z7为例 背景 软硬件平台 添加Eclypse-Z7的board file 参考相关 背景 最近刚开始使用 Digi ...
最新文章
- linux系统命令学习系列-文件和目录管理
- Flask-RESTful之响应处理
- 淘宝、美团、滴滴分别如何搭建大数据平台?
- k8s系列~mgr的应用
- [Enterprise Library]Configuration类设计分析
- linux下安装davinci
- 使用ubuntu钉钉
- r语言 新增一列数字类型_R语言实战之R语言基础语法精讲(一)
- 【解决】速达服务启动失败,文件无效
- Vue使用axios图片上传报错500
- declares(declares是什么意思)
- vmware15安装黑苹果懒人版教程
- 基因组数据质控中:先进行SNP缺失质控还是样本缺失质控?
- 设计模式——代理模式详解(Java版)
- 使用python画等边三角形的程序-python叠加等边三角形的代码编写方法
- PyScripter中文乱码原因
- Arduino与人体感应模块
- 写需求规格说明书/产品定义的个人总结
- 搭建智能DNS---就近原则
- python情人节之玫瑰花与表白方式_情人节 我用Python表白