ad9528_setup()函数详解

2024-06-30 14:03:29 浏览数 (2)

欢迎关注微信公众号【数字积木】,更精彩的内容等着你!

完成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 )。

代码语言:javascript复制
// 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 )。

代码语言:javascript复制
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 ()

代码语言:javascript复制
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 )。

代码语言:javascript复制
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

代码语言:javascript复制
// 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 ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

0 人点赞