欢迎关注微信公众号【数字积木】,更精彩的内容等着你!
完成AD9528参数配置后, 运行 ad9528_setup(..) 函数开始AD9528的配置。
ad9528_setup(..) 根据初始化配置中的参数,计算对应寄存器的值,并通过 SPI 将各个寄存器的值写入到AD9528芯片中,完成对AD9528芯片的配置。
ad952_setup 的函数原型如下:
代码语言:javascript复制int32_t ad9528_setup( struct ad9528_dev **device,
struct ad9528_init_param init_param);
结构体ad9528_dev 和 ad9528_init_param 的原型如下,用于保存主机的 SPI,GPIO 配置信息以及AD9528的参数配置:
代码语言:javascript复制struct ad9528_dev { spi_desc *spi_desc; /* SPI */ gpio_desc *gpio_resetb; /* GPIO */ /* Device Settings */ struct ad9528_state ad9528_st; struct ad9528_platform_data *pdata; }; struct ad9528_init_param { spi_init_param spi_init; /* SPI */ gpio_init_param *gpio_resetb; /* GPIO */ struct ad9528_platform_data *pdata; /* Device Settings */ };
ad9528_platform_data 同样是一个结构体,具体内容在 《AD9528配置》章节以及说明。
ad9528_setup()的流程:
1,定义 ad9528_dev 类型结构体变量 dev 并为之分配内存空间。
代码语言:javascript复制struct ad9528_dev *dev;
dev = (struct ad9528_dev *)malloc(sizeof(*dev));
2,将初始化结构体变量中的配置参数赋值给 dev 结构体,并初始化 SPI控制器 以及 复位用的GPIO控制器 。
代码语言:javascript复制dev->pdata = init_param.pdata;
ret = spi_init(&dev->spi_desc, &init_param.spi_init); /* SPI */
ret = gpio_get_optional(&dev->gpio_resetb, init_param.gpio_resetb); /* GPIO */
3,ad9528 复位 ,执行 ad9528_reset(..) 函数 。
函数 ad9528_reset(..) 中 先对芯片进行硬件复位(GPIO复位),配置AD9528 的spi控制器(4-wire SPI),并进行软件复位(0X0000 , bit7 = 1 ) 。
代码语言:javascript复制int32_t ad9528_reset(struct ad9528_dev *dev) { ....... s = gpio_direction_output(dev->gpio_resetb, 0); mdelay(100); s = gpio_direction_output(dev->gpio_resetb, 1); mdelay(100); ....... s = ad9528_spi_write_n(dev, AD9528_SERIAL_PORT_CONFIG, AD9528_SER_CONF_SOFT_RESET | (dev->pdata->spi3wire ? 0 :AD9528_SER_CONF_SDO_ACTIVE) ); mdelay(100); s = ad9528_spi_write_n(dev, AD9528_SERIAL_PORT_CONFIG_B, 0x00); ...... }
4,设置SPI读寄存器值来自于缓冲副本( buffered copy),并执行 io_update ( 0X0F , bit 0 = 1 ) 。
代码语言:javascript复制ret = ad9528_spi_write_n(dev, AD9528_SERIAL_PORT_CONFIG_B,
AD9528_SER_CONF_READ_BUFFERED);
ret = ad9528_io_update(dev);
5,验证SPI读写是否正确。
通过读取芯片 CHIP_ID ( Chip type , Clock part serial ID , Part versions ) 值,检查读回的值和芯片预设的值是否一致来判断SPI 读是否正确。( 检查寄存器 0x05 , 0x04 , 0x03 的 值是否为 0x00 , 0xff ,0x05 )
代码语言:javascript复制ret = ad9528_spi_read_n(dev, AD9528_CHIP_ID, ®_data); if ((reg_data & 0xFFFFFF) != AD9528_SPI_MAGIC) { // AD9528_SPI_MAGIC = 0x00ff05 printf("AD9528 SPI Read Verify failed (0x%X).n", reg_data); ....... }
6,PLL1 设置。
设置参考A , 参考B , PLL1反馈路径上的时钟分频数。
代码语言:javascript复制/* PLL1 Setup */
// refa div, 0x100 , 0x101
ret = ad9528_spi_write_n(dev, AD9528_PLL1_REF_A_DIVIDER,
dev->pdata->refa_r_div);
// refb div , 0x102 , 0x103
ret = ad9528_spi_write_n(dev, AD9528_PLL1_REF_B_DIVIDER,
dev->pdata->refb_r_div);
// pll feedback div , 0x104 ,0x105
ret = ad9528_spi_write_n(dev, AD9528_PLL1_FEEDBACK_DIVIDER,
dev->pdata->pll1_feedback_div);
7, 根据是否跳过PLL1,对 PLL1的电荷泵进行设置 。
- 如果开启 bypass 模式 , 启用 PLL1 电荷泵的 Force holdover 模式 ( 0x106 , bit7 ) , PLL1 电荷泵保持在三态 (Tristates)。
- 如果禁用 bypass 模式, 则根初始化参数中设置的电荷泵电流值写到对应寄存器中。同时将电荷泵设置为正常模式(normal , 0x0107 , bit[1:0]),以及禁用自动保存( automatic holdover , 0x0107 , bit5 )。
// PLL1 Charge Pump Control , 0x106 , 0x107
ret = ad9528_spi_write_n(dev, AD9528_PLL1_CHARGE_PUMP_CTRL,
AD_IFE(pll1_bypass_en,
AD9528_PLL1_CHARGE_PUMP_TRISTATE, // 0x106 , bit7 = 1
AD9528_PLL1_CHARGE_PUMP_CURRENT_nA(dev->pdata-> pll1_charge_pump_current_nA) |
AD9528_PLL1_CHARGE_PUMP_MODE_NORMAL | // 0x0107 ,bit[1:0] = 0x3
AD9528_PLL1_CHARGE_PUMP_AUTO_TRISTATE_DIS)); // 0x0107 ,bit5 = 1
8,根据初始化参数中的设置值,配置PLL1的 其他参数 。
- 当启用 pll1_bypass 时 ,将 refa ( 0x0109 , bit3 ) , refb ( 0x0109 , bit4 ) , feedback ( 0x0109 , bit5 ) 的分频计数器复位,同时按照初始化配置设置 VCXO 的输入电平模式 ( 差分 / 单端P / 单端N ) 。
- 当禁用 pll1_bypass 时 ,根据初始化配置中的参数配置设置 refa , refb 的输入使能 ,差分输入 , 以及 VCXO 的输入电平模式 。
- pll1_bypass 使能
- 另外设置 refa , refb 的单端输入模式, pll1_feedback 路径的源 ( VCXO / PLL2 feedback divider output ),以及输入参考模式选择 ( ref_mode )。
ret = ad9528_spi_write_n(dev, AD9528_PLL1_CTRL,
// AD_IFE(pll1_bypass_en,.. , ..) begin
AD_IFE(pll1_bypass_en,
// pll1_bypass_en == 1 :
AD_IF(osc_in_diff_en, AD9528_PLL1_OSC_IN_DIFF_EN ) |
AD_IF(osc_in_cmos_neg_inp_en, AD9528_PLL1_OSC_IN_CMOS_NEG_INP_EN ) |
AD9528_PLL1_REFA_BYPASS_EN | // 0x0109 , bit3 =1,
AD9528_PLL1_REFB_BYPASS_EN | // 0x0109 , bit4 =1,
AD9528_PLL1_FEEDBACK_BYPASS_EN , // 0x0109 , bit5 =1, //
pll1_bypass_en == 0 :
AD_IF(refa_en, AD9528_PLL1_REFA_RCV_EN) |
AD_IF(refb_en, AD9528_PLL1_REFB_RCV_EN) |
AD_IF(osc_in_diff_en, AD9528_PLL1_OSC_IN_DIFF_EN) |
AD_IF(osc_in_cmos_neg_inp_en,AD9528_PLL1_OSC_IN_CMOS_NEG_INP_EN) |
AD_IF(refa_diff_rcv_en, AD9528_PLL1_REFA_DIFF_RCV_EN) |
AD_IF(refb_diff_rcv_en, AD9528_PLL1_REFB_DIFF_RCV_EN) ) |
// AD_IFE(pll1_bypass_en,.. , ..) end
AD_IF(refa_cmos_neg_inp_en, AD9528_PLL1_REFA_CMOS_NEG_INP_EN) |
AD_IF(refb_cmos_neg_inp_en, AD9528_PLL1_REFB_CMOS_NEG_INP_EN) |
AD_IF(pll1_feedback_src_vcxo, AD9528_PLL1_SOURCE_VCXO) |
AD9528_PLL1_REF_MODE(dev->pdata->ref_mode)
);
9 , 设置PLL2。
如果开启 pll2_bypass 则不使用PLL2 , 启用 PLL2 电荷泵的 Force holdover 模式 ( 0x0202 , bit[1:0] = 0 ) , PLL2 电荷泵保持在三态 (Tristates)。并设置 sysref 源为外部输入 。同时设置 VCXO ,VXO , SYSREF 的频率均为 vcxo 输入频率 ( PLL2被禁用 ,buffer模式 )。
代码语言:javascript复制if (dev->pdata->pll2_bypass_en) {
ret = ad9528_spi_write_n(dev, AD9528_PLL2_CTRL,
AD9528_PLL2_CHARGE_PUMP_MODE_TRISTATE);
ret = ad9528_spi_write_n(dev, AD9528_SYSREF_RESAMPLE_CTRL, 0x1);
dev->pdata->sysref_src = SYSREF_SRC_EXTERNAL;
dev->ad9528_st.vco_out_freq[AD9528_VCO] = dev->pdata->vcxo_freq;
dev->ad9528_st.vco_out_freq[AD9528_VCXO] = dev->pdata->vcxo_freq;
dev->ad9528_st.vco_out_freq[AD9528_SYSREF] = dev->pdata->vcxo_freq;
goto pll2_bypassed;
}
10 , 计算 PLL2 的 分频数 , 以及检查该分频数是否有效 。
PLL2的反馈回路有两级分频计数器 ,总分频数等于两级分频数相乘 。同时该分频数要 大于等于 16 且 小于等于 255 , 同时不能为 18 , 19 , 23 , 27 。
代码语言:javascript复制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 rangen", pll2_ndiv); } static bool ad9528_pll2_valid_calib_div(unsigned int div) { if (div < 16 || div > 255) return false; switch (div) { case 18: case 19: case 23: case 27: return false; } return true; }
11 , 设置 PLL2的电荷。
设置 PLL2的电荷泵电流 , 并计算内部 VCO 校准用的分频计数器的值 ( A divider , 0x0201 bit[7:6] ; B divider , 0x0201 bit[5:0] )。cal_div = ( P*B ) A , P = 4 。
代码语言:javascript复制pll2_ndiv_a_cnt = pll2_ndiv % 4;
pll2_ndiv_b_cnt = pll2_ndiv / 4;
// 0x0200
ret = ad9528_spi_write_n(dev, AD9528_PLL2_CHARGE_PUMP,
AD9528_PLL2_CHARGE_PUMP_CURRENT_nA(dev->pdata-> pll2_charge_pump_current_nA));
ret = ad9528_spi_write_n(dev, AD9528_PLL2_FEEDBACK_DIVIDER_AB,
AD9528_PLL2_FB_NDIV_A_CNT(pll2_ndiv_a_cnt) | // 0x0201 bit[7:6]
AD9528_PLL2_FB_NDIV_B_CNT(pll2_ndiv_b_cnt)); // 0x0201 bit[5:0]
12 , 将 PLL2 的电荷泵设置为正常模式。
将 PLL2 的电荷泵设置为正常模式(normal ),并根据初始化配置设置是否启用 PLL2 Frequency doubler 模式 。
代码语言:javascript复制ret = ad9528_spi_write_n(dev, AD9528_PLL2_CTRL,
AD9528_PLL2_CHARGE_PUMP_MODE_NORMAL | // 0x202 , bit[1:0] = 0
AD_IF(pll2_freq_doubler_en, AD9528_PLL2_FREQ_DOUBLER_EN)); // // 0x202 , bit5 (1 = enale , 0 =disable)
13 , 计算PLL2的VCO的频率( vco_freq ) , 检查是否开启 doubler 模式 , 并设置其寄存器 。
代码语言:javascript复制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);
// 0x203
ret = ad9528_spi_write_n(dev, AD9528_PLL2_VCO_CTRL,
vco_ctrl);
14 , 设置PLL2的VCO的 M1 分频数,如果 M1 为0 , 设置 M1分频器为 power-down 模式 ( 0x0204 , bit3 = 1)。
代码语言:javascript复制// 0x204
ret = ad9528_spi_write_n(dev, AD9528_PLL2_VCO_DIVIDER,
AD9528_PLL2_VCO_DIV_M1(dev->pdata->pll2_vco_div_m1) | // 0x0204 , bit[2:0]
AD_IFE(pll2_vco_div_m1, 0, AD9528_PLL2_VCO_DIV_M1_PWR_DOWN_EN));
// 0x0204,bit3; 1 = powers down the M1 divider. 0 = normal operation.
15 , 设置各个通道输出源的频率值
- PLL1 OUT(AD9528_VCXO)
- PLL2 OUT(AD9528_VCO)
- SYSREF OUT ()
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;
16 ,设置PLL2的R1分频计数器值 。
代码语言:javascript复制// 0x207
ret = ad9528_spi_write_n(dev, AD9528_PLL2_R1_DIVIDER,
AD9528_PLL2_R1_DIV(dev->pdata->pll2_r1_div)
);
17 , 设置 PLL2 的 N2 分频计数器值 。
代码语言:javascript复制// 0x208
ret = ad9528_spi_write_n(dev, AD9528_PLL2_N2_DIVIDER,
AD9528_PLL2_N2_DIV(dev->pdata->pll2_n2_div));
18 , 设置 PLL2 的环路滤波器的参数 。
(RPOLE2 , RZERO , CPOLE1 , Bypass internal RZERO)。
代码语言:javascript复制// 0x205
ret = ad9528_spi_write_n(dev, AD9528_PLL2_LOOP_FILTER_CTRL,
AD9528_PLL2_LOOP_FILTER_CPOLE1(dev->pdata->cpole1) |
AD9528_PLL2_LOOP_FILTER_RZERO(dev->pdata->rzero) |
AD9528_PLL2_LOOP_FILTER_RPOLE2(dev->pdata->rpole2) |
AD_IF(rzero_bypass_en, AD9528_PLL2_LOOP_FILTER_RZERO_BYPASS_EN)
);
19 , 设置各个输出通道的输出 。
- 如果通道禁用 ( output_dis = 1 ), 跳过该通道设置,该通道设置为芯片默认值 。
- 如果通道启用 ( output_dis = 0 ),设置通道的 输出驱动模式,输出时钟通道分频数,输出相位延迟,输出时钟源等等 。并计算 活动通道掩码(active_mask) 以及 忽略SYNC掩码 ( ignoresync_mask )。
for (i = 0; i < dev->pdata->num_channels; i ) {
chan = &dev->pdata->channels[i];
if (chan->output_dis)
continue;
if (chan->channel_num < AD9528_NUM_CHAN) {
active_mask |= (1 << chan->channel_num);
if (chan->sync_ignore_en)
ignoresync_mask |= (1 << chan->channel_num);
ret = ad9528_spi_write_n(dev, AD9528_CHANNEL_OUTPUT(chan->channel_num),
AD9528_CLK_DIST_DRIVER_MODE(chan->driver_mode) |
AD9528_CLK_DIST_DIV(chan->channel_divider) |
AD9528_CLK_DIST_DIV_PHASE(chan->divider_phase) |
AD9528_CLK_DIST_CTRL(chan->signal_source));
}
}
20 , 设置通道 power-down 寄存器 , 以及 同步忽略 寄存器 。
根据 活动通道掩码(active_mask) 以及 同步忽略 掩码 ( ignoresync_mask )来设置通道 power-down 寄存器 , 以及 同步忽略 寄存器 。
代码语言:javascript复制// 0x501 , 0x502
ret = ad9528_spi_write_n(dev, AD9528_CHANNEL_PD_EN,
AD9528_CHANNEL_PD_MASK(~active_mask));
// 0x32B , 0x32C
ret = ad9528_spi_write_n(dev, AD9528_CHANNEL_SYNC_IGNORE,
AD9528_CHANNEL_IGNORE_MASK(ignoresync_mask));
21 , 设置 SYSREF 。
代码语言:javascript复制// 0x400 , 0x401
ret = ad9528_spi_write_n(dev, AD9528_SYSREF_K_DIVIDER,
AD9528_SYSREF_K_DIV(dev->pdata->sysref_k_div));
sysref_ctrl = ( AD9528_SYSREF_PATTERN_MODE(dev->pdata->sysref_pattern_mode) |
AD9528_SYSREF_REQUEST_BY_PIN|
AD9528_SYSREF_PATTERN_TRIGGER_CTRL(dev->pdata->sysref_req_trigger_mode)|
AD9528_SYSREF_NSHOT_MODE(dev->pdata->sysref_nshot_mode)|
AD9528_SYSREF_SOURCE(dev->pdata->sysref_src) );
ret = ad9528_spi_write_n(dev, AD9528_SYSREF_CTRL, // 0x402 , 0x403 sysref_ctrl);
22 , 设置 Power-Down 寄存器。
- Bias generation power-down
- PLL2 power-down enable
- PLL1 power-down enable
- Clock distribution power-down enable
- Chip power-down enable
// 0x500 ret = ad9528_spi_write_n(dev, AD9528_PD_EN, AD9528_PD_BIAS | AD_IF(pll1_bypass_en, AD9528_PD_PLL1) | AD_IF(pll2_bypass_en, AD9528_PD_PLL2) );
23 , 执行 io_update 。
代码语言:javascript复制ret = ad9528_io_update(dev);
24 , 如果PLL2 未被禁用 , 启动PLL2校准 , 并通过轮询检查PLL2是否校准完成。
代码语言:javascript复制if (!dev->pdata->pll2_bypass_en) {
ret = ad9528_spi_write_n(dev, AD9528_PLL2_VCO_CTRL,
vco_ctrl | AD9528_PLL2_VCO_CALIBRATE);
ret = ad9528_io_update(dev);
// 轮询检查 AD9528_READBACK, // 0x509 , bit0 , 1 = VCO calibration in progress. 0 = VCO calibration not in progress.
ret = ad9528_poll(dev, AD9528_IS_CALIBRATING, 0);
}
25 , 开启SYSREF请求。
开启SYSREF请求, 根据SYSREF模式( N-shot , continuous ),芯片输出SYSREF信号。
代码语言:javascript复制sysref_ctrl |= AD9528_SYSREF_PATTERN_REQ;
ret = ad9528_spi_write_n(dev, AD9528_SYSREF_CTRL, sysref_ctrl);
26 , 设置状态引脚的模式 , 并开启对应状态引脚的输出。
代码语言:javascript复制if (dev->pdata->stat0_pin_func_sel != 0xFF) {
// 0x505
ret = ad9528_spi_write_n(dev, AD9528_STAT_MON0,
dev->pdata->stat0_pin_func_sel);
stat_en_mask |= AD9528_STAT0_PIN_EN;
}
if (dev->pdata->stat1_pin_func_sel != 0xFF) {
// 0x506
ret = ad9528_spi_write_n(dev, AD9528_STAT_MON1,
dev->pdata->stat1_pin_func_sel);
stat_en_mask |= AD9528_STAT1_PIN_EN;
}
if (stat_en_mask) {
// 0x507
ret = ad9528_spi_write_n(dev, AD9528_STAT_PIN_EN, stat_en_mask);
}
27 , 执行 io_update 以及 发出同步请求 。
代码语言:javascript复制ret = ad9528_io_update(dev);
ret = ad9528_sync(dev);
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