void LMS7_vlogf(const LMS7_log_level_t level, const char *format, va_list args)
{
    if (level > _log_level) return;
    char *message;
    vasprintf(&message, format, args);
    LMS7_log(level, message);
    free(message);
}
/***********************************************************************
 * TBB low band calibration
 **********************************************************************/
int Calibration_LowBand_TBB(LMS7002M_t *self, unsigned char ch)
{
    MIMO_Ctrl (self, ch);
    //#warning Not finished!!!
    //not finished!!!!
    LMS7_log(LMS7_WARNING, "Calibration_LowBand_TBB not implemented");
    return -1;
}
示例#3
0
int LMS7002M_set_data_clock(LMS7002M_t *self, const double fref, const double fout, double *factual)
{
    LMS7_logf(LMS7_INFO, "CGEN tune %f MHz begin", fout/1e6);

    //always use the channel A shadow, CGEN is in global register space
    LMS7002M_set_mac_ch(self, LMS_CHA);

    //The equations:
    // fref * N = fvco
    // fvco / fdiv = fout
    // fref * N = fout * fdiv
    int fdiv = 512+2;
    double Ndiv = 0;
    double fvco = 0;

    //calculation loop to find dividers that are possible
    while (true)
    {
        //try the next even divider
        fdiv -= 2;

        Ndiv = fout*fdiv/fref;
        fvco = fout*fdiv;
        LMS7_logf(LMS7_TRACE, "Trying: fdiv = %d, Ndiv = %f, fvco = %f MHz", fdiv, Ndiv, fvco/1e6);

        //check dividers and vco in range...
        if (fdiv < 2) return -1;
        if (fdiv > 512) continue;
        if (Ndiv < 4) return -1;
        if (Ndiv > 512) continue;

        //check vco boundaries
        if (fvco < LMS7002M_CGEN_VCO_LO) continue;
        if (fvco > LMS7002M_CGEN_VCO_HI) continue;

        break; //its good
    }
    LMS7_logf(LMS7_DEBUG, "Using: fdiv = %d, Ndiv = %f, fvco = %f MHz", fdiv, Ndiv, fvco/1e6);

    //stash the freq now that we know the loop above passed
    self->cgen_freq = fout;
    self->cgen_fref = fref;

    //reset
    self->regs->reg_0x0086_reset_n_cgen = 0;
    LMS7002M_regs_spi_write(self, 0x0086);
    self->regs->reg_0x0086_reset_n_cgen = 1;
    LMS7002M_regs_spi_write(self, 0x0086);

    //configure and enable synthesizer
    self->regs->reg_0x0086_en_intonly_sdm_cgen = 0; //support frac-N
    self->regs->reg_0x0086_en_sdm_clk_cgen = 1; //enable
    self->regs->reg_0x0086_pd_cp_cgen = 0; //enable
    self->regs->reg_0x0086_pd_fdiv_fb_cgen = 0; //enable
    self->regs->reg_0x0086_pd_fdiv_o_cgen = 0; //enable
    self->regs->reg_0x0086_pd_sdm_cgen = 0; //enable
    self->regs->reg_0x0086_pd_vco_cgen = 0; //enable
    self->regs->reg_0x0086_pd_vco_comp_cgen = 0; //enable
    self->regs->reg_0x0086_en_g_cgen = 1;
    self->regs->reg_0x0086_en_coarse_cklgen = 0;
    self->regs->reg_0x008b_coarse_start_cgen = 0;
    self->regs->reg_0x0086_spdup_vco_cgen = 1; //fast settling
    LMS7002M_regs_spi_write(self, 0x0086);

    //program the N divider
    const int Nint = (int)Ndiv;
    const int Nfrac = (int)((Ndiv-Nint)*(1 << 20));
    self->regs->reg_0x0087_frac_sdm_cgen = (Nfrac) & 0xffff; //lower 16 bits
    self->regs->reg_0x0088_frac_sdm_cgen = (Nfrac) >> 16; //upper 4 bits
    self->regs->reg_0x0088_int_sdm_cgen = Nint-1;
    LMS7_logf(LMS7_DEBUG, "fdiv = %d, Ndiv = %f, Nint = %d, Nfrac = %d, fvco = %f MHz", fdiv, Ndiv, Nint, Nfrac, fvco/1e6);
    LMS7002M_regs_spi_write(self, 0x0087);
    LMS7002M_regs_spi_write(self, 0x0088);

    //program the feedback divider
    self->regs->reg_0x0089_sel_sdmclk_cgen = REG_0X0089_SEL_SDMCLK_CGEN_CLK_DIV;
    self->regs->reg_0x0089_div_outch_cgen = (fdiv/2)-1;
    LMS7002M_regs_spi_write(self, 0x0089);

    //select the correct CSW for this VCO frequency
    int csw_lowest = -1;
    self->regs->reg_0x008b_csw_vco_cgen = 0;
    for (int i = 7; i >= 0; i--)
    {
        self->regs->reg_0x008b_csw_vco_cgen |= 1 << i;
        LMS7002M_regs_spi_write(self, 0x008B);
        LMS7_sleep_for(cgen_cmp_sleep_ticks());
        LMS7002M_regs_spi_read(self, 0x008C);

        LMS7_logf(LMS7_DEBUG, "i=%d, hi=%d, lo=%d", i, self->regs->reg_0x008c_vco_cmpho_cgen, self->regs->reg_0x008c_vco_cmplo_cgen);
        if (self->regs->reg_0x008c_vco_cmplo_cgen != 0)
        {
            self->regs->reg_0x008b_csw_vco_cgen &= ~(1 << i); //clear bit i
        }
        if (self->regs->reg_0x008c_vco_cmpho_cgen != 0 && self->regs->reg_0x008c_vco_cmplo_cgen == 0 && csw_lowest < 0)
        {
            csw_lowest = self->regs->reg_0x008b_csw_vco_cgen;
        }
        LMS7002M_regs_spi_write(self, 0x008B);
    }

    //find the midpoint for the high and low bounds
    if (csw_lowest >= 0)
    {
        int csw_highest = self->regs->reg_0x008b_csw_vco_cgen;
        if (csw_lowest == csw_highest)
        {
            while (csw_lowest >= 0)
            {
                self->regs->reg_0x008b_csw_vco_cgen = csw_lowest;
                LMS7002M_regs_spi_write(self, 0x008B);
                LMS7_sleep_for(cgen_cmp_sleep_ticks());
                LMS7002M_regs_spi_read(self, 0x008C);

                if (self->regs->reg_0x008c_vco_cmpho_cgen == 0 && self->regs->reg_0x008c_vco_cmplo_cgen == 0) break;
                else csw_lowest--;
            }
            if (csw_lowest < 0) csw_lowest = 0;
        }
        csw_lowest += 1;

        LMS7_logf(LMS7_INFO, "lowest CSW_VCO %i, highest CSW_VCO %i", csw_lowest, csw_highest);
        self->regs->reg_0x008b_csw_vco_cgen = (csw_highest+csw_lowest)/2;
        LMS7002M_regs_spi_write(self, 0x008B);
    }

    //check that the vco selection was successful
    LMS7_sleep_for(cgen_cmp_sleep_ticks());
    LMS7002M_regs_spi_read(self, 0x008C);
    if (self->regs->reg_0x008c_vco_cmpho_cgen != 0 && self->regs->reg_0x008c_vco_cmplo_cgen == 0)
    {
        LMS7_log(LMS7_INFO, "CGEN VCO OK");
    }
    else
    {
        LMS7_log(LMS7_ERROR, "CGEN VCO select FAIL");
        return -3;
    }

    self->regs->reg_0x0086_spdup_vco_cgen = 0; //done with fast settling
    LMS7002M_regs_spi_write(self, 0x0086);

    //calculate the actual rate
    if (factual != NULL) *factual = fref * (Nint + (Nfrac/((double)(1 << 20)))) / fdiv;

    return 0; //OK
}
示例#4
0
int LMS7002M_set_lo_freq(LMS7002M_t *self, const LMS7002M_dir_t direction, const double fref, const double fout, double *factual)
{
    LMS7_logf(LMS7_INFO, "SXX tune %f MHz begin", fout/1e6);

    LMS7002M_set_mac_dir(self, direction);

    //The equations:
    // fref * N = fvco
    // fout = Fvco / divRatio_LOCH / 2
    int DIV_LOCH_SX = -1;
    int divRatio_LOCH = 0;
    double Ndiv = 0;
    double fvco = 0;
    int fdiv = 0;

    //calculation loop to find dividers that are possible
    while (true)
    {
        //try the next divider power
        DIV_LOCH_SX++;
        divRatio_LOCH = 1 << DIV_LOCH_SX;
        fdiv = divRatio_LOCH*2;

        Ndiv = fout*fdiv/fref;
        fvco = fout*fdiv;
        LMS7_logf(LMS7_DEBUG, "fdiv = %d, Ndiv = %f, fvco = %f MHz", fdiv, Ndiv, fvco/1e6);

        //check dividers and vco in range...
        if (fdiv > 128) return -1;
        if (Ndiv < 4) continue;
        if (Ndiv > 512) return -1;

        //check vco boundaries
        if (fvco < LMS7002M_SXX_VCOL_LO) continue;
        if (fvco > LMS7002M_SXX_VCOH_HI) continue;

        break; //its good
    }
    LMS7_logf(LMS7_DEBUG, "fdiv = %d, Ndiv = %f, fvco = %f MHz", fdiv, Ndiv, fvco/1e6);

    //select the VCO and handle overlap -- pick the VCO that we are more within range of
    const bool inVCOH = (fvco <= LMS7002M_SXX_VCOH_HI && fvco >= LMS7002M_SXX_VCOH_LO);
    const bool inVCOM = (fvco <= LMS7002M_SXX_VCOM_HI && fvco >= LMS7002M_SXX_VCOM_LO);
    const bool inVCOL = (fvco <= LMS7002M_SXX_VCOL_HI && fvco >= LMS7002M_SXX_VCOL_LO);
    const bool prefVCOH = ((fvco - LMS7002M_SXX_VCOH_LO) > (LMS7002M_SXX_VCOM_HI - fvco));
    const bool prefVCOM = ((fvco - LMS7002M_SXX_VCOM_LO) > (LMS7002M_SXX_VCOL_HI - fvco));

    int SEL_VCO = 0;
    if      (inVCOH && !inVCOM)               SEL_VCO = REG_0X0121_SEL_VCO_VCOH;
    else if (inVCOH && inVCOM && prefVCOH)    SEL_VCO = REG_0X0121_SEL_VCO_VCOH;
    else if (inVCOH && inVCOM && !prefVCOH)   SEL_VCO = REG_0X0121_SEL_VCO_VCOM;
    else if (inVCOM && !inVCOL)               SEL_VCO = REG_0X0121_SEL_VCO_VCOM;
    else if (inVCOM && inVCOL && prefVCOM)    SEL_VCO = REG_0X0121_SEL_VCO_VCOM;
    else if (inVCOM && inVCOL && !prefVCOM)   SEL_VCO = REG_0X0121_SEL_VCO_VCOL;
    else if (inVCOL)                          SEL_VCO = REG_0X0121_SEL_VCO_VCOL;
    else
    {
        LMS7_logf(LMS7_ERROR, "SXX no available VCO for %f MHz", fvco/1e6);
        return -2;
    }

    //deal with VCO divider
    const int EN_DIV2 = (fvco > 5.5e9)?1:0;

    //compensate for the lack of doubling when disabled
    if (EN_DIV2 != 0) Ndiv /= 2;

    //after a successful tune, stash the frequency
    if (direction == LMS_RX) self->sxr_freq = fout;
    if (direction == LMS_TX) self->sxt_freq = fout;
    if (direction == LMS_RX) self->sxr_fref = fref;
    if (direction == LMS_TX) self->sxt_fref = fref;

    //reset
    self->regs->reg_0x011c_reset_n = 0;
    LMS7002M_regs_spi_write(self, 0x011c);
    self->regs->reg_0x011c_reset_n = 1;
    LMS7002M_regs_spi_write(self, 0x011c);

    //configure and enable synthesizer
    self->regs->reg_0x011c_en_intonly_sdm = 0; //support frac-N
    self->regs->reg_0x011c_en_sdm_clk = 1; //enable
    self->regs->reg_0x011c_pd_cp = 0; //enable
    self->regs->reg_0x011c_pd_fbdiv = 0; //enable
    self->regs->reg_0x011c_pd_fdiv = 0; //enable
    self->regs->reg_0x011c_pd_sdm = 0; //enable
    self->regs->reg_0x011c_pd_vco = 0; //enable
    self->regs->reg_0x011c_pd_vco_comp = 0; //enable
    self->regs->reg_0x011c_en_g = 1;
    self->regs->reg_0x011c_en_coarsepll = 0;
    self->regs->reg_0x0121_coarse_start = 0;
    self->regs->reg_0x011c_en_div2_divprog = EN_DIV2;
    self->regs->reg_0x011c_spdup_vco = 1; //fast settling
    LMS7002M_regs_spi_write(self, 0x011c);

    //program the N divider
    const int Nint = (int)Ndiv - 4;
    const int Nfrac = (int)((Ndiv-((int)(Ndiv)))*(1 << 20));
    self->regs->reg_0x011d_frac_sdm = (Nfrac) & 0xffff; //lower 16 bits
    self->regs->reg_0x011e_frac_sdm = (Nfrac) >> 16; //upper 4 bits
    self->regs->reg_0x011e_int_sdm = Nint;
    LMS7_logf(LMS7_DEBUG, "fdiv = %d, Ndiv = %f, Nint = %d, Nfrac = %d, DIV_LOCH_SX = %d, SEL_VCO = %d, fvco = %f MHz", fdiv, Ndiv, Nint, Nfrac, DIV_LOCH_SX, SEL_VCO, fvco/1e6);
    LMS7002M_regs_spi_write(self, 0x011d);
    LMS7002M_regs_spi_write(self, 0x011e);

    //program the feedback divider
    self->regs->reg_0x011f_sel_sdmclk = REG_0X011F_SEL_SDMCLK_CLK_DIV;
    self->regs->reg_0x011f_div_loch = DIV_LOCH_SX;
    LMS7002M_regs_spi_write(self, 0x011f);

    //select vco based on freq
    self->regs->reg_0x0121_sel_vco = SEL_VCO;
    LMS7002M_regs_spi_write(self, 0x0121);

    //select the correct CSW for this VCO frequency
    int csw_lowest = -1;
    self->regs->reg_0x0121_csw_vco = 0;
    for (int i = 7; i >= 0; i--)
    {
        self->regs->reg_0x0121_csw_vco |= 1 << i;
        LMS7002M_regs_spi_write(self, 0x0121);
        LMS7_sleep_for(sxx_cmp_sleep_ticks());
        LMS7002M_regs_spi_read(self, 0x0123);

        LMS7_logf(LMS7_DEBUG, "i=%d, hi=%d, lo=%d", i, self->regs->reg_0x0123_vco_cmpho, self->regs->reg_0x0123_vco_cmplo);
        if (self->regs->reg_0x0123_vco_cmplo != 0)
        {
            self->regs->reg_0x0121_csw_vco &= ~(1 << i); //clear bit i
        }
        if (self->regs->reg_0x0123_vco_cmpho != 0 && self->regs->reg_0x0123_vco_cmplo == 0 && csw_lowest < 0)
        {
            csw_lowest = self->regs->reg_0x0121_csw_vco;
        }
        LMS7002M_regs_spi_write(self, 0x0121);
    }

    //find the midpoint for the high and low bounds
    if (csw_lowest >= 0)
    {
        int csw_highest = self->regs->reg_0x0121_csw_vco;
        if (csw_lowest == csw_highest)
        {
            while (csw_lowest >= 0)
            {
                self->regs->reg_0x0121_csw_vco = csw_lowest;
                LMS7002M_regs_spi_write(self, 0x0121);
                LMS7_sleep_for(sxx_cmp_sleep_ticks());
                LMS7002M_regs_spi_read(self, 0x0123);

                if (self->regs->reg_0x0123_vco_cmpho == 0 && self->regs->reg_0x0123_vco_cmplo == 0) break;
                else csw_lowest--;
            }
            if (csw_lowest < 0) csw_lowest = 0;
        }
        csw_lowest += 1;

        LMS7_logf(LMS7_INFO, "lowest CSW_VCO %i, highest CSW_VCO %i", csw_lowest, csw_highest);
        self->regs->reg_0x0121_csw_vco = (csw_highest+csw_lowest)/2;
        LMS7002M_regs_spi_write(self, 0x0121);
    }

    //check that the vco selection was successful
    LMS7_sleep_for(sxx_cmp_sleep_ticks());
    LMS7002M_regs_spi_read(self, 0x0123);
    if (self->regs->reg_0x0123_vco_cmpho != 0 && self->regs->reg_0x0123_vco_cmplo == 0)
    {
        LMS7_log(LMS7_INFO, "SXX VCO OK");
    }
    else
    {
        LMS7_log(LMS7_ERROR, "SXX VCO select FAIL");
        return -3;
    }

    self->regs->reg_0x011c_spdup_vco = 0; //done with fast settling
    LMS7002M_regs_spi_write(self, 0x011c);

    //calculate the actual rate
    if (factual != NULL) *factual = (1 << EN_DIV2) * fref * ((Nint+4) + (Nfrac/((double)(1 << 20)))) / fdiv;

    return 0; //OK
}