赞
踩
目录
一个月没有继续更新是因为入手了一块新的板子——redpitaya。
自上一个实验完成后,在罗德频谱仪上观测信号,发现信号质量不够好,毕竟黑金开发板配套的AN108模块,它的ADDA芯片都是8位的,使我们输出DDS信号的SDRF最多是48,所以信号观测起来体验感很差,而火龙果板拥有支持最高125Mhz,14位的ADDA芯片,能很大程度上提供更精确和稳定的模拟信号输出,所以以后的实验就在这块板子上进行~
火龙果(redpitaya)开发板(SOC型号zynq7010),2018.3 Vivado,2018.3 SDK, 示波器。
工程下载地址:https://download.csdn.net/download/weixin_44852755/88580848?spm=1001.2014.3001.5503
1.学会利用redpitaya板输出波形,来测试DAAD这样的一个回环
2.学会利用PS端串口来控制AXI GPIO核的输出
3.最终实现能够通过串口输入来控制AXI GPIO核的输出,进而控制DDS IP核输出不同频率信号。
根据实验任务我大致画出本次实验的系统框图 ~ 如图所示:
创建VIVADO工程之前我们要看一下火龙果板的原理图:要注意它内部各器件的型号~
其余配置如下:
注意DDR3型号
添加AXI-GPIO模块:AXI GPIO用于控制和读取GPIO引脚的状态。它可以配置GPIO引脚的输入/输出模式、电平状态和中断触发方式,并可以读取GPIO引脚的当前状态。
GPIO 接口的位宽“GPIO Width”, 最大可以支持 32 位,意味着一个通道最多可以控制32个GPIO引脚。这里我们需要控制8种不同频率,因此将其设置为 4。
“Enable Dual Channel”可以使能 GPIO 通道 2,GPIO 2 的配置与 GPIO 完全相同。该选项默认没有勾选,即该IP工作在单通道模式下。
S_AXI中S代表slave是从端口,处理器作为Master可以控制AXI GPIO这个从器件。
AXI4-Lite作为轻量级的接口可以实现简单寄存器的配置和少量的数据传输,READ_REG是读寄存器,Interrupt Registers是中断寄存器,AXI4-Lite可以实现对这两个寄存器的配置。三态缓冲器不包含在AXI GPIO里,是工具在顶层文件里自动添加的。
GPIO内核包含寄存器(输入输出和中断)和多路复用器(选择两个GPIO的通道),GPIO_DATA是数据寄存器(Data Register),读数据和写数据都是通过这个寄存器。
GPIO_TRI是三态控制寄存器——逻辑高1、逻辑低0或高阻抗(高阻态),用来控制GPIO引脚作为输入还是输出,GPIO_T是三态的使能信号,GPIO_T=1配置为输入模式(in),GPIO_T=0配置为输出模式(out)(对应前面IP核窗口的两个参数设置)。GPIO_I 是用于输入,GPIO_O 是用于输出。GPIO_WIDTH是用户指定的位宽,可以配置在1位到32位之间。
GPIO位宽分别是1跟4时:
此外来自PL端的输入数据一方面进入READ_REG,一方面进入中断检测模块Interrupt Detection。只要输入端口数据发生改变就可以生成中断,中断检测实现该功能并通过ip2intc_irpt输出一个中断。
添加配置完这两个IP核后,点击Diagram 窗口的1、2:
看到配置完成的DDS IP核你可能会有疑惑,为什么m_axis_data_tdata是32位的?我设置的SFDR为84,那么对应的位宽应该是14位,而且选了sin and cos输出后,m_axis_data_tdata不应该是28位吗?
数据手册给出解释:
因为选的是正交输出,所以最后输出端口是32位的,由于输出采用的是axi总线,因此输出数据位于M_AXIS_DATA_TDATA中,那么正余弦输出结果是如何组合成M_AXIS_DATA_TDATA的呢?
输出DATA通道TDATA结构将正弦和余弦输出字段符号扩展到下一个字节边界,然后以最低有效部分的余弦进行连接,以创建m_axis_data_tdata。如果仅选择正弦或余弦之一,则将其符号扩展并放入m_axis_data_tdata的最低有效部分。
下图显示了这三种配置的TDATA的内部结构。正交输出,仅余弦和仅正弦。例如,在图中显示了11位输出,符号扩展到16位。 <<<表示符号扩展名:
返回到工程实际来看,高29-16位是带符号的正弦信号,低13-0位是带符号的余弦信号,也就是说这32位的信号其中30、31、15、14 为符号扩展位。
所以我引入了两个Slice模块,把高位正弦信号与低位余弦信号分离的同时,剔除掉符号扩展位:生成了有符号的14位正弦信号与有符号的14位余弦信号
经符号转化后,送入ADDA模块~
当然你也可以DDS IP核设置SFDR为96,直接输出16位的正弦(31-16)、余弦信号(15-0),不存在符号扩展位。同样的步骤在你利用Slice IP核分离完正余弦信号后,经过有符号到无符号数的转化、再利用Slice IP核去除这16位无符号信号的最低两位(范围改成15-2),这样做的代价可能会些影响信号的精度~(记得修改signed_to_unsigned模块)
自定义ADDA ip核
-
- module red_ADDA_shell (
-
- input [0:0] fclk , //[0]-125MHz
- input [0:0] frstn ,
- // ADC
- input [14-1:0] adc_dat_0_i , // ADC data
- input [14-1:0] adc_dat_1_i , // ADC data
- input [ 2-1:0] adc_clk_i , // ADC clock {p,n}
- output [ 2-1:0] adc_clk_o , // optional ADC clock source (unused)
- output adc_cdcs_o , // ADC clock duty cycle stabilizer
- output [14-1:0] rtl_adc_0_o , // rtl side adc 0 output
- output [14-1:0] rtl_adc_1_o , // rtl side adc 1 output
- output rtl_adc_clk_o , // rtl side adc clk output
- // DAC
- output [14-1:0] dac_dat_o , // DAC combined data
- output dac_wrt_o , // DAC write
- output dac_sel_o , // DAC channel select
- output dac_clk_o , // DAC clock
- output dac_rst_o , // DAC reset
- input [14-1:0] rtl_dac_0_i,
- input [14-1:0] rtl_dac_1_i,
- output rtl_dac_clk );
-
-
-
- red_pitaya_ADDA U_adda(
- .fclk (fclk ), //[0]-125MHz
- .frstn (frstn ),
- .adc_dat_0_i (adc_dat_0_i ), // ADC data
- .adc_dat_1_i (adc_dat_1_i ), // ADC data
- .adc_clk_i (adc_clk_i ), // ADC clock {p,n}
- .adc_clk_o (adc_clk_o ), // optional ADC clock source (unused)
- .adc_cdcs_o (adc_cdcs_o ), // ADC clock duty cycle stabilizer
- .rtl_adc_0_o (rtl_adc_0_o ), // rtl side adc 0 output
- .rtl_adc_1_o (rtl_adc_1_o ), // rtl side adc 1 output
- .rtl_adc_clk_o (rtl_adc_clk_o ), // rtl side adc clk output
- .dac_dat_o (dac_dat_o ), // DAC combined data
- .dac_wrt_o (dac_wrt_o ), // DAC write
- .dac_sel_o (dac_sel_o ), // DAC channel select
- .dac_clk_o (dac_clk_o ), // DAC clock
- .dac_rst_o (dac_rst_o ), // DAC reset
- .rtl_dac_0_i (rtl_dac_0_i ),
- .rtl_dac_1_i (rtl_dac_1_i ),
- .rtl_dac_clk (rtl_dac_clk ));
-
- endmodule
- module red_pitaya_ADDA (
-
- input logic [0:0] fclk , //[0]-125MHz
- input logic [0:0] frstn ,
- // ADC
- input logic [14-1:0] adc_dat_0_i , // ADC data
- input logic [14-1:0] adc_dat_1_i , // ADC data
- input logic [ 2-1:0] adc_clk_i , // ADC clock {p,n}
- output logic [ 2-1:0] adc_clk_o , // optional ADC clock source (unused)
- output logic adc_cdcs_o , // ADC clock duty cycle stabilizer
- output logic [14-1:0] rtl_adc_0_o , // rtl side adc 0 output
- output logic [14-1:0] rtl_adc_1_o , // rtl side adc 1 output
- output logic rtl_adc_clk_o , // rtl side adc clk output
- // DAC
- output logic [14-1:0] dac_dat_o , // DAC combined data
- output logic dac_wrt_o , // DAC write
- output logic dac_sel_o , // DAC channel select
- output logic dac_clk_o , // DAC clock
- output logic dac_rst_o , // DAC reset
- input logic [14-1:0] rtl_dac_0_i,
- input logic [14-1:0] rtl_dac_1_i,
- output logic rtl_dac_clk
-
- );
-
- // PLL signals
- logic adc_clk_in; //ADC 时钟输入信号。
- logic pll_adc_clk; //ADC 时钟的锁相环信号。
- logic pll_dac_clk_1x; //DAC 时钟的锁相环信号
- logic pll_dac_clk_2x; //DAC 时钟的锁相环信号,频率是 pll_dac_clk_1x 的两倍。
- logic pll_dac_clk_2p; //DAC 时钟的锁相环信号,相位是 pll_dac_clk_1x 的两倍。
- logic pll_ser_clk; //串行时钟的锁相环信号。
- logic pll_locked;
-
-
- // ADC clock/reset
- logic adc_clk,adc_rstn;
-
- // stream bus type
- localparam type SBA_T = logic signed [14-1:0]; // 表示采集的数据流总线类型,具有 14 位带符号的数据。
-
- SBA_T [ 2-1:0] adc_dat; //一个大小为 2 的数据流数组,用于表示 ADC 数据。adc_dat 包含两个元素,每个元素都是带有 14 位符号的数据。
-
- // DAC 时钟信号
- logic dac_clk_1x,dac_clk_2x,dac_clk_2p,dac_rst;
-
- //DAC数据信号
- logic [14-1:0] dac_dat_a, dac_dat_b; //14 位的 DAC 数据信号 A 和 B。
-
- // PLL (clock and reset)
-
- // diferential clock input
- IBUFDS i_clk (.I (adc_clk_i[1]), .IB (adc_clk_i[0]), .O (adc_clk_in));
- // IBUFDS: 差分输入缓冲器,用于处理差分信号。adc_clk_i[1] 和 adc_clk_i[0] 是差分时钟信号的两个分量。IBUFDS 模块接收这两个差分信号作为输入,并将它们转换为单端信号 adc_clk_in,以便后续的逻辑电路可以使用这个单端信号进行处理。
-
- red_pitaya_pll pll (
- // inputs
- .clk (adc_clk_in), // clock
- .rstn (frstn[0] ), // reset - active low,将输入时钟 adc_clk_in 和复位信号 frstn[0] 作为输入,为不同的模块提供多个时钟输出和锁定状态输出。
- // output clocks
- .clk_adc (pll_adc_clk ), // ADC clock
- .clk_dac_1x (pll_dac_clk_1x), // DAC clock 125MHz
- .clk_dac_2x (pll_dac_clk_2x), // DAC clock 250MHz
- .clk_dac_2p (pll_dac_clk_2p), // DAC clock 250MHz -45DGR
- .clk_ser (pll_ser_clk ), // fast serial clock
-
- // status outputs
- .pll_locked (pll_locked)
- );
-
-
- logic ser_clk ;//用于串行通信的时钟信号。
- BUFG bufg_adc_clk (.O (adc_clk ), .I (pll_adc_clk ));
- BUFG bufg_dac_clk_1x (.O (dac_clk_1x), .I (pll_dac_clk_1x));
- BUFG bufg_dac_clk_2x (.O (dac_clk_2x), .I (pll_dac_clk_2x));
- BUFG bufg_dac_clk_2p (.O (dac_clk_2p), .I (pll_dac_clk_2p));
- BUFG bufg_ser_clk (.O (ser_clk ), .I (pll_ser_clk ));
-
-
- //复位逻辑,adc_rstn: ADC 复位信号,与 frstn[0] 和 pll_locked 有关。dac_rst:
- // ADC reset (active low)
- always @(posedge adc_clk)
- adc_rstn <= frstn[0] & pll_locked;
- // DAC reset (active high),DAC 复位信号,与 frstn[0] 和 pll_locked 有关。
- always @(posedge dac_clk_1x)
- dac_rst <= ~frstn[0] | ~pll_locked;
-
-
- // ADC IO
-
-
- // generating ADC clock is disabled,adc_clk_o: 这个信号被设定为固定的值 2'b10,意味着 ADC 时钟生成被禁用。系统中已经存在一个稳定的外部时钟源,因此不需要通过逻辑电路生成 ADC 时钟。在这种情况下,禁用内部时钟生成可以节省资源并避免冲突。
- assign adc_clk_o = 2'b10;
-
- // ADC clock duty cycle stabilizer is enabled,表明 ADC 时钟占空比稳定器被启用,并且将信号 adc_cdcs_o 赋值为 1。ADC 时钟占空比稳定器(Clock Duty Cycle Stabilizer,CDCS)用于确保 ADC 时钟的占空比保持稳定,以提高系统的稳定性和性能。
- assign adc_cdcs_o = 1'b1 ;
- logic [2-1:0] [14-1:0] adc_dat_raw;//一个 2x14 的二维数组,用于存储 ADC 采集到的原始数据。
- // IO block registers should be used here
- always @(posedge adc_clk)
- begin
- adc_dat_raw[0] <= adc_dat_0_i[13:0];
- adc_dat_raw[1] <= adc_dat_1_i[13:0];
- end
- //通过输出 ADC 数据到 rtl_adc_0_o 和 rtl_adc_1_o,可以方便地对数据进行监控和调试,以确保 ADC 模块正常工作。这些输出端口可以连接到外部设备或接口,以便将 ADC 数据导出到外部系统或外部显示设备进行显示或记录。
- always @(posedge adc_clk)
- begin
- rtl_adc_0_o <= adc_dat_0_i;
- rtl_adc_1_o <= adc_dat_1_i;
- end
- assign rtl_adc_clk_o = adc_clk;
-
- // transform into 2's complement (negative slope),adc_dat[0] 和 adc_dat[1] 用于将 ADC 数据转换为 2 的补码形式,以便后续处理或存储。
- assign adc_dat[0] = {adc_dat_raw[0][14-1], ~adc_dat_raw[0][14-2:0]};
- assign adc_dat[1] = {adc_dat_raw[1][14-1], ~adc_dat_raw[1][14-2:0]};
-
- // DAC IO
-
-
- assign rtl_dac_clk = dac_clk_1x;
- always @(posedge dac_clk_1x)
- begin
- dac_dat_a <= rtl_dac_0_i;
- dac_dat_b <= rtl_dac_1_i;
- end
-
- // DDR outputs
- ODDR oddr_dac_clk (.Q(dac_clk_o), .D1(1'b0 ), .D2(1'b1 ), .C(dac_clk_2p), .CE(1'b1), .R(1'b0 ), .S(1'b0));
- ODDR oddr_dac_wrt (.Q(dac_wrt_o), .D1(1'b0 ), .D2(1'b1 ), .C(dac_clk_2x), .CE(1'b1), .R(1'b0 ), .S(1'b0));
- ODDR oddr_dac_sel (.Q(dac_sel_o), .D1(1'b1 ), .D2(1'b0 ), .C(dac_clk_1x), .CE(1'b1), .R(dac_rst), .S(1'b0));
- ODDR oddr_dac_rst (.Q(dac_rst_o), .D1(dac_rst ), .D2(dac_rst ), .C(dac_clk_1x), .CE(1'b1), .R(1'b0 ), .S(1'b0));
- ODDR oddr_dac_dat [14-1:0] (.Q(dac_dat_o), .D1(dac_dat_b), .D2(dac_dat_a), .C(dac_clk_1x), .CE(1'b1), .R(dac_rst), .S(1'b0));
- endmodule:red_pitaya_ADDA
- module red_pitaya_pll (
- // inputs
- input logic clk , // clock
- input logic rstn , // reset - active low
- // output clocks
- output logic clk_adc , // ADC clock
- output logic clk_dac_1x, // DAC clock
- output logic clk_dac_2x, // DAC clock
- output logic clk_dac_2p, // DAC clock
- output logic clk_ser , // fast serial clock
- // status outputs
- output logic pll_locked
- );
- logic clk_fb;
- PLLE2_ADV #(
- .BANDWIDTH ("OPTIMIZED"),
- .COMPENSATION ("ZHOLD" ),
- .DIVCLK_DIVIDE ( 1 ),
- .CLKFBOUT_MULT ( 8 ),
- .CLKFBOUT_PHASE ( 0.000 ),
- .CLKOUT0_DIVIDE ( 8 ),
- .CLKOUT0_PHASE ( 0.000 ),
- .CLKOUT0_DUTY_CYCLE ( 0.5 ),
- .CLKOUT1_DIVIDE ( 8 ),
- .CLKOUT1_PHASE ( 0.000 ),
- .CLKOUT1_DUTY_CYCLE ( 0.5 ),
- .CLKOUT2_DIVIDE ( 4 ),
- .CLKOUT2_PHASE ( 0.000 ),
- .CLKOUT2_DUTY_CYCLE ( 0.5 ),
- .CLKOUT3_DIVIDE ( 4 ),
- .CLKOUT3_PHASE (-45.000 ),
- .CLKOUT3_DUTY_CYCLE ( 0.5 ),
- .CLKOUT4_DIVIDE ( 4 ), // 4->250MHz, 2->500MHz
- .CLKOUT4_PHASE ( 0.000 ),
- .CLKOUT4_DUTY_CYCLE ( 0.5 ),
- .CLKOUT5_DIVIDE ( 4 ),
- .CLKOUT5_PHASE ( 0.000 ),
- .CLKOUT5_DUTY_CYCLE ( 0.5 ),
- .CLKIN1_PERIOD ( 8.000 ),
- .REF_JITTER1 ( 0.010 )
- ) pll (
- // Output clocks
- .CLKFBOUT (clk_fb ),
- .CLKOUT0 (clk_adc ),
- .CLKOUT1 (clk_dac_1x),
- .CLKOUT2 (clk_dac_2x),
- .CLKOUT3 (clk_dac_2p),
- .CLKOUT4 (clk_ser ),
-
- // Input clock control
- .CLKFBIN (clk_fb ),
- .CLKIN1 (clk ),
- .CLKIN2 (1'b0 ),
- // Tied to always select the primary input clock
- .CLKINSEL (1'b1 ),
- // Ports for dynamic reconfiguration
- .DADDR (7'h0 ),
- .DCLK (1'b0 ),
- .DEN (1'b0 ),
- .DI (16'h0),
- .DO ( ),
- .DRDY ( ),
- .DWE (1'b0 ),
- // Other control and status signals
- .LOCKED (pll_locked),
- .PWRDWN (1'b0 ),
- .RST (!rstn )
- );
- endmodule: red_pitaya_pll
解释一下red_pitaya_ADDA 模块的代码逻辑。
red_pitaya_pll 模块:这是一个锁相环模块,用于生成各种时钟信号,包括 ADC 时钟、DAC 时钟和其他内部时钟信号。它的输入是外部信号 clk 和 rstn,输出包括各种时钟信号如 clk_adc、clk_dac_1x、clk_dac_2x、clk_dac_2p、clk_ser 和 clk_pdm。它通过 PLL 控制时钟的频率和相位,并提供了锁定状态的输出信号 pll_locked。
ADC 逻辑部分:ADC 通过时钟信号 adc_clk 对输入的模拟数据进行采样。采样后的数据经过一定的处理后输出到 rtl_adc_0_o 和 rtl_adc_1_o 输出端口。这部分代码包括了时钟同步和数据处理逻辑。adc_dat_raw 数组用于存储采样后的原始数据。
DAC 逻辑部分:DAC 的逻辑包括了一些处理步骤。模块中,dac_dat_a 和 dac_dat_b 存储要输出的 DAC 数据,而 oddr_dac_clk、oddr_dac_wrt、oddr_dac_sel 和 oddr_dac_rst 控制 DAC 的时钟、写入、选择和复位操作。oddr_dac_dat 则负责输出 DAC 数据。这些部分一起工作以确保 DAC 正确运行并输出正确的模拟信号。
其中代码:
- ODDR oddr_dac_clk (.Q(dac_clk_o), .D1(1'b0 ), .D2(1'b1 ), .C(dac_clk_2p), .CE(1'b1), .R(1'b0 ), .S(1'b0));
- ODDR oddr_dac_wrt (.Q(dac_wrt_o), .D1(1'b0 ), .D2(1'b1 ), .C(dac_clk_2x), .CE(1'b1), .R(1'b0 ), .S(1'b0));
- ODDR oddr_dac_sel (.Q(dac_sel_o), .D1(1'b1 ), .D2(1'b0 ), .C(dac_clk_1x), .CE(1'b1), .R(dac_rst), .S(1'b0));
- ODDR oddr_dac_rst (.Q(dac_rst_o), .D1(dac_rst ), .D2(dac_rst ), .C(dac_clk_1x), .CE(1'b1), .R(1'b0 ), .S(1'b0));
- ODDR oddr_dac_dat [14-1:0] (.Q(dac_dat_o), .D1(dac_dat_b), .D2(dac_dat_a), .C(dac_clk_1x), .CE(1'b1), .R(dac_rst), .S(1'b0));
oddr_dac_clk: 这个实例用于生成 DAC 的时钟信号。它有两个输入数据位,分别是1'b0和1'b1,这意味着在时钟信号满足特定条件时,dac_clk_o会在不同的时钟周期交替为0和1。.C(dac_clk_2p)表示该时钟的控制信号,.CE(1'b1)表示使能控制信号,.R(1'b0)和.S(1'b0)分别表示异步复位和同步置位的控制信号。
同样地,oddr_dac_wrt: 这个实例用于生成 DAC 的写入控制信号。oddr_dac_sel: 这个实例用于生成 DAC 的通道选择信号。oddr_dac_rst: 这个实例用于生成 DAC 的复位信号。oddr_dac_dat [14-1:0]: 这个实例用于生成 DAC 的数据信号,dac_dat_b 和 dac_dat_a 是作为两个独立的输入数据传递给了模块的 D1 和 D2 端口,而 dac_clk_1x 是时钟信号传递给了 C 端口。这意味着这两个输入数据 dac_dat_b 和 dac_dat_a 通过时钟信号 dac_clk_1x 被输入到了模块中,并且经过模块内部的逻辑处理后,最终的输出结果会出现在 dac_dat_o 端口上。
自定义dds_contral ip核
- module dds_contral(
- input clk,
- input rst_n,
- input [3 : 0] key_PINC,
- output reg [23 : 0] Fword
-
- );
- always@(*)
- begin
- case(key_PINC)
- 4'b0001: Fword <= 'h20c4;
- 4'b0010: Fword <= 'h624c;
- 4'b0011: Fword <= 'ha3d4;
- 4'b0100: Fword <= 'h147a8;
- 4'b0101: Fword <= 'h1EB85;
- 4'b0110: Fword <= 'h28f5c;
- 4'b0111: Fword <= 'h33333;
- 4'b1000: Fword <= 'h3d70a;
- default: Fword <= 'h1EB85;
-
- endcase
- end
- endmodule
频率字的计算参考第一篇文章~不再细说。
自定义signed_to_unsigned模块:目的是为了在示波器上显示
- module signed_to_unsigned(
- DIN ,
- DOUT
- );
-
- parameter DWL = 14;
-
- input [DWL-1:0] DIN;
- output[DWL-1:0] DOUT;
- assign DOUT[DWL-1] = ~DIN[DWL-1];
- assign DOUT[DWL-2:0] = DIN[DWL-2:0];
-
- endmodule
-
因为ADDA芯片可以支持时钟频率为125MHZ,所以利用一个锁相环把PS端引出的时钟频率倍频到125Mhz后输出给ADDA模块~
red_ADDA_shell IP核输出 ADC 数据到 rtl_adc_0_o 和 rtl_adc_1_o,这里添加ILA_1咱们只观测rtl_adc_0_o,时钟由rtl_adc_clk_o提供。
此外添加ILA_2观测DDS IP输出的信号,最后我们可以与ILA_1观测的信号进行对比~
约束文件:
查看原理图可以很清晰的看到各个引脚位置~
- # ADC data
- set_property IOSTANDARD LVCMOS18 [get_ports {adc_dat_*_i[*]}]
- set_property IOB TRUE [get_ports {adc_dat_*_i[*]}]
-
- # ADC 0 data
- set_property PACKAGE_PIN Y17 [get_ports {adc_dat_0_i[0]}]
- set_property PACKAGE_PIN U17 [get_ports {adc_dat_0_i[1]}]
- set_property PACKAGE_PIN Y16 [get_ports {adc_dat_0_i[2]}]
- set_property PACKAGE_PIN W15 [get_ports {adc_dat_0_i[3]}]
- set_property PACKAGE_PIN W14 [get_ports {adc_dat_0_i[4]}]
- set_property PACKAGE_PIN Y14 [get_ports {adc_dat_0_i[5]}]
- set_property PACKAGE_PIN W13 [get_ports {adc_dat_0_i[6]}]
- set_property PACKAGE_PIN V12 [get_ports {adc_dat_0_i[7]}]
- set_property PACKAGE_PIN V13 [get_ports {adc_dat_0_i[8]}]
- set_property PACKAGE_PIN T14 [get_ports {adc_dat_0_i[9]}]
- set_property PACKAGE_PIN T15 [get_ports {adc_dat_0_i[10]}]
- set_property PACKAGE_PIN V15 [get_ports {adc_dat_0_i[11]}]
- set_property PACKAGE_PIN T16 [get_ports {adc_dat_0_i[12]}]
- set_property PACKAGE_PIN V16 [get_ports {adc_dat_0_i[13]}]
-
-
- # ADC 1 data
- set_property PACKAGE_PIN R18 [get_ports {adc_dat_1_i[0]}]
- set_property PACKAGE_PIN R16 [get_ports {adc_dat_1_i[1]}]
- set_property PACKAGE_PIN P18 [get_ports {adc_dat_1_i[2]}]
- set_property PACKAGE_PIN N17 [get_ports {adc_dat_1_i[3]}]
- set_property PACKAGE_PIN R19 [get_ports {adc_dat_1_i[4]}]
- set_property PACKAGE_PIN T20 [get_ports {adc_dat_1_i[5]}]
- set_property PACKAGE_PIN T19 [get_ports {adc_dat_1_i[6]}]
- set_property PACKAGE_PIN U20 [get_ports {adc_dat_1_i[7]}]
- set_property PACKAGE_PIN V20 [get_ports {adc_dat_1_i[8]}]
- set_property PACKAGE_PIN W20 [get_ports {adc_dat_1_i[9]}]
- set_property PACKAGE_PIN W19 [get_ports {adc_dat_1_i[10]}]
- set_property PACKAGE_PIN Y19 [get_ports {adc_dat_1_i[11]}]
- set_property PACKAGE_PIN W18 [get_ports {adc_dat_1_i[12]}]
- set_property PACKAGE_PIN Y18 [get_ports {adc_dat_1_i[13]}]
-
-
- set_property IOSTANDARD DIFF_HSTL_I_18 [get_ports adc_clk_i[*]]
- set_property PACKAGE_PIN U18 [get_ports adc_clk_i[1]]
- set_property PACKAGE_PIN U19 [get_ports adc_clk_i[0]]
-
- # Output ADC clock
- set_property IOSTANDARD LVCMOS18 [get_ports {adc_clk_o[*]}]
- set_property SLEW FAST [get_ports {adc_clk_o[*]}]
- set_property DRIVE 8 [get_ports {adc_clk_o[*]}]
- #set_property IOB TRUE [get_ports {adc_clk_o[*]}]
-
- set_property PACKAGE_PIN N20 [get_ports {adc_clk_o[0]}]
- set_property PACKAGE_PIN P20 [get_ports {adc_clk_o[1]}]
-
- # ADC clock stabilizer
- set_property IOSTANDARD LVCMOS18 [get_ports adc_cdcs_o]
- set_property PACKAGE_PIN V18 [get_ports adc_cdcs_o]
- set_property SLEW FAST [get_ports adc_cdcs_o]
- set_property DRIVE 8 [get_ports adc_cdcs_o]
-
- ### DAC
-
- # data
- set_property IOSTANDARD LVCMOS33 [get_ports {dac_dat_o[*]}]
- set_property SLEW SLOW [get_ports {dac_dat_o[*]}]
- set_property DRIVE 8 [get_ports {dac_dat_o[*]}]
- #set_property IOB TRUE [get_ports {dac_dat_o[*]}]
-
- set_property PACKAGE_PIN M19 [get_ports {dac_dat_o[0]}]
- set_property PACKAGE_PIN M20 [get_ports {dac_dat_o[1]}]
- set_property PACKAGE_PIN L19 [get_ports {dac_dat_o[2]}]
- set_property PACKAGE_PIN L20 [get_ports {dac_dat_o[3]}]
- set_property PACKAGE_PIN K19 [get_ports {dac_dat_o[4]}]
- set_property PACKAGE_PIN J19 [get_ports {dac_dat_o[5]}]
- set_property PACKAGE_PIN J20 [get_ports {dac_dat_o[6]}]
- set_property PACKAGE_PIN H20 [get_ports {dac_dat_o[7]}]
- set_property PACKAGE_PIN G19 [get_ports {dac_dat_o[8]}]
- set_property PACKAGE_PIN G20 [get_ports {dac_dat_o[9]}]
- set_property PACKAGE_PIN F19 [get_ports {dac_dat_o[10]}]
- set_property PACKAGE_PIN F20 [get_ports {dac_dat_o[11]}]
- set_property PACKAGE_PIN D20 [get_ports {dac_dat_o[12]}]
- set_property PACKAGE_PIN D19 [get_ports {dac_dat_o[13]}]
-
- # control
- set_property IOSTANDARD LVCMOS33 [get_ports dac_*_o]
- set_property SLEW FAST [get_ports dac_*_o]
- set_property DRIVE 8 [get_ports dac_*_o]
- #set_property IOB TRUE [get_ports dac_*_o]
-
- set_property PACKAGE_PIN M17 [get_ports dac_wrt_o]
- set_property PACKAGE_PIN N16 [get_ports dac_sel_o]
- set_property PACKAGE_PIN M18 [get_ports dac_clk_o]
- set_property PACKAGE_PIN N15 [get_ports dac_rst_o]
-
-
-
-
- #include <stdio.h>
- #include <string.h>
- #include "xparameters.h"
- #include "xgpio.h"
- #include "xuartps.h"
-
- #define GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID
- #define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID
- #define CHANNEL 1
-
- XGpio Gpio;
- XUartPs Uart_Ps;
-
- int main() {
- XUartPs_Config *Config;
- Config = XUartPs_LookupConfig(UART_DEVICE_ID);
- if (Config == NULL) {
- printf("无法找到UART配置。\n");
- return 1;
- }
-
- XUartPs *Uart_PsPtr = &Uart_Ps;
- XUartPs_CfgInitialize(Uart_PsPtr, Config, Config->BaseAddress);
- XUartPs_SetBaudRate(Uart_PsPtr, 115200);
-
- if (XGpio_Initialize(&Gpio, GPIO_DEVICE_ID) != XST_SUCCESS) {
- printf("GPIO初始化失败。\n");
- return 1;
- }
-
- XGpio_SetDataDirection(&Gpio, CHANNEL, 0x0);
-
- int input;
- int output;
- int gpio_output;
- int in_num; // 用整数存储输入
-
- while (1) {
- printf("输入一个数字频率模式:\n ");
- scanf("%d", &in_num); // 直接读取整数
-
- if (in_num >= 1 && in_num <= 8) {
- input = in_num;
-
- // 将输入转换为4位二进制数
- output = input & 0xF;
-
- // 将4位二进制数输出到GPIO
- XGpio_DiscreteWrite(&Gpio, CHANNEL, output);
-
- // 从GPIO读取值
- gpio_output = XGpio_DiscreteRead(&Gpio, CHANNEL);
-
- // 将从GPIO读取的值发送到串口
- char buffer[15];
- sprintf(buffer, "频率%d", gpio_output);
- XUartPs_Send(Uart_PsPtr, (u8 *)buffer, strlen(buffer)); // 发送整个字符串
- XUartPs_Send(Uart_PsPtr, (u8 *)"\n", 1); // 发送换行符
- } else {
- printf("输入错误。请确保输入一个1到8之间的数字。\n");
- }
- }
- return 0;
- }
代码通过 XUartPs_LookupConfig 函数查找 UART 的配置信息,如果找不到,则打印一条错误消息。然后通过 XUartPs_CfgInitialize 函数进行初始化,并设置波特率为 115200。然后,通过 XGpio_Initialize 函数初始化 GPIO 设备,并设置通道的数据方向为输出。接下来进入一个无限循环,循环中首先提示用户输入一个数字频率模式。
输入一个整数作为频率模式,如果用户输入的数字在 1 到 8 之间,代码将读取输入的数字并将其转换为一个 4 位的二进制数,接着,代码将这个 4 位二进制数写入到 GPIO 设备中,然后再从 GPIO 读取值。接着将从 GPIO 读取的值发送到 UART,以便通过串口发送出去。这部分代码使用了 sprintf 函数将读取的 GPIO 值转换为字符串,然后通过 XUartPs_Send 函数将字符串发送到串口。
CLTA+S保存代码并编译~
电脑与开发板用USB、JATG线相连,连接上示波器
选择自己电脑的串口号连接串口。
程序下载完成后,目光移至SDK Terminal
我们可以再回到VIVADO,把线从板子的DA输出连接到AD输入,观测这个回环~
由此可以看出,比较符合自己一开始的预期~
2023年11月9日补充:
我在redpitaya教程里找到了关于官方给的DA模块,首先你要有他们提供的FPGA例程文件夹:
我在这个例程里找到了他们定义的DA模块:
-
- `timescale 1 ns / 1 ps
-
- module axis_red_pitaya_dac #
- (
- parameter integer DAC_DATA_WIDTH = 14,
- parameter integer AXIS_TDATA_WIDTH = 32
- )
- (
- // PLL signals
- input wire aclk,
- input wire ddr_clk,
- input wire locked,
-
- // DAC signals
- output wire dac_clk,
- output wire dac_rst,
- output wire dac_sel,
- output wire dac_wrt,
- output wire [DAC_DATA_WIDTH-1:0] dac_dat,
-
- // Slave side
- output wire s_axis_tready,
- input wire [AXIS_TDATA_WIDTH-1:0] s_axis_tdata,
- input wire s_axis_tvalid
- );
-
- reg [DAC_DATA_WIDTH-1:0] int_dat_a_reg;
- reg [DAC_DATA_WIDTH-1:0] int_dat_b_reg;
- reg int_rst_reg;
-
- wire [DAC_DATA_WIDTH-1:0] int_dat_a_wire;
- wire [DAC_DATA_WIDTH-1:0] int_dat_b_wire;
-
- assign int_dat_a_wire = s_axis_tdata[DAC_DATA_WIDTH-1:0];
- assign int_dat_b_wire = s_axis_tdata[AXIS_TDATA_WIDTH/2+DAC_DATA_WIDTH-1:AXIS_TDATA_WIDTH/2];
-
- genvar j;
-
- always @(posedge aclk)
- begin
- if(~locked | ~s_axis_tvalid)
- begin
- int_dat_a_reg <= {(DAC_DATA_WIDTH){1'b0}};
- int_dat_b_reg <= {(DAC_DATA_WIDTH){1'b0}};
- end
- else
- begin
- int_dat_a_reg <= {int_dat_a_wire[DAC_DATA_WIDTH-1], ~int_dat_a_wire[DAC_DATA_WIDTH-2:0]};
- int_dat_b_reg <= {int_dat_b_wire[DAC_DATA_WIDTH-1], ~int_dat_b_wire[DAC_DATA_WIDTH-2:0]};
- end
- int_rst_reg <= ~locked | ~s_axis_tvalid;
- end
-
- ODDR ODDR_rst(.Q(dac_rst), .D1(int_rst_reg), .D2(int_rst_reg), .C(aclk), .CE(1'b1), .R(1'b0), .S(1'b0));
- ODDR ODDR_sel(.Q(dac_sel), .D1(1'b0), .D2(1'b1), .C(aclk), .CE(1'b1), .R(1'b0), .S(1'b0));
- ODDR ODDR_wrt(.Q(dac_wrt), .D1(1'b0), .D2(1'b1), .C(ddr_clk), .CE(1'b1), .R(1'b0), .S(1'b0));
- ODDR ODDR_clk(.Q(dac_clk), .D1(1'b0), .D2(1'b1), .C(ddr_clk), .CE(1'b1), .R(1'b0), .S(1'b0));
-
- generate
- for(j = 0; j < DAC_DATA_WIDTH; j = j + 1)
- begin : DAC_DAT
- ODDR ODDR_inst(
- .Q(dac_dat[j]),
- .D1(int_dat_a_reg[j]),
- .D2(int_dat_b_reg[j]),
- .C(aclk),
- .CE(1'b1),
- .R(1'b0),
- .S(1'b0)
- );
- end
- endgenerate
- assign s_axis_tready = 1'b1;
-
- endmodule
如何使用?
dds_contral模块的频率字要更改~计算方法看ZYNQ学习笔记(一):基于ZYNQ7020、AN108的DDS实验(VIO可控频率字)
xdc文件只保留DAC就可:
-
- ### DAC
-
- # data
- set_property IOSTANDARD LVCMOS33 [get_ports {dac_dat[*]}]
- set_property SLEW SLOW [get_ports {dac_dat[*]}]
- set_property DRIVE 8 [get_ports {dac_dat[*]}]
- #set_property IOB TRUE [get_ports {dac_dat_o[*]}]
-
- set_property PACKAGE_PIN M19 [get_ports {dac_dat[0]}]
- set_property PACKAGE_PIN M20 [get_ports {dac_dat[1]}]
- set_property PACKAGE_PIN L19 [get_ports {dac_dat[2]}]
- set_property PACKAGE_PIN L20 [get_ports {dac_dat[3]}]
- set_property PACKAGE_PIN K19 [get_ports {dac_dat[4]}]
- set_property PACKAGE_PIN J19 [get_ports {dac_dat[5]}]
- set_property PACKAGE_PIN J20 [get_ports {dac_dat[6]}]
- set_property PACKAGE_PIN H20 [get_ports {dac_dat[7]}]
- set_property PACKAGE_PIN G19 [get_ports {dac_dat[8]}]
- set_property PACKAGE_PIN G20 [get_ports {dac_dat[9]}]
- set_property PACKAGE_PIN F19 [get_ports {dac_dat[10]}]
- set_property PACKAGE_PIN F20 [get_ports {dac_dat[11]}]
- set_property PACKAGE_PIN D20 [get_ports {dac_dat[12]}]
- set_property PACKAGE_PIN D19 [get_ports {dac_dat[13]}]
-
- # control
- set_property IOSTANDARD LVCMOS33 [get_ports dac_*]
- set_property SLEW FAST [get_ports dac_*]
- set_property DRIVE 8 [get_ports dac_*]
- #set_property IOB TRUE [get_ports dac_*_o]
-
- set_property PACKAGE_PIN M17 [get_ports dac_wrt]
- set_property PACKAGE_PIN N16 [get_ports dac_sel]
- set_property PACKAGE_PIN M18 [get_ports dac_clk]
- set_property PACKAGE_PIN N15 [get_ports dac_rst]
-
-
-
-
软件还是用上面的就可以~实验现象自然是能根据不通输入输出不同波形~
工程下载地址:https://download.csdn.net/download/weixin_44852755/88580818?spm=1001.2014.3001.5503
遇见的问题:综合分析过程出现错误
[DRC REQP-1578] Input clock driver: Unsupported MMCME2_ADV connectivity. The signal system_i/clk_wiz_0/inst/clk_in1 on the system_i/clk_wiz_0/inst/mmcm_adv_inst/CLKIN1 pin of system_i/clk_wiz_0/inst/mmcm_adv_inst with COMPENSATION mode ZHOLD must be driven by a clock capable IO.(忘了截图了~跟下面类似,不过我用的锁相环是MMCM模式)
解决方法:
将source由“single ended clock capable pin”调为“global buffer”即可。再次implementation时候就不报错。
总体来说,整体工程的完成自己断断续续大概用了20天,虽然实现的功能不复杂,但真正做起来,对于自己这个初学者还是非常费时费力的~回顾这个过程自己走了很多弯路,但也学会了很多,比如自己还没记录下来的IP核如何封装,如何移植,不用ILA IP核怎么用另一种方式去添加观测信号,如何把工程固化到板子上等等。~路阻且长,还在路上~加油
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。