前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ad9528_setup()函数详解

ad9528_setup()函数详解

作者头像
数字积木
发布2022-06-14 16:59:02
4941
发布2022-06-14 16:59:02
举报
文章被收录于专栏:数字积木数字积木

完成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 range\n", 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 ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-05-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数字积木 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档