void LMS7002M_tbb_set_path(LMS7002M_t *self, const LMS7002M_chan_t channel, const int path) { LMS7002M_set_mac_ch(self, channel); self->regs->reg_0x0105_pd_lpfh_tbb = 1; self->regs->reg_0x0105_pd_lpflad_tbb = 1; self->regs->reg_0x0105_pd_lpfs5_tbb = 1; self->regs->reg_0x010a_bypladder_tbb = 1; switch (path) { case LMS7002M_TBB_BYP: break; case LMS7002M_TBB_LBF: self->regs->reg_0x010a_bypladder_tbb = 0; self->regs->reg_0x0105_pd_lpflad_tbb = 0; self->regs->reg_0x0105_pd_lpfs5_tbb = 0; break; case LMS7002M_TBB_HBF: self->regs->reg_0x0105_pd_lpfh_tbb = 0; break; } LMS7002M_regs_spi_write(self, 0x0105); LMS7002M_regs_spi_write(self, 0x010A); }
LMS7002M_API void LMS7002M_set_diq_mux(LMS7002M_t *self, const LMS7002M_dir_t direction, const int positions[4]) { //set the same config on both ports, only one is used as per port config if (direction == LMS_TX) { self->regs.reg_0x0024_lml1_bqp = __lms7002m_diq_index(LMS7002M_LML_BQ, positions); self->regs.reg_0x0024_lml1_bip = __lms7002m_diq_index(LMS7002M_LML_BI, positions); self->regs.reg_0x0024_lml1_aqp = __lms7002m_diq_index(LMS7002M_LML_AQ, positions); self->regs.reg_0x0024_lml1_aip = __lms7002m_diq_index(LMS7002M_LML_AI, positions); self->regs.reg_0x0027_lml2_bqp = __lms7002m_diq_index(LMS7002M_LML_BQ, positions); self->regs.reg_0x0027_lml2_bip = __lms7002m_diq_index(LMS7002M_LML_BI, positions); self->regs.reg_0x0027_lml2_aqp = __lms7002m_diq_index(LMS7002M_LML_AQ, positions); self->regs.reg_0x0027_lml2_aip = __lms7002m_diq_index(LMS7002M_LML_AI, positions); } if (direction == LMS_RX) { self->regs.reg_0x0024_lml1_s3s = positions[3]; self->regs.reg_0x0024_lml1_s2s = positions[2]; self->regs.reg_0x0024_lml1_s1s = positions[1]; self->regs.reg_0x0024_lml1_s0s = positions[0]; self->regs.reg_0x0027_lml2_s3s = positions[3]; self->regs.reg_0x0027_lml2_s2s = positions[2]; self->regs.reg_0x0027_lml2_s1s = positions[1]; self->regs.reg_0x0027_lml2_s0s = positions[0]; } LMS7002M_regs_spi_write(self, 0x0024); LMS7002M_regs_spi_write(self, 0x0027); }
void LMS7002M_rxtsp_enable(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable) { LMS7002M_set_mac_ch(self, channel); self->regs->reg_0x0400_en = enable?1:0; self->regs->reg_0x0400_bstart = 0; self->regs->reg_0x0400_insel = REG_0X0400_INSEL_LML; //r19 regs - probably means baseband input LMS7002M_regs_spi_write(self, 0x0400); self->regs->reg_0x0403_hbd_ovr = REG_0X0403_HBD_OVR_BYPASS; self->regs->reg_0x040a_agc_mode = REG_0X040A_AGC_MODE_BYPASS; self->regs->reg_0x040c_cmix_byp = 1; self->regs->reg_0x040c_agc_byp = 1; self->regs->reg_0x040c_gfir3_byp = 1; self->regs->reg_0x040c_gfir2_byp = 1; self->regs->reg_0x040c_gfir1_byp = 1; self->regs->reg_0x040c_dc_byp = 1; self->regs->reg_0x040c_gc_byp = 1; self->regs->reg_0x040c_ph_byp = 1; LMS7002M_regs_spi_write(self, 0x0400); LMS7002M_regs_spi_write(self, 0x0403); LMS7002M_regs_spi_write(self, 0x040a); LMS7002M_regs_spi_write(self, 0x040c); }
double LMS7002M_rxtsp_read_rssi(LMS7002M_t *self, const LMS7002M_chan_t channel) { LMS7002M_set_mac_ch(self, channel); self->regs->reg_0x040c_agc_byp = 0; LMS7002M_regs_spi_write(self, 0x040c); self->regs->reg_0x040a_agc_mode = REG_0X040A_AGC_MODE_RSSI; LMS7002M_regs_spi_write(self, 0x040a); self->regs->reg_0x0400_capsel = REG_0X0400_CAPSEL_RSSI; self->regs->reg_0x0400_capture = 0; LMS7002M_regs_spi_write(self, 0x0400); //trigger capture to capd readback regs self->regs->reg_0x0400_capture = 1; LMS7002M_regs_spi_write(self, 0x0400); const int rssi_lo = LMS7002M_spi_read(self, 0x040E); const int rssi_hi = LMS7002M_spi_read(self, 0x040F); const int rssi_int = (rssi_hi << 2) | rssi_lo; //a full scale DC input level be 1.0 RSSI return rssi_int/((double)(1 << 16)); }
void LMS7002M_sxx_enable(LMS7002M_t *self, const LMS7002M_dir_t direction, const bool enable) { LMS7002M_set_mac_dir(self, direction); self->regs->reg_0x0124_en_dir_sxx = 1; LMS7002M_regs_spi_write(self, 0x0124); self->regs->reg_0x011c_en_g = enable?1:0; LMS7002M_regs_spi_write(self, 0x011c); }
LMS7002M_API void LMS7002M_txtsp_tsg_const(LMS7002M_t *self, const LMS7002M_chan_t channel, const int valI, const int valQ) { LMS7002M_set_mac_ch(self, channel); //muxes self->regs.reg_0x0200_tsgfc = REG_0X0200_TSGFC_FS; self->regs.reg_0x0200_tsgmode = REG_0X0200_TSGMODE_DC; self->regs.reg_0x0200_insel = REG_0X0200_INSEL_TEST; LMS7002M_regs_spi_write(self, 0x0200); //load I self->regs.reg_0x020c_dc_reg = valI; LMS7002M_regs_spi_write(self, 0x020c); self->regs.reg_0x0200_tsgdcldi = 0; LMS7002M_regs_spi_write(self, 0x0200); self->regs.reg_0x0200_tsgdcldi = 1; LMS7002M_regs_spi_write(self, 0x0200); self->regs.reg_0x0200_tsgdcldi = 0; LMS7002M_regs_spi_write(self, 0x0200); //load Q self->regs.reg_0x020c_dc_reg = valQ; LMS7002M_regs_spi_write(self, 0x020c); self->regs.reg_0x0200_tsgdcldq = 0; LMS7002M_regs_spi_write(self, 0x0200); self->regs.reg_0x0200_tsgdcldq = 1; LMS7002M_regs_spi_write(self, 0x0200); self->regs.reg_0x0200_tsgdcldq = 0; LMS7002M_regs_spi_write(self, 0x0200); }
LMS7002M_API void LMS7002M_trf_enable(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable) { LMS7002M_set_mac_ch(self, channel); self->regs.reg_0x0124_en_dir_trf = 1; LMS7002M_regs_spi_write(self, 0x0124); self->regs.reg_0x0100_en_g_trf = enable?1:0; self->regs.reg_0x0100_pd_tlobuf_trf = 0; self->regs.reg_0x0100_pd_txpad_trf = 0; LMS7002M_regs_spi_write(self, 0x0100); }
LMS7002M_API void LMS7002M_txtsp_tsg_tone(LMS7002M_t *self, const LMS7002M_chan_t channel) { LMS7002M_set_mac_ch(self, channel); //muxes self->regs.reg_0x0200_tsgmode = REG_0X0200_TSGMODE_NCO; self->regs.reg_0x0200_insel = REG_0X0200_INSEL_TEST; LMS7002M_regs_spi_write(self, 0x0200); self->regs.reg_0x0200_tsgfcw = REG_0X0200_TSGFCW_DIV8; LMS7002M_regs_spi_write(self, 0x0200); }
void LMS7002M_tbb_enable(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable) { LMS7002M_set_mac_ch(self, channel); self->regs->reg_0x0124_en_dir_tbb = 1; LMS7002M_regs_spi_write(self, 0x0124); self->regs->reg_0x0105_en_g_tbb = enable?1:0; LMS7002M_tbb_set_test_in(self, channel, LMS7002M_TBB_TSTIN_OFF); LMS7002M_tbb_enable_loopback(self, channel, LMS7002M_TBB_LB_DISCONNECTED, false); LMS7002M_regs_spi_write(self, 0x0105); }
void LMS7002M_rxtsp_set_dc_correction( LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enabled, const int window) { LMS7002M_set_mac_ch(self, channel); self->regs->reg_0x040c_dc_byp = (enabled)?0:1; LMS7002M_regs_spi_write(self, 0x040c); self->regs->reg_0x0404_dccorr_avg = window; LMS7002M_regs_spi_write(self, 0x0404); }
LMS7002M_API void LMS7002M_txtsp_set_freq(LMS7002M_t *self, const LMS7002M_chan_t channel, const double freqRel) { LMS7002M_set_mac_ch(self, channel); self->regs.reg_0x0208_cmix_byp = 0; LMS7002M_regs_spi_write(self, 0x0208); LMS7002M_set_nco_freq(self, LMS_TX, channel, freqRel); }
LMS7002M_API void LMS7002M_trf_select_band(LMS7002M_t *self, const LMS7002M_chan_t channel, const int band) { LMS7002M_set_mac_ch(self, channel); self->regs.reg_0x0103_sel_band1_trf = (band == 1)?1:0; self->regs.reg_0x0103_sel_band2_trf = (band == 2)?1:0; LMS7002M_regs_spi_write(self, 0x0103); }
double LMS7002M_tbb_set_filter_bw(LMS7002M_t *self, const LMS7002M_chan_t channel, const double bw) { LMS7002M_set_mac_ch(self, channel); int val = 0; double actual = bw; bool bypass = false; const bool hb = bw >= 18.5e6; if (bw <= 2.4e6) val = 6, actual = 2.4e6; else if (bw <= 2.74e6) val = 19, actual = 2.74e6; else if (bw <= 5.5e6) val = 75, actual = 5.5e6; else if (bw <= 8.2e6) val = 133, actual = 8.2e6; else if (bw <= 11.0e6) val = 193, actual = 11.0e6; else if (bw <= 18.5e6) val = 18, actual = 18.5e6; else if (bw <= 38.0e6) val = 97, actual = 38.0e6; else if (bw <= 54.0e6) val = 163, actual = 54.0e6; else bypass = true; //only one filter is actually used self->regs->reg_0x0109_rcal_lpfh_tbb = val; self->regs->reg_0x0109_rcal_lpflad_tbb = val; LMS7002M_regs_spi_write(self, 0x0109); //set path if (bypass) LMS7002M_tbb_set_path(self, channel, LMS7002M_TBB_BYP); else if (hb) LMS7002M_tbb_set_path(self, channel, LMS7002M_TBB_HBF); else LMS7002M_tbb_set_path(self, channel, LMS7002M_TBB_LBF); return actual; }
LMS7002M_API void LMS7002M_power_down(LMS7002M_t *self) { self->regs.reg_0x0020_rxen_a = 0; self->regs.reg_0x0020_rxen_b = 0; self->regs.reg_0x0020_txen_a = 0; self->regs.reg_0x0020_txen_b = 0; LMS7002M_regs_spi_write(self, 0x0020); }
void LMS7002M_tbb_enable_loopback(LMS7002M_t *self, const LMS7002M_chan_t channel, const int mode, const bool swap) { LMS7002M_set_mac_ch(self, channel); self->regs->reg_0x0105_loopb_tbb = mode; self->regs->reg_0x0105_loopb_tbb |= swap?0:(1 << 2); LMS7002M_regs_spi_write(self, 0x0105); }
LMS7002M_API void LMS7002M_set_mac_ch(LMS7002M_t *self, const LMS7002M_chan_t channel) { int newValue = REG_0X0020_MAC_CHAB; if (channel == LMS_CHA) newValue = REG_0X0020_MAC_CHA; if (channel == LMS_CHB) newValue = REG_0X0020_MAC_CHB; if (self->regs.reg_0x0020_mac == newValue) return; self->regs.reg_0x0020_mac = newValue; LMS7002M_regs_spi_write(self, 0x0020); }
LMS7002M_API void LMS7002M_set_mac_dir(LMS7002M_t *self, const LMS7002M_dir_t direction) { //SPI Regs r19 p10: //The special case is frequency synthesizers SXR and SXT. Register addresses are the //same for SXR and SXT. To control SXT we have to set MAC[1:0] to the "01" and MAC[1:0] //to the "10" for SXR. const int newValue = (direction == LMS_TX)?1:2; if (self->regs.reg_0x0020_mac == newValue) return; self->regs.reg_0x0020_mac = newValue; LMS7002M_regs_spi_write(self, 0x0020); }
void LMS7002M_ldo_enable(LMS7002M_t *self, const bool enable, const int group) { //LDO is a global register space LMS7002M_set_mac_ch(self, LMS_CHAB); //TODO, we can implement more groups, for now only 1 if (group != LMS7002M_LDO_ALL) return; const int val = enable?1:0; self->regs->reg_0x0092_en_ldo_dig= val; self->regs->reg_0x0092_en_ldo_diggn= val; self->regs->reg_0x0092_en_ldo_digsxr= val; self->regs->reg_0x0092_en_ldo_digsxt= val; self->regs->reg_0x0092_en_ldo_divgn= val; self->regs->reg_0x0092_en_ldo_divsxr= val; self->regs->reg_0x0092_en_ldo_divsxt= val; self->regs->reg_0x0092_en_ldo_lna12= val; self->regs->reg_0x0092_en_ldo_lna14= val; self->regs->reg_0x0092_en_ldo_mxrfe= val; self->regs->reg_0x0092_en_ldo_rbb= val; self->regs->reg_0x0092_en_ldo_rxbuf= val; self->regs->reg_0x0092_en_ldo_tbb= val; self->regs->reg_0x0092_en_ldo_tia12= val; self->regs->reg_0x0092_en_ldo_tia14= val; self->regs->reg_0x0092_en_g_ldo= val; self->regs->reg_0x0093_en_ldo_afe= val; self->regs->reg_0x0093_en_ldo_cpgn= val; self->regs->reg_0x0093_en_ldo_cpsxr= val; self->regs->reg_0x0093_en_ldo_tlob= val; self->regs->reg_0x0093_en_ldo_tpad= val; self->regs->reg_0x0093_en_ldo_txbuf= val; self->regs->reg_0x0093_en_ldo_vcogn= val; self->regs->reg_0x0093_en_ldo_vcosxr= val; self->regs->reg_0x0093_en_ldo_vcosxt= val; self->regs->reg_0x0093_en_ldo_cpsxt= val; LMS7002M_regs_spi_write(self, 0x0092); LMS7002M_regs_spi_write(self, 0x0093); }
void LMS7002M_rxtsp_set_decim(LMS7002M_t *self, const LMS7002M_chan_t channel, const size_t decim) { LMS7002M_set_mac_ch(self, channel); if (decim == 1) self->regs->reg_0x0403_hbd_ovr = REG_0X0403_HBD_OVR_BYPASS; if (decim == 2) self->regs->reg_0x0403_hbd_ovr = 0; if (decim == 4) self->regs->reg_0x0403_hbd_ovr = 1; if (decim == 8) self->regs->reg_0x0403_hbd_ovr = 2; if (decim == 16) self->regs->reg_0x0403_hbd_ovr = 3; if (decim == 32) self->regs->reg_0x0403_hbd_ovr = 4; LMS7002M_regs_spi_write(self, 0x0403); }
LMS7002M_API void LMS7002M_txtsp_set_interp(LMS7002M_t *self, const LMS7002M_chan_t channel, const size_t interp) { LMS7002M_set_mac_ch(self, channel); if (interp == 1) self->regs.reg_0x0203_hbi_ovr = REG_0X0203_HBI_OVR_BYPASS; if (interp == 2) self->regs.reg_0x0203_hbi_ovr = 0; if (interp == 4) self->regs.reg_0x0203_hbi_ovr = 1; if (interp == 8) self->regs.reg_0x0203_hbi_ovr = 2; if (interp == 16) self->regs.reg_0x0203_hbi_ovr = 3; if (interp == 32) self->regs.reg_0x0203_hbi_ovr = 4; LMS7002M_regs_spi_write(self, 0x0203); }
LMS7002M_API void LMS7002M_setup_digital_loopback(LMS7002M_t *self) { self->regs.reg_0x002a_rx_mux = REG_0X002A_RX_MUX_TXFIFO; //self->regs.reg_0x002a_rx_mux = REG_0X002A_RX_MUX_LFSR; if (self->regs.reg_0x002a_txwrclk_mux == REG_0X002A_TXWRCLK_MUX_FCLK1) { self->regs.reg_0x002a_rxwrclk_mux = REG_0X002A_RXWRCLK_MUX_FCLK1; } if (self->regs.reg_0x002a_txwrclk_mux == REG_0X002A_TXWRCLK_MUX_FCLK2) { self->regs.reg_0x002a_rxwrclk_mux = REG_0X002A_RXWRCLK_MUX_FCLK2; } LMS7002M_regs_spi_write(self, 0x002A); }
LMS7002M_API void LMS7002M_txtsp_enable(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable) { LMS7002M_set_mac_ch(self, channel); self->regs.reg_0x0200_en = enable?1:0; self->regs.reg_0x0200_bstart = 0; self->regs.reg_0x0200_insel = REG_0X0200_INSEL_LML; LMS7002M_regs_spi_write(self, 0x0200); self->regs.reg_0x0203_hbi_ovr = REG_0X0203_HBI_OVR_BYPASS; self->regs.reg_0x0208_cmix_byp = 1; self->regs.reg_0x0208_isinc_byp = 1; self->regs.reg_0x0208_gfir3_byp = 1; self->regs.reg_0x0208_gfir2_byp = 1; self->regs.reg_0x0208_gfir1_byp = 1; self->regs.reg_0x0208_dc_byp = 1; self->regs.reg_0x0208_gc_byp = 1; self->regs.reg_0x0208_ph_byp = 1; LMS7002M_regs_spi_write(self, 0x0200); LMS7002M_regs_spi_write(self, 0x0203); LMS7002M_regs_spi_write(self, 0x0208); }
double LMS7002M_tbb_set_iamp(LMS7002M_t *self, const LMS7002M_chan_t channel, const double gain) { LMS7002M_set_mac_ch(self, channel); int value = (int)(gain + 0.5); if (value < 0) value = 0; if (value > 63) value = 63; //ensure enabled iamp self->regs->reg_0x0105_pd_lpfiamp_tbb = 0; LMS7002M_regs_spi_write(self, 0x0105); //TODO this may need calibration self->regs->reg_0x0108_cg_iamp_tbb = value; return gain; }
void LMS7002M_rxtsp_set_iq_correction( LMS7002M_t *self, const LMS7002M_chan_t channel, const double phase, const double gain) { LMS7002M_set_mac_ch(self, channel); const bool bypassPhase = (phase == 0.0); const bool bypassGain = (gain == 0.0); self->regs->reg_0x040c_ph_byp = bypassPhase?1:0; self->regs->reg_0x040c_gc_byp = bypassGain?1:0; LMS7002M_regs_spi_write(self, 0x040c); //TODO //self->regs->reg_0x0403_iqcorr = //self->regs->reg_0x0402_gcorri = //self->regs->reg_0x0401_gcorrq = }
void LMS7002M_tbb_set_test_in(LMS7002M_t *self, const LMS7002M_chan_t channel, const int path) { LMS7002M_set_mac_ch(self, channel); self->regs->reg_0x010a_tstin_tbb = path; LMS7002M_regs_spi_write(self, 0x010a); }
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 }
LMS7002M_API void LMS7002M_set_spi_mode(LMS7002M_t *self, const int numWires) { if (numWires == 3) self->regs.reg_0x0021_spimode = REG_0X0021_SPIMODE_3WIRE; if (numWires == 4) self->regs.reg_0x0021_spimode = REG_0X0021_SPIMODE_4WIRE; LMS7002M_regs_spi_write(self, 0x0021); }
LMS7002M_API void LMS7002M_reset(LMS7002M_t *self) { LMS7002M_spi_write(self, 0x0020, 0x0); LMS7002M_regs_spi_write(self, 0x0020); LMS7002M_regs_spi_write(self, 0x002E);//must write }
int LMS7002M_set_gfir_taps( LMS7002M_t *self, const LMS7002M_dir_t direction, const LMS7002M_chan_t channel, const int which, const short *taps, const size_t ntaps) { LMS7002M_set_mac_ch(self, channel); //bypass the filter for null/empty filter taps const bool bypass = (taps == NULL) || (ntaps == 0); if (direction == LMS_RX) { if (which == 1) self->regs->reg_0x040c_gfir1_byp = bypass?1:0; if (which == 2) self->regs->reg_0x040c_gfir2_byp = bypass?1:0; if (which == 3) self->regs->reg_0x040c_gfir3_byp = bypass?1:0; LMS7002M_regs_spi_write(self, 0x040c); } if (direction == LMS_TX) { if (which == 1) self->regs->reg_0x0208_gfir1_byp = bypass?1:0; if (which == 2) self->regs->reg_0x0208_gfir2_byp = bypass?1:0; if (which == 3) self->regs->reg_0x0208_gfir3_byp = bypass?1:0; LMS7002M_regs_spi_write(self, 0x0208); } //bypass programed, skip writing taps if (bypass) return 0; //bounds check if (which < 1) return -1; if (which > 3) return -1; if (which == 1 && ntaps != 5*8) return -2; if (which == 2 && ntaps != 5*8) return -2; if (which == 3 && ntaps != 3*5*8) return -2; //taps configuration logic from LMS7002_MainControl::LoadGFIRCoefficients if (which == 1) { int addr = (direction == LMS_RX)?0x0480:0x0280; for (int k=0; k<5; ++k) { for (int i=0; i<8; ++i) { LMS7002M_spi_write(self, addr, taps[k*8+i]); ++addr; } } } else if (which == 2) { int addr = (direction == LMS_RX)?0x04c0:0x02c0; for (int k=0; k<5; ++k) { for (int i=0; i<8; ++i) { LMS7002M_spi_write(self, addr, taps[k*8+i]); ++addr; } } } else if (which == 3) { int addr = (direction == LMS_RX)?0x0500:0x0300; int coefIndex = 0; unsigned short coefValue = 0; for (int n=0; n<3; ++n) { for (int k=0; k<5; ++k) { for (int i=0; i<8; ++i) { coefValue = taps[coefIndex]; ++coefIndex; LMS7002M_spi_write(self, addr, coefValue); ++addr; } } addr += 24; //skip reserved } } return 0; //OK }
LMS7002M_API void LMS7002M_configure_lml_port(LMS7002M_t *self, const LMS7002M_port_t portNo, const LMS7002M_dir_t direction, const int mclkDiv) { //set TRXIQ on both ports if (portNo == LMS_PORT1) { self->regs.reg_0x0023_lml_mode1 = REG_0X0023_LML_MODE1_TRXIQ; self->regs.reg_0x0023_lml_txnrxiq1 = (direction==LMS_TX)? REG_0X0023_LML_TXNRXIQ1_RXIQ:REG_0X0023_LML_TXNRXIQ1_TXIQ; //WARNING: TX/RX perspective swap } if (portNo == LMS_PORT2) { self->regs.reg_0x0023_lml_mode2 = REG_0X0023_LML_MODE2_TRXIQ; self->regs.reg_0x0023_lml_txnrxiq2 = (direction==LMS_TX)? REG_0X0023_LML_TXNRXIQ2_RXIQ:REG_0X0023_LML_TXNRXIQ2_TXIQ; //WARNING: TX/RX perspective swap } //automatic directions based on mode above self->regs.reg_0x0023_enabledirctr1 = 0; self->regs.reg_0x0023_enabledirctr2 = 0; //delayed mclk is used for RX read clock -- needs small delay set self->regs.reg_0x002b_mclk2dly = 1; //1X delay self->regs.reg_0x002b_mclk1dly = 1; //1X delay //set the FIFO rd and wr clock muxes based on direction if (direction == LMS_TX) { self->regs.reg_0x002a_txrdclk_mux = REG_0X002A_TXRDCLK_MUX_TXTSPCLK; self->regs.reg_0x002a_txwrclk_mux = (portNo==LMS_PORT1)?REG_0X002A_TXWRCLK_MUX_FCLK1:REG_0X002A_TXWRCLK_MUX_FCLK2; } if (direction == LMS_RX) { self->regs.reg_0x002a_rxrdclk_mux = (portNo==LMS_PORT1)?REG_0X002A_RXRDCLK_MUX_MCLK1:REG_0X002A_RXRDCLK_MUX_MCLK2; self->regs.reg_0x002a_rxwrclk_mux = REG_0X002A_RXWRCLK_MUX_RXTSPCLK; } //data stream muxes if (direction == LMS_TX) { self->regs.reg_0x002a_tx_mux = (portNo==LMS_PORT1)?REG_0X002A_TX_MUX_PORT1:REG_0X002A_TX_MUX_PORT2; } if (direction == LMS_RX) { self->regs.reg_0x002a_rx_mux = REG_0X002A_RX_MUX_RXTSP; } //clock mux (outputs to mclk pin) if (portNo == LMS_PORT1) { self->regs.reg_0x002b_mclk1src = (direction==LMS_TX)? ((mclkDiv==1)?REG_0X002B_MCLK1SRC_TXTSPCLKA:REG_0X002B_MCLK1SRC_TXTSPCLKA_DIV): ((mclkDiv==1)?REG_0X002B_MCLK1SRC_RXTSPCLKA:REG_0X002B_MCLK1SRC_RXTSPCLKA_DIV); } if (portNo == LMS_PORT2) { self->regs.reg_0x002b_mclk2src = (direction==LMS_TX)? ((mclkDiv==1)?REG_0X002B_MCLK2SRC_TXTSPCLKA:REG_0X002B_MCLK2SRC_TXTSPCLKA_DIV): ((mclkDiv==1)?REG_0X002B_MCLK2SRC_RXTSPCLKA:REG_0X002B_MCLK2SRC_RXTSPCLKA_DIV); } //clock divider (outputs to mclk pin) if (direction == LMS_TX) { self->regs.reg_0x002b_txdiven = (mclkDiv > 1)?1:0; self->regs.reg_0x002c_txtspclk_div = (mclkDiv/2)-1; } if (direction == LMS_RX) { self->regs.reg_0x002b_rxdiven = (mclkDiv > 1)?1:0; self->regs.reg_0x002c_rxtspclk_div = (mclkDiv/2)-1; } LMS7002M_regs_spi_write(self, 0x0023); LMS7002M_regs_spi_write(self, 0x002A); LMS7002M_regs_spi_write(self, 0x002B); LMS7002M_regs_spi_write(self, 0x002C); }