ZYNQ学习笔记(二):按键控制DDS信号频率实验+仅有PL端(FPGA)逻辑资源的程序固化到SD卡


前言

本文是在:ZYNQ学习笔记(一):基于ZYNQ7020、AN108的DDS实验(VIO可控频率字)的基础上进一步修改。

一、设计需求、

在上一个实验的基础上:
把生成DDS信号的主频由50Mhz改为100Mhz
去掉VIO模块,替换为按键控制频率字
通过按键能够切换不同的输出频率,分别为:1,3,5,10,15,20,25,30Mhz
仅有PL端(FPGA)逻辑资源的程序固化

工程下载地址:KEY_DDS

二、程序设计与IP核配置、

顶层代码:

`timescale 1ns / 1ps
module key_dds(
    input       sys_clk,            
    input       rst_n,
    input       key1, //按键 key1
     //DA芯片接口
    output                da_clk,  
    output    [7:0]       da_data,  //输出给DA的数据
    //AD芯片接口
    input     [7:0]       ad_data,  //AD输入数据
    output                ad_clk            
    );

wire key1_value; //key1 消抖后的按键值
wire key1_flag;  //key1 消抖后的按键值的有效标志

key_debounce u_key1_debounce(
.rst_n (rst_n),
.clk (sys_clk),
.key (key1),
.key_value (key1_value),
.key_flag (key1_flag) 
);

wire     clk_100M;  
clk_wiz_0 pll_inst
   (
    // Clock out ports
    .clk_out1(ad_clk),     // 给ad_clk 25MHz的频率
    .clk_out2(clk_100M),
    // Status and control signals
    .reset(~rst_n), // input reset
   // Clock in ports
    .clk_in1(sys_clk)     // input clk_in1
 );
 
wire [23 : 0]   Fword;         
key_control  key_control_lg(
    .clk(sys_clk), 
    .rst_n(rst_n), 
    .key1_value(key1_value),
    .key1_flag(key1_flag),
    .Fword(Fword)
);

wire [0:0]      fre_ctrl_word_en;    
//output
wire [0 : 0]    m_axis_data_tvalid;
wire [7 : 0]    m_axis_data_tdata;
wire [0 : 0]    m_axis_phase_tvalid;
wire [23 : 0]   m_axis_phase_tdata;
 
assign          fre_ctrl_word_en = 1'b1;
dds_compiler_0 u_dds_compiler_0 (
  .aclk(clk_100M),                                                // input wire aclk

  .s_axis_config_tvalid(fre_ctrl_word_en),           // input wire s_axis_config_tvalid
  .s_axis_config_tdata(Fword),                            // input wire [23 : 0] s_axis_config_tdata
  
  .m_axis_data_tvalid(m_axis_data_tvalid),        // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_data_tdata),          // output wire [7 : 0] m_axis_data_tdata
  .m_axis_phase_tvalid(m_axis_phase_tvalid),      // output wire m_axis_phase_tvalid
  .m_axis_phase_tdata(m_axis_phase_tdata)         // output wire [23 : 0] m_axis_phase_tdata
);

//DA数据发送
da_wave_send u_da_wave_send(
    .clk         (clk_100M), 
    .rst_n       (rst_n),
    .rd_data     (m_axis_data_tdata),
    .da_clk      (da_clk),  
    .da_data     (da_data)
    );
// ILA
ila_0 u_ila (
	.clk(ad_clk), // input wire clk
	.probe0(Fword), // input wire [23:0]  probe1 
	.probe1(da_data), // input wire [7:0]  probe2
	.probe2(ad_data) // input wire [7:0]  probe3
);
endmodule

相较于实验一,程序做出的改进有:增加了按键消抖模块,它处理来自key1输入的按键信号,并输出消抖后的按键值和有效标志。clk_wiz_0 模块增加了一路100Mhz频率输出,用来给DDS还有DA数据发送模块提供时钟信号,删除了VIO模块,改用按键模块控制频率字。

PLL IP核配置如下:

这里除了保留提供给ADC采样的频率外,还要增加一个提供给DDS IP核的100Mhz时钟频率输出。

ILA IP核配置如下:

分别采集频率字、DA输出以及AD输入信号。

DDS IP核配置:

这里的Parameters选项设置,当用Hardware Parameters选项时,工程并不会报错,但是下载到板子上输出的频率并不是所预设的那些,但是当我更换为System Parameters选项,所设频率输出正常

后面我查找资料发现,System Parameters选项用于配置DDS IP核在整个系统中的全局性质和性能;而Hardware Parameters选项用于配置DDS IP核内部的硬件细节和运算方式,要用于优化DDS核的行为,这些参数通常更低级,与IP核的内部运算和控制有关。

当我使用PLL锁相环将系统时钟进行倍频,然后将倍频后的时钟信号输入到DDS IP核,在这种情况下是希望配置DDS核以适应整个系统的时钟设置和性能要求,关注点是DDS核如何与系统中的时钟倍频器协同工作,以生成正确的输出信号,所以应该是通过System Parameters来确定DDS核的基本工作方式。

此外Spurious Free Dynamic Range(sfdr)是意为无杂散动态范围,SFDR是指基波强度与最大杂波或谐波的强度之比,所以SFDR值越大则说明系统的噪声水平越低,灵敏度越高。SFDR 还决定了输出的数据位宽。

因为ADDA模块的位宽限制,Ootput Width最大只能等于8,所以我们把SDRF设置为48。

Frequency Resolution是意为频率分辨率,它决定了我们可以在频率域中分辨的最小频率间隔,更高的频率分辨率意味着我们可以更精确地分辨不同的频率成分。

频率分辨率与几个参数之间的关系:

这里我们还是同实验一一样,把Phase Width设为20,那么通过公式可以计算出Frequency Resolution的值~

这些也算对实验一DDS IP核知识的补充了。

按键消抖模块的代码:

`timescale 1ns / 1ps
module key_debounce(
    input clk , 
    input rst_n , 
    
    input key ,             //外部输入的按键值 
    output reg key_value ,  //消抖后的按键值 
    output reg key_flag     //消抖后的按键值的效标志
    );

