AD9528是ADI(亚德诺半导体技术有限公司, Analog Devices, Inc. 简称ADI )出品的一款双级PLL,集成JESD204B SYSREF发生器,可用于多器件同步。
一级锁相环(PLL1):通过减少系统时钟的抖动,从而实现输入基准电压调理;
二级锁相环(PLL2):提供高频时钟,可实现来自时钟输出驱动器的较低积分抖动以及较低宽带噪声;
外部VCXO晶振(晶振频率范围:0~400MHz):提供PLL2所需的低噪声基准电压,以满足苛刻的相位噪声和抖动要求,实现可以接受的性能;
片内压控振荡器VCO:调谐频率范围为3.450 GHz至4.025 GHz;
集成的SYSREF发生器输出单次、N次或连续信号,并与PLL1和PLL2输出同步,以便对齐多个器件的时间。
AD9528的六路(channel0、channel1、channel2、channel3、channel12和channel13)输出最高频率可达1.25 GHz,剩余的八路输出最高频率可达1 GHz。每一路输出均可配置为直接从PLL1、PLL2或内部SYSREF发生器输出。14路输出通道的每一路都包含一个带数字相位粗调功能的分频器、一个模拟微调相位延迟模块,芯片的14路输出都具有时序对齐的高度灵活性。AD9528还可用作灵活的双通道输入缓冲器,以便实现14路器件时钟和/或SYSREF信号的分配。
芯片内部详细图解如下所示:
一、VIVADO中micro blaze连线:
二、SDK中代码配置步骤说明:
1、初始化SPI控制器:
struct xil_spi_init_param xil_spi_param = {
.type = SPI_PL,
.device_id = 0,
};
ad9528_param.spi_init = clkchip_spi_init_param;
2、定义配置并关联结构体:
struct ad9528_dev* clkchip_device;
struct ad9528_channel_spec ad9528_channels[14];
struct ad9528_init_param ad9528_param;
struct ad9528_platform_data ad9528_pdata;
// ad9528 defaults
ad9528_param.pdata = &ad9528_pdata;
ad9528_param.pdata->num_channels = 14;
ad9528_param.pdata->channels = &ad9528_channels[0];
3、初始化参数配置,以及通过返回值判断是否配置正确:
status = ad9528_init(&ad9528_param);
if(status) {
printf("error: ad9528_init() failed with %d\n", status);
}
4、PLL1、PLL2、输出通道使能、输出通道模式、输出通道分频等相关参数的配置,以及通过返回值判断是否配置正确:
status = ad9528_setup(&clkchip_device, ad9528_param);
if(status < 0) {
printf("error: ad9528_setup() failed with %d\n", status);
goto error_1;
}
else {
printf("success: ad9528_setup() is succeed %d\n", status);
}
三、部分配置程序说明:
1、PLL1 bypass:(主要是通过0X0108寄存器来进行配置,不需要REFA和REFB,用VCXO晶振作为PLL2的输入)
/*PLL1 Setup*/
ret = ad9528_spi_write_n(dev,
AD9528_PLL1_REF_A_DIVIDER,
0X02
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL1_REF_B_DIVIDER,
0X02
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL1_FEEDBACK_DIVIDER,
0X02
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL1_CHARGE_PUMP_CTRL,
0X18
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL1_CTRL,
0X61);
if (ret < 0)
return ret;
2、PLL2 配置:
pll2_ndiv = dev->pdata->pll2_vco_div_m1 * dev->pdata->pll2_n2_div;
if (!ad9528_pll2_valid_calib_div(pll2_ndiv)) {
printf("Feedback calibration divider value (%u) out of range\n", pll2_ndiv);
return -1;
}
pll2_ndiv_a_cnt = pll2_ndiv % 4;
pll2_ndiv_b_cnt = pll2_ndiv / 4;
ret = ad9528_spi_write_n(dev,
AD9528_PLL2_CHARGE_PUMP,
0X49
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL2_FEEDBACK_DIVIDER_AB,
0X87
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL2_CTRL,
0X03
);
if (ret < 0)
return ret;
vco_freq = div_u64((uint64_t)dev->pdata->vcxo_freq *
(dev->pdata->pll2_freq_doubler_en ? 2 : 1) * pll2_ndiv,
dev->pdata->pll2_r1_div);
vco_ctrl = AD_IF(pll2_freq_doubler_en || dev->pdata->pll2_r1_div != 1,
AD9528_PLL2_DOUBLER_R1_EN);
ret = ad9528_spi_write_n(dev,
AD9528_PLL2_VCO_CTRL, //0x0203
0X05
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL2_VCO_DIVIDER,
0X03
);
if (ret < 0)
return ret;
if (dev->pdata->pll2_vco_div_m1)
dev->ad9528_st.vco_out_freq[AD9528_VCO] =
vco_freq / dev->pdata->pll2_vco_div_m1;
else
dev->ad9528_st.vco_out_freq[AD9528_VCO] = vco_freq;
dev->ad9528_st.vco_out_freq[AD9528_VCXO] = dev->pdata->vcxo_freq;
ret = ad9528_spi_write_n(dev,
AD9528_PLL2_R1_DIVIDER,
0X02
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL2_N2_DIVIDER,
0X09
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PLL2_LOOP_FILTER_CTRL,
0X00
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_CHANNEL_PD_EN,
0XF5
);
if (ret < 0)
return ret;
ret = ad9528_spi_write_n(dev,
AD9528_PD_EN,
0X10
);
if (ret < 0)
return ret;
ret = ad9528_io_update(dev);
if (ret < 0)
return ret;
if (dev->pdata->stat0_pin_func_sel != 0xFF) {
ret = ad9528_spi_write_n(dev, AD9528_STAT_MON0,
0X0A
);
if (ret < 0)
return ret;
stat_en_mask |= AD9528_STAT0_PIN_EN;
}
if (dev->pdata->stat1_pin_func_sel != 0xFF) {
ret = ad9528_spi_write_n(dev, AD9528_STAT_MON1,
0X03
);
if (ret < 0)
return ret;
stat_en_mask |= AD9528_STAT1_PIN_EN;
}
if (stat_en_mask) {
ret = ad9528_spi_write_n(dev, AD9528_STAT_PIN_EN,
0X0C
);
if (ret < 0)
return ret;
}
ret = ad9528_io_update(dev);
if (ret < 0)
return ret;
3、通道分频配置(参数初始化配置的时候就设置好了各个通道需要的分频系数):
for (i = 0; i < init_param->pdata->num_channels; i++) {
(&init_param->pdata->channels[i])->channel_num = 0;
(&init_param->pdata->channels[i])->sync_ignore_en = 0;
(&init_param->pdata->channels[i])->output_dis = 0;
(&init_param->pdata->channels[i])->driver_mode = DRIVER_MODE_LVDS;
(&init_param->pdata->channels[i])->signal_source = SOURCE_VCO;
(&init_param->pdata->channels[i])->divider_phase = 0;
(&init_param->pdata->channels[i])->channel_divider = 10;
}
4、判断配置是否成功:
1)、通过上面提及到的每个函数的返回值来判断是否配置正确;
2)、芯片上有两个状态信号,这里我是直接连到了板子上的两个LED灯,并通过灯的亮灭状态来判断PLL2是否锁定;
3)、用示波器点测通道是否能正确输出所配置的时钟频率大小。