完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本实验,为ZX-2开发板的综合实验,该实验利用ZX-2开发板上的ADC、独立按键、UART等外设,搭建了一个具备丰富功能的数据采集卡,ZX-2开发板负责进行数据的采集并将数据通过串口发送到PC机上,PC端,利用强大的串口调试工具——串口猎人,来实现数据的接收分析,并将数据分别以波形、码表、柱状图的形式动态显示出来,以让使用者能够直观的看到ADC采集到的信号细节。同时,用户也可以使用串口猎人通过串口给下位机(FPGA)发送指令,下位机将对接收到的指令进行解码,然后依据解码结果来配置FPGA中各个子模块的控制寄存器,以实现通过串口控制FPGA中子模块工作状态的功能。 本实验中,涉及到的应用模块和知识点如下所示: 串口收发模块的设计和使用; 串口收发模块仿真模型的设计; 串口简单数据帧的解码; 串口帧转Memory Mapped总线的设计; Memory MappedSlave模块的设计; 线性序列机设计思想的应用(ADC驱动); 独立按键消抖的分析与实现; 直接数字频率合成(DDS)的设计与实现; 使能时钟对系统间模块协调工作的重要性; 串口猎人的详细使用; 完整系统的仿真验证设计; 头文件在设计中的运用; Quartus II软件中可定制化存储器ROM的使用; 本实验不仅注重可综合的代码编写,同时更注重代码的仿真验证。通过仿真,我们能够寻找设计中可能存在的问题并修正。最终,在整个系统仿真无误的基础上,下载到开发板上一次性成功。 下图为本设计的框架结构图: 系统采用模块化设计,在模块划分的过程中,重点考虑了系统的可扩展性,下表为对系统中各模块功能的简单介绍。 基于串口的虚拟示波器模块功能介绍 模块名 模块功能 Uart_Byte_Rx 串口字节接收模块,负责进行PC机发送指令数据的接收工作; CMD 串口指令解析与控制模块,该模块通过解码PC机发送的指令数据序列,获取控制命令,转换为Memory Mapped Master总线,以实现上位机通过串口控制下位机(FPGA)中各模块寄存器的功能; Sample_Ctrl ADC采样速率控制模块,该模块通过控制使能ADC采数的速率来调整ADC的采样率; UART_Byte_Tx 串口字节发送模块,该模块负责将需要发送的字节数据通过UART协议发送出去(发送到上位机) UART_Tx_Ctrl 串口发送控制模块,根据数据有效标志信号来控制串口发送模块将数据出去 tlc549_Driver TLC549模数转换(ADC)芯片驱动模块,负责驱动TLC549进行模数转换,并将转换结果以字节格式输出。 DDS DDS信号发生器模块,该模块生成一个固定频率的正弦波,在没有外部信号发生器的情况下,可通过该模块生成正弦波,并将输出数据接入到采样部分,以模拟采集正弦波信号并在上位机显示 Mux1 AD采集结果和DDS数据结果多路选择模块,在测试时,用户可以选择需要采样的数据为内部信号发生器生成的正弦波数据(供演示用)或者ADC采集到的电压结果(实际测试) Mux2 AD采集结果和DDS数据结果有效标志信号选择多路器,在ADC和DDS都处于工作的状态下,通过该多路器来实现数据有效标志信号的选择 normal_keys_detect 独立按键消抖模块,通过该模块来实现AD数据和DDS数据的切换,即在演示和实际测试中进行切换。 系统中各端口和信号的功能介绍如下: 基于串口的虚拟示波器端口和信号介绍 端口 位宽 端口功能描述 Clk 1 系统时钟,50M Rst_n 1 全局复位,低电平复位 Rs232_Rx 1 串口接收引脚 Rs232_Tx 1 串口发送引脚 Key_in 3 按键输入 ADC_Din 1 ADC芯片数据引脚 ADC_Clk 1 ADC接口时钟 ADC_Cs_n 1 ADC芯片片选信号 内部信号 位宽 信号功能描述 Baud_Set 3 波特率选择信号,对应波特率如下: 000: 9600bps 001: 19200bps 010: 38400bps 011: 57600bps 100: 115200bps 101: 230400bps 110: 460800bps 111: 921600bps Rx_Byte 8 串口接收到的字节数据 Rx_Int 1 串口接收字节成功标志信号,每次接收成功,此信号产生一个时钟周期的高脉冲 Byte_En 1 串口字节数据发送使能信号,每一次一个时钟周期的高脉冲使能一次串口字节发送。 Tx_Done 1 串口发送字节数据完成标志,每个字节的数据发送完成,此信号产生一个时钟周期的高脉冲 ADC_En 1 ADC单次转换使能信号,每一次一个时钟周期的高脉冲使能一次AD转换 ADC_Data 8 ADC采样结果 ADC_Flag 1 ADC转换结果有效标志,每次转换结果有效后,此信号产生一个时钟周期的高脉冲 ADC_Busy 1 ADC工作忙标志,高电平表明ADC正处于转换状态,新的转换命令将被忽略,只有当该信号为低电平时,外部控制逻辑才可触发新的转换 m_wr 1 主机写数据的请求,为1表明有写请求 m_addr 8 主机写数据的地址 m_wrdata 16 主机写数据 DDS_Data 8 DDS生成的波形数据 DDS_Flag 1 DDS采样使能标志,该信号由DDS采样速率控制进程产生 Data_Flag 1 数据有效标志(根据用户按键进行选择DDS_Flag 或ADC_Flag) Data_Byte 8 串口发送字节数据 Data_Sel 1 数据选择信号(选择串口发送DDS_Data 或 ADC_Data),为1选择DDS_Data,为0选择ADC_Data Flag_Sel 1 数据有效标志信号选择信号(选择DDS_Flag 或ADC_Flag)为1选择DDS_ Flag,为0选择ADC_ Flag Key_Flag 1 按键检测成功标志信号,每次按键检测成功该信号产生一个时钟周期的高脉冲信号 Key_Value 3 按键检测结果 本实验为综合性实验,代码量较大,因此这里只针对部分代码进行讲解。如果文档中没有讲到的内容,大家可以参看代码注释。 1.1Tx_Bps_Gen Tx_Bps_Gen为发送波特率生成模块,每当有Byte_En信号到来时,即开始产生发送一个完整字节的数据需要的完整波特率时钟信号。 本设计,波特率支持9600bps到921600bps。例如,需要产生的波特率时钟为9600bps,即波特率时钟频率为9600Hz,周期为104.17us。生成9600Hz波特率时钟的核心思想就是对系统时钟进行计数,这里设定系统时钟为50MHz,则一个时钟的周期为20ns,我们只需要对系统时钟计数5208次,每计数5208次产生一个时钟周期的高电平脉冲,即可实现生成9600Hz波特率时钟的功能。相应代码如下所示: 018 parameter system_clk = 50_000_000; /*输入时钟频率设定,默认50M*/ 019 020 /*根据输入时钟频率计算生成各波特率时分频计数器的计数最大值*/ 021 localparam bps9600 = system_clk/9600 - 1; 022 localparam bps19200 = system_clk/19200 - 1; 023 localparam bps38400 = system_clk/38400 - 1; 024 localparam bps57600 = system_clk/57600 - 1; 025 localparam bps115200 = system_clk/115200 - 1; 026 localparam bps230400 = system_clk/230400 - 1; 027 localparam bps460800 = system_clk/460800 - 1; 028 localparam bps921600 = system_clk/921600 - 1; 029 030 reg [31:0]BPS_PARA;/*波特率分频计数器的计数最大值*/ 031 032 always@(posedge Clk or negedge Rst_n) 033 if(!Rst_n)begin 034 BPS_PARA <= bps9600;/*复位时波特率默认为9600bps*/ 035 end 036 else begin 037 case(Baud_Set)/*根据波特率控制信号选择不同的波特率计数器计数最大值*/ 038 3'd0: BPS_PARA <= bps9600; 039 3'd1: BPS_PARA <= bps19200; 040 3'd2: BPS_PARA <= bps38400; 041 3'd3: BPS_PARA <= bps57600; 042 3'd4: BPS_PARA <= bps115200; 043 3'd5: BPS_PARA <= bps230400; 044 3'd6: BPS_PARA <= bps460800; 045 3'd7: BPS_PARA <= bps921600; 046 default: BPS_PARA <= bps9600; 047 endcase 048 end 049 050 //========================================================= 051 reg[12:0]Count; 052 053 reg n_state; 054 localparam IDEL_1 = 1'b0, 055 SEND = 1'b1; 056 057 reg BPS_EN; 058 059 /*-------波特率时钟生成控制逻辑--------------*/ 060 always@(posedge Clk or negedge Rst_n) 061 if(!Rst_n)begin 062 BPS_EN <= 1'b0; 063 n_state <= IDEL_1; 064 end 065 else begin 066 case(n_state) 067 IDEL_1: 068 if(Byte_En)begin/*检测到字节发送使能信号,则启动波特率生成进程,同时进入发送状态*/ 069 BPS_EN <= 1'b1; 070 n_state <= SEND; 071 end 072 else begin 073 n_state <= IDEL_1; 074 BPS_EN <= 1'b0; 075 end 076 SEND: 077 if(Tx_Done == 1)begin/*发送完成,关闭波特率生成进程,回到空闲状态*/ 078 BPS_EN <= 1'b0; 079 n_state <= IDEL_1; 080 end 081 else begin 082 n_state <= SEND; 083 BPS_EN <= 1'b1; 084 end 085 default:n_state <= IDEL_1; 086 endcase 087 end 088 089 /*-------波特率时钟生成定时器--------------*/ 090 always@(posedge Clk or negedge Rst_n) 091 if(!Rst_n) 092 Count <= 13'd0; 093 else if(BPS_EN == 1'b0) 094 Count <= 13'd0; 095 else begin 096 if(Count == BPS_PARA) 097 Count <= 13'd0; 098 else 099 Count <= Count + 1'b1; 100 end 101 102 /*输出数据接收采样时钟*/ 103 //----------------------------------------------- 104 always @(posedge Clk or negedge Rst_n) 105 if(!Rst_n) 106 Bps_Clk <= 1'b0; 107 else if(Count== 1) 108 Bps_Clk <= 1'b1; 109 else 110 Bps_Clk <= 1'b0; 第18行“parameter system_clk = 50_000_000;”,这里用一个全局参数定义了系统时钟,暂时设定为50M,可根据实际使用的板卡上的工作时钟进行修改。 所谓波特率生成,就是用一个定时器来定时,产生频率与对应波特率时钟频率相同的时钟信号。例如,我们使用波特率为115200bps,则我们需要产生一个频率为115200Hz的时钟信号。那么如何产生这样一个115200Hz的时钟信号呢?这里,我们首先将115200Hz时钟信号的周期计算出来,1秒钟为1000_000_000ns,因此波特率时钟的周期Tb=1000000000/115200 =8680.6ns,即115200信号的一个周期为8680.6ns,那么,我们只需要设定我们的定时器定时时间为8680.6ns,每当定时时间到,产生一个系统时钟周期长度的高脉冲信号即可。系统时钟频率为50MHz,即周期为20ns,那么,我们只需要计数8680/20个系统时钟,就可获得8680ns的定时,即bps115200=Tb/Tclk- 1=Tb*fclk - 1=fclk/115200-1。相应的,其它波特率定时值的计算与此类似,这里小梅哥就不再一一分析。20行至28行为波特率定时器定时值的计算部分。 为了能够通过外部控制波特率,设计中使用了一个3位的波特率选择端口:Baud_Set。通过给此端口不同的值,就能选择不同的波特率,此端口控制不同波特率的原理很简单,就是一个多路选择器,第32行至第48行即为此多路选择器的控制代码, Baud_Set的值与各波特率的对应关系如下: 000 : 9600bps; 001 : 19200bps; 010 :38400bps; 011 :57600bps; 100 :115200bps; 101 :230400bps; 110 :460800bps; 111 :921600bps; 1.2Uart_Byte_Tx Uart_Byte_Tx为字节发送模块,该模块在波特率时钟的节拍下,依照UART通信协议发送一个完整的字节的数据。当一个字节发送完毕后,Tx_Done产生一个高脉冲信号,以告知其它模块或逻辑一个字节的数据已经传输完成,可以开始下一个字节的发送了。其发送一个字节数据的实现代码如下: 33 /*计数波特率时钟,11个波特率时钟为一次完整的数据发送过程*/ 34 always@(posedge Clk or negedge Rst_n) 35 if(!Rst_n) 36 Bps_Clk_Cnt <= 4'b0; 37 else if(Bps_Clk_Cnt == 4'd11) 38 Bps_Clk_Cnt <= 4'b0; 39 else if(Bps_Clk) 40 Bps_Clk_Cnt <= Bps_Clk_Cnt + 1'b1; 41 else 42 Bps_Clk_Cnt <= Bps_Clk_Cnt; 43 44 /*生成数据发送完成标志信号*/ 45 always@(posedge Clk or negedge Rst_n) 46 if(!Rst_n) 47 Tx_Done <= 1'b0; 48 else if(Bps_Clk_Cnt == 4'd11) 49 Tx_Done <= 1'b1; 50 else 51 Tx_Done <= 1'b0; 52 53 /*在开始发送起始位的时候就读取并寄存Data_Byte,以免Data_Byte变化导致数据的丢失*/ 54 always@(posedge Clk or negedge Rst_n) 55 if(!Rst_n) 56 Data = 8'd0; 57 else if(Bps_Clk & Bps_Clk_Cnt == 4'd1) 58 Data <= Data_Byte; 59 else 60 Data <= Data; 61 62 /*发送数据序列机*/ 63 always@(posedge Clk or negedge Rst_n) 64 if(!Rst_n) 65 Rs232_Tx <= 1'b1; 66 else begin 67 case(Bps_Clk_Cnt) 68 4'd1: Rs232_Tx <= 1'b0; 69 4'd2: Rs232_Tx <= Data[0]; 70 4'd3: Rs232_Tx <= Data[1]; 71 4'd4: Rs232_Tx <= Data[2]; 72 4'd5: Rs232_Tx <= Data[3]; 73 4'd6: Rs232_Tx <= Data[4]; 74 4'd7: Rs232_Tx <= Data[5]; 75 4'd8: Rs232_Tx <= Data[6]; 76 4'd9: Rs232_Tx <= Data[7]; 77 4'd10: Rs232_Tx <= 1'b1; 78 default:Rs232_Tx <= 1'b1; 79 endcase 80 end 在UART协议中,一个完整的字节包括一位起始位、8位数据位、一位停止位即总共十位数据,那么,要想完整的实现这十位数据的发送,就需要11个波特率时钟脉冲,如下所示: BPS_CLK信号的第一个上升沿到来时,字节发送模块开始发送起始位,接下来的2到9个上升沿,发送8个数据位,第10个上升沿到第11个上升沿为停止位的发送。 小梅哥和你一起深入学习FPGA |
|
相关推荐
24个回答
|
|
好,顶小美哥。
|
|
|
|
既然大家评价还可以,那我就干脆自作主张,将整个开发板的全部资料放出来吧,大家要的赶紧下载。另外,整个内容比较大,网速快的筒子可以下载了解压好了,将其中大家需要部分再次上传上来,我一直用流量卡工作,所以流量神马的实在伤不起,就不给大家传文件了,这里只留下地址。
链接:http://pan.baidu.com/s/1qW0YXhq 密码:1wvz |
|
|
|
小梅哥真赞!刚接触fpga,难得好资料,小梅哥要是做视频的话,肯定会让很多入门者少走些弯路。
|
|
|
|
很不错,学习了
|
|
|
|
学习一下
|
|
|
|
只有小组成员才能发言,加入小组>>
2900 浏览 3 评论
27703 浏览 2 评论
3479 浏览 2 评论
3987 浏览 4 评论
基于采用FPGA控制MV-D1024E系列相机的图像采集系统设计
2335 浏览 3 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 01:46 , Processed in 0.536669 second(s), Total 55, Slave 46 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号