//reg define
reg [19:0] cnt ;
reg key_reg ;

always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt <= 20'd0;
        key_reg <= 1'b1;
    end
    else begin
        key_reg <= key;             //将按键值延迟一拍  
        if(key_reg != key) begin    //检测到按键状态发生变化
            cnt <= 20'd100_0000;    //则将计数器置为20'd100_0000
                                    //即延时100_0000 * 20ns(1s/50MHz) = 20ms
        end
        else begin                  //如果当前按键值和前一个按键值一样,即按键没有发生变化
            if(cnt > 20'd0)         //则计数器递减到0
                cnt <= cnt - 1'b1;
            else
                cnt <= 20'd0;
        end
    end
end

//将消抖后的最终的按键值送出去 
always @ (posedge clk or negedge rst_n) begin 
    if(!rst_n) begin 
        key_value <= 1'b1; 
        key_flag <= 1'b0; 
    end 
    //在计数器递减到1时送出按键值 
    else if(cnt == 20'd1) begin 
        key_value <= key; 
        key_flag <= 1'b1; 
    end 
    else begin 
        key_value <= key_value; 
        key_flag <= 1'b0; 
    end 
end

endmodule

代码中的 if(key_reg != key) begin ,即每检测到按键被按下或松开,就让计数器从 100_0000 开始递减,时长 20ms。在这 20ms 期间,每当有抖动产生,计数器就被重置回 100_0000,即重新开始计时 20ms。

代码中的 else if(cnt == 20’d1) begin,只有在计数器递减到 1 时,即此时计数器计时完了 20ms,才会寄存按键的值。这样,每当按键被按下或松开,20ms 内的抖动就被消除了。

频率调节的代码:

`timescale 1ns / 1ps

module key_control(
    input           clk,
    input           rst_n,
    input           key1_value,    // 消抖后的按键值
    input           key1_flag,     // 消抖后的按键值的有效标志
    output reg [23:0] Fword
);

reg [2:0] key_count;      // 用于跟踪按键按下次数

always @(posedge clk or negedge rst_n) begin
    if (rst_n == 1'b0) begin
        Fword <= 'h28f5;   // 初始频率
        key_count <= 3'b000;  // 初始按键计数
    end else begin
        // 根据按键状态选择不同的频率
        if (key1_flag && key1_value) begin
            key_count <= key_count + 1'b1;
            if (key_count == 3'b111) begin
                key_count <= 3'b000; // 重置计数器
            end
        end
        case (key_count)
            3'b000: Fword <= 'h28f5; // 1MHz
            3'b001: Fword <= 'h7ae1; // 3MHz
            3'b010: Fword <= 'hcccc; // 5MHz
            3'b011: Fword <= 'h19999; //10MHz
            3'b100: Fword <= 'h26666; //15MHz
            3'b101: Fword <= 'h33333;//20MHz
            3'b110: Fword <= 'h40000;//25MHz
            3'b111: Fword <= 'h4cccc;//30MHz
        endcase
    end
end

endmodule

因为 key1_value为消抖后的按键值, key1_flag为消抖后的按键值的有效标志 ,只有当key1_flag的值为1时, key1_value的值才为真,所以当两者的值同为1时,代表按键真的被按下,此时记录按键状态加一 ,然后再根据按键状态选择不同的频率字~从而控制输出不同的频率信号。

DA数据发送模块代码同实验一,这里不再细说~

约束代码在原来的基础添加上按键的引脚分配:

########key N15 N16##################
set_property PACKAGE_PIN N15 [get_ports key1]
set_property IOSTANDARD LVCMOS33 [get_ports key1]

最后所生成的RTL原理图:

三、连接测试、

将示波器与开发板相连,在比特文件下载到开发板后,观察示波器输出波形:

按下PL_KEY1,

继续按下观察波形频率~

不再进行一一展示
当我按下第八次,频率回到1Mhz,生成信号的频率虽有误差但基本吻合,至此就完成了设计需求的前三个任务~

四、程序固化、

上面的任务结束后,为避免每次测试都要与电脑连接就挺麻烦的~故尝试着把工程固化到板子上,

先了解一下固化的逻辑:

1.ZYNQ固化必须用到PS块。所以纯PL工程中需要新建一个block块,加入并配置PS。在 ZYNQ 中,PS 作为主器件,PL 可以看作是 PS 的一个外设,因此需要由 PS 来配置 PL。

2.配置完成后系统会新生成一个.v文件,再将自己原本要固化的代码例化进去编译生成新的bit文件。

3.在SDK里生成固化代码及BOOT.bin文件,并把BOOT.bin文件下载到SD卡里。

4.选用的固化启动方式为SD卡启动。现在开始固化的流程~

4.1、配置PS端

创建block块。

可自定义名称,我这里直接默认点“OK”

添加IP核

双击该模块

如图进行设置~,这里QSPI与SD卡启动我都使能了

对DDR3进行配置选择,黑金zynq 7020开发板的DDR3芯片(共计8Gbit),型号为H5TQ4G63AFR-PBC(兼容MT41J256M16RE-125)。

配置完成后,按图所示依次点击。

这里手动把两个clk连在一起,鼠标左键直接拖就行,不连会报错,编译不过去。

Ctrl+S保存,回到左边sources窗口,右击前面新建的design_1.bd块文件,选Generate Output Products

依次点击Generate~OK,完成后还是右击design_1.bd块文件,选第二个

完成后点击OK就可以~

4.2、例化代码

sources窗口点击刚生成的.v文件

把上面工程的顶层代码例化部分加入这个.v文件(源代码不能够动,直接加入就可),我分为了4部分加入

`timescale 1 ps / 1 ps

module design_1_wrapper
   (DDR_addr,
    DDR_ba,
    DDR_cas_n,
    DDR_ck_n,
    DDR_ck_p,
    DDR_cke,
    DDR_cs_n,
    DDR_dm,
    DDR_dq,
    DDR_dqs_n,
    DDR_dqs_p,
    DDR_odt,
    DDR_ras_n,
    DDR_reset_n,
    DDR_we_n,
    FIXED_IO_ddr_vrn,
    FIXED_IO_ddr_vrp,
    FIXED_IO_mio,
    FIXED_IO_ps_clk,
    FIXED_IO_ps_porb,
    FIXED_IO_ps_srstb,

///1///
    sys_clk,
    rst_n,
    key1,
    da_clk,
    da_data,
    ad_data,
    ad_clk
///
    );
    
///2///
    input       sys_clk;            
    input       rst_n;
    input       key1;
    output                da_clk;  
    output    [7:0]       da_data; 
    input     [7:0]       ad_data; 
    output                ad_clk;   
///

///3///
  key_debounce u_key1_debounce(
  .rst_n (rst_n),
  .clk (sys_clk),
  .key (key1),
  .key_value (key1_value),
  .key_flag (key1_flag) 
  );
 
   clk_wiz_0 pll_inst
   (
    .clk_out1(ad_clk),    
    .clk_out2(clk_100M),
    .reset(~rst_n), 

    .clk_in1(sys_clk) 
  );      

   key_control  key_control_lg(
    .clk(sys_clk), 
    .rst_n(rst_n), 
    .key1_value(key1_value),
    .key1_flag(key1_flag),
    .Fword(Fword)
   );
 
   dds_compiler_0 u_dds_compiler_0 (
  .aclk(clk_100M),                                 
  .s_axis_config_tvalid(fre_ctrl_word_en),       
  .s_axis_config_tdata(Fword),                    
  .m_axis_data_tvalid(m_axis_data_tvalid),       
  .m_axis_data_tdata(m_axis_data_tdata),          
  .m_axis_phase_tvalid(m_axis_phase_tvalid),      
  .m_axis_phase_tdata(m_axis_phase_tdata)        
);

   da_wave_send u_da_wave_send(
    .clk         (clk_100M), 
    .rst_n       (rst_n),
    .rd_data     (m_axis_data_tdata),
    .da_clk      (da_clk),  
    .da_data     (da_data)
    );

   ila_0 u_ila (
	.clk(ad_clk), 
	.probe0(Fword),  
	.probe1(da_data), 
	.probe2(ad_data) 
   ); 
///
    
  inout [14:0]DDR_addr;
  inout [2:0]DDR_ba;
  inout DDR_cas_n;
  inout DDR_ck_n;
  inout DDR_ck_p;
  inout DDR_cke;
  inout DDR_cs_n;
  inout [3:0]DDR_dm;
  inout [31:0]DDR_dq;
  inout [3:0]DDR_dqs_n;
  inout [3:0]DDR_dqs_p;
  inout DDR_odt;
  inout DDR_ras_n;
  inout DDR_reset_n;
  inout DDR_we_n;
  inout FIXED_IO_ddr_vrn;
  inout FIXED_IO_ddr_vrp;
  inout [53:0]FIXED_IO_mio;
  inout FIXED_IO_ps_clk;
  inout FIXED_IO_ps_porb;
  inout FIXED_IO_ps_srstb;

///4///
  wire key1_value;
  wire key1_flag;
  wire     clk_100M;
  wire [23 : 0]   Fword; 
  wire [0:0]      fre_ctrl_word_en;    
  wire [0 : 0]    m_axis_data_tvalid;
  wire [7 : 0]    m_axis_data_tdata;
  wire [0 : 0]    m_axis_phase_tvalid;
  wire [23 : 0]   m_axis_phase_tdata;
///

  wire [14:0]DDR_addr;
  wire [2:0]DDR_ba;
  wire DDR_cas_n;
  wire DDR_ck_n;
  wire DDR_ck_p;
  wire DDR_cke;
  wire DDR_cs_n;
  wire [3:0]DDR_dm;
  wire [31:0]DDR_dq;
  wire [3:0]DDR_dqs_n;
  wire [3:0]DDR_dqs_p;
  wire DDR_odt;
  wire DDR_ras_n;
  wire DDR_reset_n;
  wire DDR_we_n;
  wire FIXED_IO_ddr_vrn;
  wire FIXED_IO_ddr_vrp;
  wire [53:0]FIXED_IO_mio;
  wire FIXED_IO_ps_clk;
  wire FIXED_IO_ps_porb;
  wire FIXED_IO_ps_srstb;

  design_1 design_1_i
       (.DDR_addr(DDR_addr),
        .DDR_ba(DDR_ba),
        .DDR_cas_n(DDR_cas_n),
        .DDR_ck_n(DDR_ck_n),
        .DDR_ck_p(DDR_ck_p),
        .DDR_cke(DDR_cke),
        .DDR_cs_n(DDR_cs_n),
        .DDR_dm(DDR_dm),
        .DDR_dq(DDR_dq),
        .DDR_dqs_n(DDR_dqs_n),
        .DDR_dqs_p(DDR_dqs_p),
        .DDR_odt(DDR_odt),
        .DDR_ras_n(DDR_ras_n),
        .DDR_reset_n(DDR_reset_n),
        .DDR_we_n(DDR_we_n),
        .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
        .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
        .FIXED_IO_mio(FIXED_IO_mio),
        .FIXED_IO_ps_clk(FIXED_IO_ps_clk),
        .FIXED_IO_ps_porb(FIXED_IO_ps_porb),
        .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb));
endmodule

代码保存以后,把该文件置顶~

之后生成比特文件,此时我把生成的比特文件下载到板子上验证了一下,观察正常一切工作~

下面导出硬件~

勾选包含比特文件~

4.3、利用SDK生成BOOT.BIN文件

启动SDK软件 File→Launch SDK,弹出的页面依次点击OK,在SDK页面如图所示:

输入名称为FSBL,之后点击“Next”~

接下来的页面选ZYNQ FSBL,然后点Finish~

下面生成BOOT.BIN启动文件:

选择输出位置:

页面右下方点击Add,添加生成的FSBL.elf文件。

同样的步骤添加比特文件

两个文件都添加后,点击页面底部的Great Image

此时,你可以在之前设置的输出位置找到BOOT.bin文件~

4.4、固化结果测试

拷贝文件到SD卡的boot分区,开发板设置为SD卡启动模式,连接示波器,开发板上电观察:

可以看到已经固化成功~

总结

通过本次实验,我们成功地实现了一个DDS系统,验证了通过按键使DDS信号输出频率在1MHz、3MHz、5MHz、10MHz、15MHz、20MHz、25MHz和30MHz之间切换,并且在每个频率下保持稳定。此外还完成了对这个仅有PL端(FPGA)逻辑资源的DDS工程进行程序固化。这算是在zynq的学习上又前进了小小的一步~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空lg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值