/** @file LMS7002M.cpp @author Lime Microsystems (www.limemicro.com) @brief Implementation of LMS7002M transceiver configuring */ #include "LMS7002M.h" #include <stdio.h> #include <set> #include "lmsComms.h" #include "INI.h" #include <cmath> #include <iostream> #include <algorithm> #include "LMS7002M_RegistersMap.h" #include <chrono> #include <thread> float_type LMS7002M::gVCO_frequency_table[3][2] = { { 3800, 5222 }, { 4961, 6754 }, {6306, 7714} }; float_type LMS7002M::gCGEN_VCO_frequencies[2] = {2000, 2700}; ///define for parameter enumeration if prefix might be needed #define LMS7param(id) id //module addresses needs to be sorted in ascending order const uint16_t LMS7002M::readOnlyRegisters[] = { 0x002F, 0x008C, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x0123, 0x0209, 0x020A, 0x020B, 0x040E, 0x040F }; const uint16_t LMS7002M::readOnlyRegistersMasks[] = { 0x0000, 0x0FFF, 0x007F, 0x0000, 0x0000, 0x0000, 0x0000, 0x003F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; /** @brief Simple logging function to print status messages @param text message to print @param type message type for filtering specific information */ void LMS7002M::Log(const char* text, LogType type) { switch(type) { case LOG_INFO: printf("%s\n", text); break; case LOG_WARNING: printf("Warning: %s\n", text); break; case LOG_ERROR: printf("ERROR: %s\n", text); break; case LOG_DATA: printf("DATA: %s\n", text); break; } } LMS7002M::LMS7002M() : controlPort(NULL), mRegistersMap(new LMS7002M_RegistersMap()) { mRefClkSXR_MHz = 30.72; mRefClkSXT_MHz = 30.72; } /** @brief Creates LMS7002M main control object, it requires LMScomms to communicate with chip @param controlPort data connection for controlling LMS7002 chip registers */ LMS7002M::LMS7002M(LMScomms* controlPort) : controlPort(controlPort), mRegistersMap(new LMS7002M_RegistersMap()) { mRefClkSXR_MHz = 30.72; mRefClkSXT_MHz = 30.72; //memory intervals for registers tests and calibration algorithms MemorySectionAddresses[LimeLight][0] = 0x0020; MemorySectionAddresses[LimeLight][1] = 0x002F; MemorySectionAddresses[EN_DIR][0] = 0x0081; MemorySectionAddresses[EN_DIR][1] = 0x0081; MemorySectionAddresses[AFE][0] = 0x0082; MemorySectionAddresses[AFE][1] = 0x0082; MemorySectionAddresses[BIAS][0] = 0x0084; MemorySectionAddresses[BIAS][1] = 0x0084; MemorySectionAddresses[XBUF][0] = 0x0085; MemorySectionAddresses[XBUF][1] = 0x0085; MemorySectionAddresses[CGEN][0] = 0x0086; MemorySectionAddresses[CGEN][1] = 0x008C; MemorySectionAddresses[LDO][0] = 0x0092; MemorySectionAddresses[LDO][1] = 0x00A7; MemorySectionAddresses[BIST][0] = 0x00A8; MemorySectionAddresses[BIST][1] = 0x00AC; MemorySectionAddresses[CDS][0] = 0x00AD; MemorySectionAddresses[CDS][1] = 0x00AE; MemorySectionAddresses[TRF][0] = 0x0100; MemorySectionAddresses[TRF][1] = 0x0104; MemorySectionAddresses[TBB][0] = 0x0105; MemorySectionAddresses[TBB][1] = 0x010A; MemorySectionAddresses[RFE][0] = 0x010C; MemorySectionAddresses[RFE][1] = 0x0114; MemorySectionAddresses[RBB][0] = 0x0115; MemorySectionAddresses[RBB][1] = 0x011A; MemorySectionAddresses[SX][0] = 0x011C; MemorySectionAddresses[SX][1] = 0x0124; MemorySectionAddresses[TxTSP][0] = 0x0200; MemorySectionAddresses[TxTSP][1] = 0x020C; MemorySectionAddresses[TxNCO][0] = 0x0240; MemorySectionAddresses[TxNCO][1] = 0x0261; MemorySectionAddresses[TxGFIR1][0] = 0x0280; MemorySectionAddresses[TxGFIR1][1] = 0x02A7; MemorySectionAddresses[TxGFIR2][0] = 0x02C0; MemorySectionAddresses[TxGFIR2][1] = 0x02E7; MemorySectionAddresses[TxGFIR3a][0] = 0x0300; MemorySectionAddresses[TxGFIR3a][1] = 0x0327; MemorySectionAddresses[TxGFIR3b][0] = 0x0340; MemorySectionAddresses[TxGFIR3b][1] = 0x0367; MemorySectionAddresses[TxGFIR3c][0] = 0x0380; MemorySectionAddresses[TxGFIR3c][1] = 0x03A7; MemorySectionAddresses[RxTSP][0] = 0x0400; MemorySectionAddresses[RxTSP][1] = 0x040F; MemorySectionAddresses[RxNCO][0] = 0x0440; MemorySectionAddresses[RxNCO][1] = 0x0461; MemorySectionAddresses[RxGFIR1][0] = 0x0480; MemorySectionAddresses[RxGFIR1][1] = 0x04A7; MemorySectionAddresses[RxGFIR2][0] = 0x04C0; MemorySectionAddresses[RxGFIR2][1] = 0x04E7; MemorySectionAddresses[RxGFIR3a][0] = 0x0500; MemorySectionAddresses[RxGFIR3a][1] = 0x0527; MemorySectionAddresses[RxGFIR3b][0] = 0x0540; MemorySectionAddresses[RxGFIR3b][1] = 0x0567; MemorySectionAddresses[RxGFIR3c][0] = 0x0580; MemorySectionAddresses[RxGFIR3c][1] = 0x05A7; mRegistersMap->InitializeDefaultValues(LMS7parameterList); } LMS7002M::~LMS7002M() { } /** @brief Sends reset signal to chip, after reset enables B channel controls @return 0-success, other-failure */ liblms7_status LMS7002M::ResetChip() { if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; LMScomms::GenericPacket pkt; pkt.cmd = CMD_LMS7002_RST; pkt.outBuffer.push_back(LMS_RST_PULSE); controlPort->TransferPacket(pkt); if (pkt.status == STATUS_COMPLETED_CMD) { Modify_SPI_Reg_bits(LMS7param(MIMO_SISO), 0); //enable B channel after reset return LIBLMS7_SUCCESS; } else return LIBLMS7_FAILURE; } liblms7_status LMS7002M::LoadConfigLegacyFile(const char* filename) { ifstream f(filename); if (f.good() == false) //file not found { f.close(); return LIBLMS7_FILE_NOT_FOUND; } f.close(); uint16_t addr = 0; uint16_t value = 0; uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); //remember used channel liblms7_status status; typedef INI<string, string, string> ini_t; ini_t parser(filename, true); if (parser.select("FILE INFO") == false) return LIBLMS7_FILE_INVALID_FORMAT; string type = ""; type = parser.get("type", "undefined"); stringstream ss; if (type.find("LMS7002 configuration") == string::npos) { ss << "File " << filename << " not recognized" << endl; return LIBLMS7_FILE_INVALID_FORMAT; } int fileVersion = 0; fileVersion = parser.get("version", 0); vector<uint16_t> addrToWrite; vector<uint16_t> dataToWrite; if (fileVersion == 1) { if (parser.select("Reference clocks")) { mRefClkSXR_MHz = parser.get("SXR reference frequency MHz", 30.72); mRefClkSXT_MHz = parser.get("SXT reference frequency MHz", 30.72); } if (parser.select("LMS7002 registers ch.A") == true) { ini_t::sectionsit_t section = parser.sections.find("LMS7002 registers ch.A"); uint16_t x0020_value = 0; Modify_SPI_Reg_bits(LMS7param(MAC), 1); //select A channel for (ini_t::keysit_t pairs = section->second->begin(); pairs != section->second->end(); pairs++) { sscanf(pairs->first.c_str(), "%hx", &addr); sscanf(pairs->second.c_str(), "%hx", &value); if (addr == LMS7param(MAC).address) //skip register containing channel selection { x0020_value = value; continue; } addrToWrite.push_back(addr); dataToWrite.push_back(value); } status = SPI_write_batch(&addrToWrite[0], &dataToWrite[0], addrToWrite.size()); if (status != LIBLMS7_SUCCESS && status != LIBLMS7_NOT_CONNECTED) return status; //parse FCW or PHO if (parser.select("NCO Rx ch.A") == true) { char varname[64]; int mode = Get_SPI_Reg_bits(LMS7param(MODE_RX)); if (mode == 0) //FCW { for (int i = 0; i < 16; ++i) { sprintf(varname, "FCW%02i", i); SetNCOFrequency(LMS7002M::Rx, i, parser.get(varname, 0.0)); } } else { for (int i = 0; i < 16; ++i) { sprintf(varname, "PHO%02i", i); SetNCOPhaseOffset(LMS7002M::Rx, i, parser.get(varname, 0.0)); } } } if (parser.select("NCO Tx ch.A") == true) { char varname[64]; int mode = Get_SPI_Reg_bits(LMS7param(MODE_TX)); if (mode == 0) //FCW { for (int i = 0; i < 16; ++i) { sprintf(varname, "FCW%02i", i); SetNCOFrequency(LMS7002M::Tx, i, parser.get(varname, 0.0)); } } else { for (int i = 0; i < 16; ++i) { sprintf(varname, "PHO%02i", i); SetNCOPhaseOffset(LMS7002M::Tx, i, parser.get(varname, 0.0)); } } } status = SPI_write(0x0020, x0020_value); if (status != LIBLMS7_SUCCESS && status != LIBLMS7_NOT_CONNECTED) return status; } Modify_SPI_Reg_bits(LMS7param(MAC), 2); if (parser.select("LMS7002 registers ch.B") == true) { addrToWrite.clear(); dataToWrite.clear(); ini_t::sectionsit_t section = parser.sections.find("LMS7002 registers ch.B"); for (ini_t::keysit_t pairs = section->second->begin(); pairs != section->second->end(); pairs++) { sscanf(pairs->first.c_str(), "%hx", &addr); sscanf(pairs->second.c_str(), "%hx", &value); addrToWrite.push_back(addr); dataToWrite.push_back(value); } Modify_SPI_Reg_bits(LMS7param(MAC), 2); //select B channel status = SPI_write_batch(&addrToWrite[0], &dataToWrite[0], addrToWrite.size()); if (status != LIBLMS7_SUCCESS && status != LIBLMS7_NOT_CONNECTED) return status; //parse FCW or PHO if (parser.select("NCO Rx ch.B") == true) { char varname[64]; int mode = Get_SPI_Reg_bits(LMS7param(MODE_RX)); if (mode == 0) //FCW { for (int i = 0; i < 16; ++i) { sprintf(varname, "FCW%02i", i); SetNCOFrequency(LMS7002M::Rx, i, parser.get(varname, 0.0)); } } else { for (int i = 0; i < 16; ++i) { sprintf(varname, "PHO%02i", i); SetNCOPhaseOffset(LMS7002M::Rx, i, parser.get(varname, 0.0)); } } } if (parser.select("NCO Tx ch.A") == true) { char varname[64]; int mode = Get_SPI_Reg_bits(LMS7param(MODE_TX)); if (mode == 0) //FCW { for (int i = 0; i < 16; ++i) { sprintf(varname, "FCW%02i", i); SetNCOFrequency(LMS7002M::Tx, i, parser.get(varname, 0.0)); } } else { for (int i = 0; i < 16; ++i) { sprintf(varname, "PHO%02i", i); SetNCOPhaseOffset(LMS7002M::Tx, i, parser.get(varname, 0.0)); } } } } Modify_SPI_Reg_bits(LMS7param(MAC), ch); return LIBLMS7_SUCCESS; } else return LIBLMS7_FILE_INVALID_FORMAT; return LIBLMS7_FAILURE; } /** @brief Reads configuration file and uploads registers to chip @param filename Configuration source file @return 0-success, other-failure */ liblms7_status LMS7002M::LoadConfig(const char* filename) { ifstream f(filename); if (f.good() == false) //file not found { f.close(); return LIBLMS7_FILE_NOT_FOUND; } f.close(); uint16_t addr = 0; uint16_t value = 0; uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); //remember used channel liblms7_status status; typedef INI<string, string, string> ini_t; ini_t parser(filename, true); if (parser.select("file_info") == false) { //try loading as legacy format status = LoadConfigLegacyFile(filename); Modify_SPI_Reg_bits(MAC, 1); return status; } string type = ""; type = parser.get("type", "undefined"); stringstream ss; if (type.find("lms7002m_minimal_config") == string::npos) { ss << "File " << filename << " not recognized" << endl; return LIBLMS7_FILE_INVALID_FORMAT; } int fileVersion = 0; fileVersion = parser.get("version", 0); vector<uint16_t> addrToWrite; vector<uint16_t> dataToWrite; if (fileVersion == 1) { if(parser.select("lms7002_registers_a") == true) { ini_t::sectionsit_t section = parser.sections.find("lms7002_registers_a"); uint16_t x0020_value = 0; Modify_SPI_Reg_bits(LMS7param(MAC), 1); //select A channel for (ini_t::keysit_t pairs = section->second->begin(); pairs != section->second->end(); pairs++) { sscanf(pairs->first.c_str(), "%hx", &addr); sscanf(pairs->second.c_str(), "%hx", &value); if (addr == LMS7param(MAC).address) //skip register containing channel selection { x0020_value = value; continue; } addrToWrite.push_back(addr); dataToWrite.push_back(value); } status = SPI_write_batch(&addrToWrite[0], &dataToWrite[0], addrToWrite.size()); if (status != LIBLMS7_SUCCESS && status != LIBLMS7_NOT_CONNECTED) return status; status = SPI_write(0x0020, x0020_value); if (status != LIBLMS7_SUCCESS && status != LIBLMS7_NOT_CONNECTED) return status; Modify_SPI_Reg_bits(LMS7param(MAC), 2); if (status != LIBLMS7_SUCCESS && status != LIBLMS7_NOT_CONNECTED) return status; } if (parser.select("lms7002_registers_b") == true) { addrToWrite.clear(); dataToWrite.clear(); ini_t::sectionsit_t section = parser.sections.find("lms7002_registers_b"); for (ini_t::keysit_t pairs = section->second->begin(); pairs != section->second->end(); pairs++) { sscanf(pairs->first.c_str(), "%hx", &addr); sscanf(pairs->second.c_str(), "%hx", &value); addrToWrite.push_back(addr); dataToWrite.push_back(value); } Modify_SPI_Reg_bits(LMS7param(MAC), 2); //select B channel status = SPI_write_batch(&addrToWrite[0], &dataToWrite[0], addrToWrite.size()); if (status != LIBLMS7_SUCCESS && status != LIBLMS7_NOT_CONNECTED) return status; } Modify_SPI_Reg_bits(LMS7param(MAC), ch); parser.select("reference_clocks"); mRefClkSXR_MHz = parser.get("sxr_ref_clk_mhz", 30.72); mRefClkSXT_MHz = parser.get("sxt_ref_clk_mhz", 30.72); } Modify_SPI_Reg_bits(MAC, 1); if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; return LIBLMS7_SUCCESS; } /** @brief Reads all registers from chip and saves to file @param filename destination filename @return 0-success, other failure */ liblms7_status LMS7002M::SaveConfig(const char* filename) { liblms7_status status; typedef INI<> ini_t; ini_t parser(filename, true); parser.create("file_info"); parser.set("type", "lms7002m_minimal_config"); parser.set("version", 1); char addr[80]; char value[80]; uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); vector<uint16_t> addrToRead; for (uint8_t i = 0; i < MEMORY_SECTIONS_COUNT; ++i) for (uint16_t addr = MemorySectionAddresses[i][0]; addr <= MemorySectionAddresses[i][1]; ++addr) addrToRead.push_back(addr); vector<uint16_t> dataReceived; dataReceived.resize(addrToRead.size(), 0); parser.create("lms7002_registers_a"); Modify_SPI_Reg_bits(LMS7param(MAC), 1); for (uint16_t i = 0; i < addrToRead.size(); ++i) { dataReceived[i] = Get_SPI_Reg_bits(addrToRead[i], 15, 0, false); sprintf(addr, "0x%04X", addrToRead[i]); sprintf(value, "0x%04X", dataReceived[i]); parser.set(addr, value); } parser.create("lms7002_registers_b"); addrToRead.clear(); //add only B channel addresses for (uint8_t i = 0; i < MEMORY_SECTIONS_COUNT; ++i) for (uint16_t addr = MemorySectionAddresses[i][0]; addr <= MemorySectionAddresses[i][1]; ++addr) if (addr >= 0x0100) addrToRead.push_back(addr); Modify_SPI_Reg_bits(LMS7param(MAC), 2); for (uint16_t i = 0; i < addrToRead.size(); ++i) { dataReceived[i] = Get_SPI_Reg_bits(addrToRead[i], 15, 0, false); sprintf(addr, "0x%04X", addrToRead[i]); sprintf(value, "0x%04X", dataReceived[i]); parser.set(addr, value); } Modify_SPI_Reg_bits(LMS7param(MAC), ch); //retore previously used channel parser.create("reference_clocks"); parser.set("sxt_ref_clk_mhz", mRefClkSXT_MHz); parser.set("sxr_ref_clk_mhz", mRefClkSXR_MHz); parser.save(filename); return LIBLMS7_SUCCESS; } /** @brief Returns reference clock in MHz used for SXT or SXR @param Tx transmitter or receiver selection */ float_type LMS7002M::GetReferenceClk_SX(bool tx) { return tx ? mRefClkSXT_MHz : mRefClkSXR_MHz; } /** @return Current CLKGEN frequency in MHz Returned frequency depends on reference clock used for Receiver */ float_type LMS7002M::GetFrequencyCGEN_MHz() { float_type dMul = (mRefClkSXR_MHz/2.0)/(Get_SPI_Reg_bits(LMS7param(DIV_OUTCH_CGEN))+1); //DIV_OUTCH_CGEN uint16_t gINT = Get_SPI_Reg_bits(0x0088, 13, 0); //read whole register to reduce SPI transfers uint32_t gFRAC = ((gINT & 0xF) * 65536) | Get_SPI_Reg_bits(0x0087, 15, 0); return dMul * (((gINT>>4) + 1 + gFRAC/1048576.0)); } /** @brief Returns TSP reference frequency @param tx TxTSP or RxTSP selection @return TSP reference frequency in MHz */ float_type LMS7002M::GetReferenceClk_TSP_MHz(bool tx) { float_type cgenFreq = GetFrequencyCGEN_MHz(); float_type clklfreq = cgenFreq/pow(2.0, Get_SPI_Reg_bits(LMS7param(CLKH_OV_CLKL_CGEN))); if(Get_SPI_Reg_bits(LMS7param(EN_ADCCLKH_CLKGN)) == 0) return tx ? clklfreq : cgenFreq/4.0; else return tx ? cgenFreq : clklfreq/4.0; } /** @brief Sets CLKGEN frequency, calculations use receiver'r reference clock @param freq_MHz desired frequency in MHz @return 0-succes, other-cannot deliver desired frequency */ liblms7_status LMS7002M::SetFrequencyCGEN(const float_type freq_MHz) { float_type dFvco; float_type dFrac; int16_t iHdiv; //VCO frequency selection according to F_CLKH iHdiv = (int16_t)((gCGEN_VCO_frequencies[1]/ 2) / freq_MHz) - 1; dFvco = 2*(iHdiv+1) * freq_MHz; //Integer division uint16_t gINT = (uint16_t)(dFvco/mRefClkSXR_MHz - 1); //Fractional division dFrac = dFvco/mRefClkSXR_MHz - (uint32_t)(dFvco/mRefClkSXR_MHz); uint32_t gFRAC = (uint32_t)(dFrac * 1048576); Modify_SPI_Reg_bits(LMS7param(INT_SDM_CGEN), gINT); //INT_SDM_CGEN Modify_SPI_Reg_bits(0x0087, 15, 0, gFRAC&0xFFFF); //INT_SDM_CGEN[15:0] Modify_SPI_Reg_bits(0x0088, 3, 0, gFRAC>>16); //INT_SDM_CGEN[19:16] Modify_SPI_Reg_bits(LMS7param(DIV_OUTCH_CGEN), iHdiv); //DIV_OUTCH_CGEN return TuneVCO(VCO_CGEN); } /** @brief Performs VCO tuning operations for CLKGEN, SXR, SXT modules @param module module selection for tuning 0-cgen, 1-SXR, 2-SXT @return 0-success, other-failure */ liblms7_status LMS7002M::TuneVCO(VCO_Module module) // 0-cgen, 1-SXR, 2-SXT { if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; int8_t i; uint8_t cmphl; //comparators int16_t csw_lowest = -1; uint16_t addrVCOpd; // VCO power down address uint16_t addrCSW_VCO; uint16_t addrCMP; //comparator address uint8_t lsb; //SWC lsb index uint8_t msb; //SWC msb index uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); //remember used channel if(module != VCO_CGEN) //set addresses to SX module { Modify_SPI_Reg_bits(LMS7param(MAC), module); addrVCOpd = LMS7param(PD_VCO).address; addrCSW_VCO = LMS7param(CSW_VCO).address; lsb = LMS7param(CSW_VCO).lsb; msb = LMS7param(CSW_VCO).msb; addrCMP = LMS7param(VCO_CMPHO).address; } else //set addresses to CGEN module { addrVCOpd = LMS7param(PD_VCO_CGEN).address; addrCSW_VCO = LMS7param(CSW_VCO_CGEN).address; lsb = LMS7param(CSW_VCO_CGEN).lsb; msb = LMS7param(CSW_VCO_CGEN).msb; addrCMP = LMS7param(VCO_CMPHO_CGEN).address; } // Initialization Modify_SPI_Reg_bits (addrVCOpd, 2, 1, 0); //activate VCO and comparator if (Get_SPI_Reg_bits(addrVCOpd, 2, 1) != 0) return LIBLMS7_VCO_IS_POWERED_DOWN; if(module == VCO_CGEN) Modify_SPI_Reg_bits(LMS7param(SPDUP_VCO_CGEN), 1); //SHORT_NOISEFIL=1 SPDUP_VCO_ Short the noise filter resistor to speed up the settling time else Modify_SPI_Reg_bits(LMS7param(SPDUP_VCO), 1); //SHORT_NOISEFIL=1 SPDUP_VCO_ Short the noise filter resistor to speed up the settling time Modify_SPI_Reg_bits (addrCSW_VCO , msb, lsb , 0); //Set SWC_VCO<7:0>=<00000000> i=7; while(i>=0) { Modify_SPI_Reg_bits (addrCSW_VCO, lsb + i, lsb + i, 1); // CSW_VCO<i>=1 std::this_thread::sleep_for(std::chrono::milliseconds(5)); cmphl = (uint8_t)Get_SPI_Reg_bits(addrCMP, 13, 12); if ( (cmphl&0x01) == 1) // reduce CSW Modify_SPI_Reg_bits (addrCSW_VCO, lsb + i, lsb + i, 0); // CSW_VCO<i>=0 if( cmphl == 2 && csw_lowest < 0) csw_lowest = Get_SPI_Reg_bits(addrCSW_VCO, msb, lsb); --i; } if(csw_lowest >= 0) { uint16_t csw_highest = Get_SPI_Reg_bits(addrCSW_VCO, msb, lsb); if(csw_lowest == csw_highest) { while(csw_lowest>=0) { Modify_SPI_Reg_bits(addrCSW_VCO, msb, lsb, csw_lowest); std::this_thread::sleep_for(std::chrono::milliseconds(5)); if(Get_SPI_Reg_bits(addrCMP, 13, 12) == 0) { ++csw_lowest; break; } else --csw_lowest; } } Modify_SPI_Reg_bits(addrCSW_VCO, msb, lsb, csw_lowest+(csw_highest-csw_lowest)/2); } if (module == VCO_CGEN) Modify_SPI_Reg_bits(LMS7param(SPDUP_VCO_CGEN), 0); //SHORT_NOISEFIL=1 SPDUP_VCO_ Short the noise filter resistor to speed up the settling time else Modify_SPI_Reg_bits(LMS7param(SPDUP_VCO), 0); //SHORT_NOISEFIL=1 SPDUP_VCO_ Short the noise filter resistor to speed up the settling time cmphl = (uint8_t)Get_SPI_Reg_bits(addrCMP, 13, 12); Modify_SPI_Reg_bits(LMS7param(MAC), ch); //restore previously used channel if(cmphl == 2) return LIBLMS7_SUCCESS; else return LIBLMS7_FAILURE; } /** @brief Returns given parameter value from chip register @param param LMS7002M control parameter @param fromChip read directly from chip @return parameter value */ uint16_t LMS7002M::Get_SPI_Reg_bits(const LMS7Parameter ¶m, bool fromChip) { return Get_SPI_Reg_bits(param.address, param.msb, param.lsb, fromChip); } /** @brief Returns given parameter value from chip register @param address register address @param msb most significant bit index @param lsb least significant bit index @param fromChip read directly from chip @return register bits from selected interval, shifted to right by lsb bits */ uint16_t LMS7002M::Get_SPI_Reg_bits(uint16_t address, uint8_t msb, uint8_t lsb, bool fromChip) { return (SPI_read(address, fromChip) & (~(~0<<(msb+1)))) >> lsb; //shift bits to LSB } /** @brief Change given parameter value @param param LMS7002M control parameter @param fromChip read initial value directly from chip @param value new parameter value */ liblms7_status LMS7002M::Modify_SPI_Reg_bits(const LMS7Parameter ¶m, const uint16_t value, bool fromChip) { return Modify_SPI_Reg_bits(param.address, param.msb, param.lsb, value, fromChip); } /** @brief Change given parameter value @param address register address @param value new bits value, the value is shifted left by lsb bits @param fromChip read initial value directly from chip */ liblms7_status LMS7002M::Modify_SPI_Reg_bits(const uint16_t address, const uint8_t msb, const uint8_t lsb, const uint16_t value, bool fromChip) { uint16_t spiDataReg = SPI_read(address, fromChip); //read current SPI reg data uint16_t spiMask = (~(~0 << (msb - lsb + 1))) << (lsb); // creates bit mask spiDataReg = (spiDataReg & (~spiMask)) | ((value << lsb) & spiMask);//clear bits return SPI_write(address, spiDataReg); //write modified data back to SPI reg } /** @brief Modifies given registers with values applied using masks @param addr array of register addresses @param masks array of applied masks @param values array of values to be written @param start starting index of given arrays @param stop end index of given arrays */ liblms7_status LMS7002M::Modify_SPI_Reg_mask(const uint16_t *addr, const uint16_t *masks, const uint16_t *values, uint8_t start, uint8_t stop) { liblms7_status status; uint16_t reg_data; vector<uint16_t> addresses; vector<uint16_t> data; while (start <= stop) { reg_data = SPI_read(addr[start], true, &status); //read current SPI reg data reg_data &= ~masks[start];//clear bits reg_data |= (values[start] & masks[start]); addresses.push_back(addr[start]); data.push_back(reg_data); ++start; } if (status != LIBLMS7_SUCCESS) return status; SPI_write_batch(&addresses[0], &data[0], addresses.size()); return status; } /** @brief Sets SX frequency @param Tx Rx/Tx module selection @param freq_MHz desired frequency in MHz @param refClk_MHz reference clock in MHz @return 0-success, other-cannot deliver requested frequency */ liblms7_status LMS7002M::SetFrequencySX(bool tx, float_type freq_MHz, float_type refClk_MHz) { if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; const uint8_t sxVCO_N = 2; //number of entries in VCO frequencies const float_type m_dThrF = 5500; //threshold to enable additional divider uint8_t ch; //remember used channel float_type VCOfreq; int8_t div_loch; int8_t sel_vco; bool canDeliverFrequency = false; uint16_t integerPart; uint32_t fractionalPart; int8_t i; //find required VCO frequency for (div_loch = 6; div_loch >= 0; --div_loch) { VCOfreq = (1 << (div_loch + 1)) * freq_MHz; if ((VCOfreq >= gVCO_frequency_table[0][0]) && (VCOfreq <= gVCO_frequency_table[2][sxVCO_N - 1])) { canDeliverFrequency = true; break; } } if (canDeliverFrequency == false) return LIBLMS7_CANNOT_DELIVER_FREQUENCY; integerPart = (uint16_t)(VCOfreq / (refClk_MHz * (1 + (VCOfreq > m_dThrF))) - 4); fractionalPart = (uint32_t)((VCOfreq / (refClk_MHz * (1 + (VCOfreq > m_dThrF))) - (uint32_t)(VCOfreq / (refClk_MHz * (1 + (VCOfreq > m_dThrF))))) * 1048576); ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); Modify_SPI_Reg_bits(LMS7param(MAC), tx + 1); Modify_SPI_Reg_bits(LMS7param(INT_SDM), integerPart); //INT_SDM Modify_SPI_Reg_bits(0x011D, 15, 0, fractionalPart & 0xFFFF); //FRAC_SDM[15:0] Modify_SPI_Reg_bits(0x011E, 3, 0, (fractionalPart >> 16)); //FRAC_SDM[19:16] Modify_SPI_Reg_bits(LMS7param(DIV_LOCH), div_loch); //DIV_LOCH Modify_SPI_Reg_bits(LMS7param(EN_DIV2_DIVPROG), (VCOfreq > m_dThrF)); //EN_DIV2_DIVPROG //find which VCO supports required frequency Modify_SPI_Reg_bits(LMS7param(PD_VCO), 0); // Modify_SPI_Reg_bits(LMS7param(PD_VCO_COMP), 0); // int cswBackup = Get_SPI_Reg_bits(LMS7param(CSW_VCO)); //remember to restore previous tune value canDeliverFrequency = false; int tuneScore[] = { -128, -128, -128 }; //best is closest to 0 for (sel_vco = 0; sel_vco < 3; ++sel_vco) { Modify_SPI_Reg_bits(LMS7param(SEL_VCO), sel_vco); liblms7_status status = TuneVCO(tx ? VCO_SXT : VCO_SXR); int csw = Get_SPI_Reg_bits(LMS7param(CSW_VCO), true); tuneScore[sel_vco] = -128 + csw; if (status == LIBLMS7_SUCCESS) canDeliverFrequency = true; } if (abs(tuneScore[0]) < abs(tuneScore[1])) { if (abs(tuneScore[0]) < abs(tuneScore[2])) sel_vco = 0; else sel_vco = 2; } else { if (abs(tuneScore[1]) < abs(tuneScore[2])) sel_vco = 1; else sel_vco = 2; } Modify_SPI_Reg_bits(LMS7param(SEL_VCO), sel_vco); Modify_SPI_Reg_bits(LMS7param(CSW_VCO), cswBackup); Modify_SPI_Reg_bits(LMS7param(MAC), ch); //restore used channel if (tx) mRefClkSXT_MHz = refClk_MHz; else mRefClkSXR_MHz = refClk_MHz; if (canDeliverFrequency == false) return LIBLMS7_CANNOT_DELIVER_FREQUENCY; return TuneVCO( tx ? VCO_SXT : VCO_SXR); //Rx-1, Tx-2 } /** @brief Returns currently set SXR/SXT frequency @return SX frequency MHz */ float_type LMS7002M::GetFrequencySX_MHz(bool Tx, float_type refClk_MHz) { uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); //remember previously used channel float_type dMul; if(Tx) Modify_SPI_Reg_bits(LMS7param(MAC), 2); // Rx mac = 1, Tx max = 2 else Modify_SPI_Reg_bits(LMS7param(MAC), 1); // Rx mac = 1, Tx max = 2 uint16_t gINT = Get_SPI_Reg_bits(0x011E, 13, 0); // read whole register to reduce SPI transfers uint32_t gFRAC = ((gINT&0xF) * 65536) | Get_SPI_Reg_bits(0x011D, 15, 0); dMul = (float_type)refClk_MHz / (1 << (Get_SPI_Reg_bits(LMS7param(DIV_LOCH)) + 1)); //Calculate real frequency according to the calculated parameters dMul = dMul * ((gINT >> 4) + 4 + (float_type)gFRAC / 1048576.0) * (Get_SPI_Reg_bits(LMS7param(EN_DIV2_DIVPROG)) + 1); Modify_SPI_Reg_bits(LMS7param(MAC), ch); //restore used channel return dMul; } /** @brief Loads given DC_REG values into registers @param tx TxTSP or RxTSP selection @param I DC_REG I value @param Q DC_REG Q value */ liblms7_status LMS7002M::LoadDC_REG_IQ(bool tx, int16_t I, int16_t Q) { if(tx) { Modify_SPI_Reg_bits(LMS7param(DC_REG_TXTSP), I); Modify_SPI_Reg_bits(LMS7param(TSGDCLDI_TXTSP), 0); Modify_SPI_Reg_bits(LMS7param(TSGDCLDI_TXTSP), 1); Modify_SPI_Reg_bits(LMS7param(TSGDCLDI_TXTSP), 0); Modify_SPI_Reg_bits(LMS7param(DC_REG_TXTSP), Q); Modify_SPI_Reg_bits(LMS7param(TSGDCLDQ_TXTSP), 0); Modify_SPI_Reg_bits(LMS7param(TSGDCLDQ_TXTSP), 1); Modify_SPI_Reg_bits(LMS7param(TSGDCLDQ_TXTSP), 0); Modify_SPI_Reg_bits(LMS7param(DC_BYP_TXTSP), 0); //DC_BYP } else { Modify_SPI_Reg_bits(LMS7param(DC_REG_RXTSP), I); Modify_SPI_Reg_bits(LMS7param(TSGDCLDI_RXTSP), 0); Modify_SPI_Reg_bits(LMS7param(TSGDCLDI_RXTSP), 1); Modify_SPI_Reg_bits(LMS7param(TSGDCLDI_RXTSP), 0); Modify_SPI_Reg_bits(LMS7param(DC_REG_TXTSP), Q); Modify_SPI_Reg_bits(LMS7param(TSGDCLDQ_RXTSP), 0); Modify_SPI_Reg_bits(LMS7param(TSGDCLDQ_RXTSP), 1); Modify_SPI_Reg_bits(LMS7param(TSGDCLDQ_RXTSP), 0); Modify_SPI_Reg_bits(LMS7param(DC_BYP_RXTSP), 0); //DC_BYP } return LIBLMS7_SUCCESS; } /** @brief Sets chosen NCO's frequency @param tx transmitter or receiver selection @param index NCO index from 0 to 15 @param freq_MHz desired NCO frequency @return 0-success, other-failure */ liblms7_status LMS7002M::SetNCOFrequency(bool tx, uint8_t index, float_type freq_MHz) { if(index > 15) return LIBLMS7_INDEX_OUT_OF_RANGE; float_type refClk_MHz = GetReferenceClk_TSP_MHz(tx); uint16_t addr = tx ? 0x0240 : 0x0440; uint32_t fcw = (uint32_t)((freq_MHz/refClk_MHz)*4294967296); SPI_write(addr+2+index*2, (fcw >> 16)); //NCO frequency control word register MSB part. SPI_write(addr+3+index*2, fcw); //NCO frequency control word register LSB part. return LIBLMS7_SUCCESS; } /** @brief Returns chosen NCO's frequency in MHz @param tx transmitter or receiver selection @param index NCO index from 0 to 15 @param fromChip read frequency directly from chip or local registers @return NCO frequency in MHz */ float_type LMS7002M::GetNCOFrequency_MHz(bool tx, uint8_t index, const float_type refClk_MHz, bool fromChip) { if(index > 15) return LIBLMS7_INDEX_OUT_OF_RANGE; uint16_t addr = tx ? 0x0240 : 0x0440; uint32_t fcw = 0; fcw |= SPI_read(addr + 2 + index * 2, fromChip) << 16; //NCO frequency control word register MSB part. fcw |= SPI_read(addr + 3 + index * 2, fromChip); //NCO frequency control word register LSB part. return refClk_MHz*(fcw/4294967296.0); } /** @brief Sets chosen NCO phase offset angle when memory table MODE is 0 @param tx transmitter or receiver selection @param angle_deg phase offset angle in degrees @return 0-success, other-failure */ liblms7_status LMS7002M::SetNCOPhaseOffsetForMode0(bool tx, float_type angle_deg) { uint16_t addr = tx ? 0x0241 : 0x0441; uint16_t pho = (uint16_t)(65536 * (angle_deg / 360 )); SPI_write(addr, pho); return LIBLMS7_SUCCESS; } /** @brief Sets chosen NCO's phase offset angle @param tx transmitter or receiver selection @param index PHO index from 0 to 15 @param angle_deg phase offset angle in degrees @return 0-success, other-failure */ liblms7_status LMS7002M::SetNCOPhaseOffset(bool tx, uint8_t index, float_type angle_deg) { if(index > 15) return LIBLMS7_INDEX_OUT_OF_RANGE; uint16_t addr = tx ? 0x0244 : 0x0444; uint16_t pho = (uint16_t)(65536*(angle_deg / 360)); SPI_write(addr+index, pho); return LIBLMS7_SUCCESS; } /** @brief Returns chosen NCO's phase offset angle in radians @param tx transmitter or receiver selection @param index PHO index from 0 to 15 @return phase offset angle in degrees */ float_type LMS7002M::GetNCOPhaseOffset_Deg(bool tx, uint8_t index) { uint16_t addr = tx ? 0x0244 : 0x0444; uint16_t pho = SPI_read(addr+index); float_type angle = 360*pho/65536.0; return angle; } /** @brief Uploads given FIR coefficients to chip @param tx Transmitter or receiver selection @param GFIR_index GIR index from 0 to 2 @param coef array of coefficients @param coefCount number of coefficients @return 0-success, other-failure This function does not change GFIR*_L or GFIR*_N parameters, they have to be set manually */ liblms7_status LMS7002M::SetGFIRCoefficients(bool tx, uint8_t GFIR_index, const int16_t *coef, uint8_t coefCount) { uint8_t index; uint8_t coefLimit; uint16_t startAddr; if (GFIR_index == 0) startAddr = 0x0280; else if (GFIR_index == 1) startAddr = 0x02C0; else startAddr = 0x0300; if (tx == false) startAddr += 0x0200; if (GFIR_index < 2) coefLimit = 40; else coefLimit = 120; if (coefCount > coefLimit) return LIBLMS7_TOO_MANY_VALUES; vector<uint16_t> addresses; for (index = 0; index < coefCount; ++index) addresses.push_back(startAddr + index + 24 * (index / 40)); SPI_write_batch(&addresses[0], (uint16_t*)coef, coefCount); return LIBLMS7_SUCCESS; } /** @brief Returns currently loaded FIR coefficients @param tx Transmitter or receiver selection @param GFIR_index GIR index from 0 to 2 @param coef array of returned coefficients @param coefCount number of coefficients to read @return 0-success, other-failure */ liblms7_status LMS7002M::GetGFIRCoefficients(bool tx, uint8_t GFIR_index, int16_t *coef, uint8_t coefCount) { liblms7_status status = LIBLMS7_FAILURE; uint8_t index; uint8_t coefLimit; uint16_t startAddr; if(GFIR_index == 0) startAddr = 0x0280; else if (GFIR_index == 1) startAddr = 0x02C0; else startAddr = 0x0300; if (tx == false) startAddr += 0x0200; if (GFIR_index < 2) coefLimit = 40; else coefLimit = 120; if (coefCount > coefLimit) return LIBLMS7_TOO_MANY_VALUES; std::vector<uint16_t> addresses; for (index = 0; index < coefCount; ++index) addresses.push_back(startAddr + index + 24 * (index / 40)); uint16_t spiData[120]; memset(spiData, 0, 120 * sizeof(uint16_t)); if (controlPort->IsOpen()) { status = SPI_read_batch(&addresses[0], spiData, coefCount); for (index = 0; index < coefCount; ++index) coef[index] = spiData[index]; } else { const int channel = Get_SPI_Reg_bits(LMS7param(MAC), false) > 1 ? 1 : 0; for (index = 0; index < coefCount; ++index) coef[index] = mRegistersMap->GetValue(channel, addresses[index]); status = LIBLMS7_SUCCESS; } return status; } /** @brief Write given data value to whole register @param address SPI address @param data new register value @return 0-succes, other-failure */ liblms7_status LMS7002M::SPI_write(uint16_t address, uint16_t data) { if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if ((mRegistersMap->GetValue(0, LMS7param(MAC).address) & 0x0003) > 1 && address >= 0x0100) mRegistersMap->SetValue(1, address, data); else mRegistersMap->SetValue(0, address, data); if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; LMScomms::GenericPacket pkt; pkt.cmd = CMD_LMS7002_WR; pkt.outBuffer.push_back(address >> 8); pkt.outBuffer.push_back(address & 0xFF); pkt.outBuffer.push_back(data >> 8); pkt.outBuffer.push_back(data & 0xFF); controlPort->TransferPacket(pkt); if (pkt.status == STATUS_COMPLETED_CMD) return LIBLMS7_SUCCESS; else return LIBLMS7_FAILURE; } /** @brief Reads whole register value from given address @param address SPI address @param status operation status(optional) @param fromChip read value directly from chip @return register value */ uint16_t LMS7002M::SPI_read(uint16_t address, bool fromChip, liblms7_status *status) { if (controlPort == NULL) { if (status) *status = LIBLMS7_NO_CONNECTION_MANAGER; } if (controlPort->IsOpen() == false || fromChip == false) { if ((mRegistersMap->GetValue(0, LMS7param(MAC).address) & 0x0003) > 1 && address >= 0x0100) return mRegistersMap->GetValue(1, address); else return mRegistersMap->GetValue(0, address); } LMScomms::GenericPacket pkt; pkt.cmd = CMD_LMS7002_RD; pkt.outBuffer.push_back(address >> 8); pkt.outBuffer.push_back(address & 0xFF); if (controlPort->TransferPacket(pkt) == LMScomms::TRANSFER_SUCCESS) { if (status) *status = (pkt.status == STATUS_COMPLETED_CMD ? LIBLMS7_SUCCESS : LIBLMS7_FAILURE); return (pkt.inBuffer[2] << 8) | pkt.inBuffer[3]; } else return 0; } /** @brief Batches multiple register writes into least ammount of transactions @param spiAddr spi register addresses to be written @param spiData registers data to be written @param cnt number of registers to write @return 0-success, other-failure */ liblms7_status LMS7002M::SPI_write_batch(const uint16_t* spiAddr, const uint16_t* spiData, uint16_t cnt) { LMScomms::GenericPacket pkt; pkt.cmd = CMD_LMS7002_WR; uint32_t index = 0; for (uint32_t i = 0; i < cnt; ++i) { pkt.outBuffer.push_back(spiAddr[i] >> 8); pkt.outBuffer.push_back(spiAddr[i] & 0xFF); pkt.outBuffer.push_back(spiData[i] >> 8); pkt.outBuffer.push_back(spiData[i] & 0xFF); if ((mRegistersMap->GetValue(0, LMS7param(MAC).address) & 0x0003) > 1) mRegistersMap->SetValue(1, spiAddr[i], spiData[i]); else mRegistersMap->SetValue(0, spiAddr[i], spiData[i]); } if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; controlPort->TransferPacket(pkt); if (pkt.status == STATUS_COMPLETED_CMD) return LIBLMS7_SUCCESS; else return LIBLMS7_FAILURE; } /** @brief Batches multiple register reads into least amount of transactions @param spiAddr SPI addresses to read @param spiData array for read data @param cnt number of registers to read @return 0-success, other-failure */ liblms7_status LMS7002M::SPI_read_batch(const uint16_t* spiAddr, uint16_t* spiData, uint16_t cnt) { LMScomms::GenericPacket pkt; pkt.cmd = CMD_LMS7002_RD; uint32_t index = 0; for (uint32_t i = 0; i < cnt; ++i) { pkt.outBuffer.push_back(spiAddr[i] >> 8); pkt.outBuffer.push_back(spiAddr[i] & 0xFF); } if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; LMScomms::TransferStatus status = controlPort->TransferPacket(pkt); if (status != LMScomms::TRANSFER_SUCCESS) return LIBLMS7_FAILURE; for (uint32_t i = 0; i < cnt; ++i) { spiData[i] = (pkt.inBuffer[2*sizeof(uint16_t)*i + 2] << 8) | pkt.inBuffer[2*sizeof(uint16_t)*i + 3]; if ((mRegistersMap->GetValue(0, LMS7param(MAC).address) & 0x0003) > 1) mRegistersMap->SetValue(1, spiAddr[i], spiData[i]); else mRegistersMap->SetValue(0, spiAddr[i], spiData[i]); } return pkt.status == STATUS_COMPLETED_CMD ? LIBLMS7_SUCCESS : LIBLMS7_FAILURE; /* for(int i=0; i<cnt; ++i) { spiData[i] = Get_SPI_Reg_bits(spiAddr[i], 15, 0); if ((mRegistersMap->GetValue(0, LMS7param(MAC).address) & 0x0003) > 1) mRegistersMap->SetValue(1, spiAddr[i], spiData[i]); else mRegistersMap->SetValue(0, spiAddr[i], spiData[i]); } return LIBLMS7_SUCCESS;*/ } /** @brief Performs registers test by writing known data and confirming readback data @return 0-registers test passed, other-failure */ liblms7_status LMS7002M::RegistersTest() { char chex[16]; if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; liblms7_status status; uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); //backup both channel data for restoration after test vector<uint16_t> ch1Addresses; for (uint8_t i = 0; i < MEMORY_SECTIONS_COUNT; ++i) for (uint16_t addr = MemorySectionAddresses[i][0]; addr <= MemorySectionAddresses[i][1]; ++addr) ch1Addresses.push_back(addr); vector<uint16_t> ch1Data; ch1Data.resize(ch1Addresses.size(), 0); //backup A channel Modify_SPI_Reg_bits(LMS7param(MAC), 1); status = SPI_read_batch(&ch1Addresses[0], &ch1Data[0], ch1Addresses.size()); if (status != LIBLMS7_SUCCESS) return status; vector<uint16_t> ch2Addresses; for (uint8_t i = 0; i < MEMORY_SECTIONS_COUNT; ++i) for (uint16_t addr = MemorySectionAddresses[i][0]; addr <= MemorySectionAddresses[i][1]; ++addr) if (addr >= 0x0100) ch2Addresses.push_back(addr); vector<uint16_t> ch2Data; ch2Data.resize(ch2Addresses.size(), 0); Modify_SPI_Reg_bits(LMS7param(MAC), 2); status = SPI_read_batch(&ch2Addresses[0], &ch2Data[0], ch2Addresses.size()); if (status != LIBLMS7_SUCCESS) return status; //test registers ResetChip(); Modify_SPI_Reg_bits(LMS7param(MIMO_SISO), 0); Modify_SPI_Reg_bits(LMS7param(PD_RX_AFE2), 0); Modify_SPI_Reg_bits(LMS7param(PD_TX_AFE2), 0); Modify_SPI_Reg_bits(LMS7param(MAC), 1); stringstream ss; //check single channel memory sections vector<MemorySection> modulesToCheck = { AFE, BIAS, XBUF, CGEN, LDO, BIST, CDS, TRF, TBB, RFE, RBB, SX, TxTSP, TxNCO, TxGFIR1, TxGFIR2, TxGFIR3a, TxGFIR3b, TxGFIR3c, RxTSP, RxNCO, RxGFIR1, RxGFIR2, RxGFIR3a, RxGFIR3b, RxGFIR3c, LimeLight }; const char* moduleNames[] = { "AFE", "BIAS", "XBUF", "CGEN", "LDO", "BIST", "CDS", "TRF", "TBB", "RFE", "RBB", "SX", "TxTSP", "TxNCO", "TxGFIR1", "TxGFIR2", "TxGFIR3a", "TxGFIR3b", "TxGFIR3c", "RxTSP", "RxNCO", "RxGFIR1", "RxGFIR2", "RxGFIR3a", "RxGFIR3b", "RxGFIR3c", "LimeLight" }; const uint16_t patterns[] = { 0xAAAA, 0x5555 }; const uint8_t patternsCount = 2; bool allTestSuccess = true; for (unsigned i = 0; i < modulesToCheck.size(); ++i) { bool moduleTestsSuccess = true; uint16_t startAddr = MemorySectionAddresses[modulesToCheck[i]][0]; uint16_t endAddr = MemorySectionAddresses[modulesToCheck[i]][1]; uint8_t channelCount = startAddr >= 0x0100 ? 2 : 1; for (int cc = 1; cc <= channelCount; ++cc) { Modify_SPI_Reg_bits(LMS7param(MAC), cc); sprintf(chex, "0x%04X", startAddr); ss << moduleNames[i] << " [" << chex << ":"; sprintf(chex, "0x%04X", endAddr); ss << chex << "]"; if (startAddr >= 0x0100) ss << " Ch." << (cc == 1 ? "A" : "B"); ss << endl; for (uint8_t p = 0; p < patternsCount; ++p) moduleTestsSuccess &= RegistersTestInterval(startAddr, endAddr, patterns[p], ss) == LIBLMS7_SUCCESS; } allTestSuccess &= moduleTestsSuccess; } //restore register values Modify_SPI_Reg_bits(LMS7param(MAC), 1); SPI_write_batch(&ch1Addresses[0], &ch1Data[0], ch1Addresses.size()); Modify_SPI_Reg_bits(LMS7param(MAC), 2); SPI_write_batch(&ch2Addresses[0], &ch2Data[0], ch2Addresses.size()); Modify_SPI_Reg_bits(LMS7param(MAC), ch); fstream fout; fout.open("registersTest.txt", ios::out); fout << ss.str() << endl; fout.close(); return allTestSuccess ? LIBLMS7_SUCCESS : LIBLMS7_FAILURE; } /** @brief Performs registers test for given address interval by writing given pattern data @param startAddr first register address @param endAddr last reigster address @param pattern data to be written into registers @return 0-register test passed, other-failure */ liblms7_status LMS7002M::RegistersTestInterval(uint16_t startAddr, uint16_t endAddr, uint16_t pattern, stringstream &ss) { vector<uint16_t> addrToWrite; vector<uint16_t> dataToWrite; vector<uint16_t> dataReceived; vector<uint16_t> dataMasks; for (uint16_t addr = startAddr; addr <= endAddr; ++addr) { addrToWrite.push_back(addr); } dataMasks.resize(addrToWrite.size(), 0xFFFF); for (uint16_t j = 0; j < sizeof(readOnlyRegisters)/sizeof(uint16_t); ++j) for (uint16_t k = 0; k < addrToWrite.size(); ++k) if (readOnlyRegisters[j] == addrToWrite[k]) { dataMasks[k] = readOnlyRegistersMasks[j]; break; } dataToWrite.clear(); dataReceived.clear(); for (uint16_t j = 0; j < addrToWrite.size(); ++j) { if (addrToWrite[j] == 0x00A6) dataToWrite.push_back(0x1 | pattern & ~0x2); else if (addrToWrite[j] == 0x0084) dataToWrite.push_back(pattern & ~0x19); else dataToWrite.push_back(pattern & dataMasks[j]); } dataReceived.resize(addrToWrite.size(), 0); liblms7_status status; status = SPI_write_batch(&addrToWrite[0], &dataToWrite[0], addrToWrite.size()); if (status != LIBLMS7_SUCCESS) return status; status = SPI_read_batch(&addrToWrite[0], &dataReceived[0], addrToWrite.size()); if (status != LIBLMS7_SUCCESS) return status; bool registersMatch = true; char ctemp[16]; for (uint16_t j = 0; j < dataToWrite.size(); ++j) { if (dataToWrite[j] != (dataReceived[j] & dataMasks[j])) { registersMatch = false; sprintf(ctemp, "0x%04X", addrToWrite[j]); ss << "\t" << ctemp << "(wr/rd): "; sprintf(ctemp, "0x%04X", dataToWrite[j]); ss << ctemp << "/"; sprintf(ctemp, "0x%04X", dataReceived[j]); ss << ctemp << endl; } } if (registersMatch) { sprintf(ctemp, "0x%04X", pattern); ss << "\tRegisters OK (" << ctemp << ")\n"; } return registersMatch ? LIBLMS7_SUCCESS : LIBLMS7_FAILURE; } /** @brief Parameters setup instructions for Tx calibration @return 0-success, other-failure */ liblms7_status LMS7002M::CalibrateTxSetup(float_type bandwidth_MHz) { //Stage 2 uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); uint8_t sel_band1_trf = (uint8_t)Get_SPI_Reg_bits(LMS7param(SEL_BAND1_TRF)); uint8_t sel_band2_trf = (uint8_t)Get_SPI_Reg_bits(LMS7param(SEL_BAND2_TRF)); //rfe //reset RFE to defaults SetDefaults(RFE); if (sel_band1_trf == 1) Modify_SPI_Reg_bits(LMS7param(SEL_PATH_RFE), 3); //SEL_PATH_RFE 3 else if (sel_band2_trf == 1) Modify_SPI_Reg_bits(LMS7param(SEL_PATH_RFE), 2); else return LIBLMS7_BAND_NOT_SELECTED; if (ch == 2) Modify_SPI_Reg_bits(LMS7param(EN_NEXTRX_RFE), 1); // EN_NEXTTX_RFE 1 Modify_SPI_Reg_bits(LMS7param(G_RXLOOPB_RFE), 8); //G_RXLOOPB_RFE 8 Modify_SPI_Reg_bits(0x010C, 4, 3, 0); //PD_MXLOBUF_RFE 0, PD_QGEN_RFE 0 Modify_SPI_Reg_bits(LMS7param(CCOMP_TIA_RFE), 10); //CCOMP_TIA_RFE 10 Modify_SPI_Reg_bits(LMS7param(CFB_TIA_RFE), 2600); //CFB_TIA_RFE 2600 Modify_SPI_Reg_bits(LMS7param(ICT_LODC_RFE), 31); //ICT_LODC_RFE 31 Modify_SPI_Reg_bits(LMS7param(PD_LNA_RFE), 1); //RBB //reset RBB to defaults SetDefaults(RBB); Modify_SPI_Reg_bits(LMS7param(PD_LPFL_RBB), 0); //PD_LPFL_RBB 0 Modify_SPI_Reg_bits(LMS7param(RCC_CTL_LPFL_RBB), 0); //RCC_CTL_LPFL_RBB 0 Modify_SPI_Reg_bits(LMS7param(C_CTL_LPFL_RBB), 1500); //C_CTL_LPFL_RBB 1500 Modify_SPI_Reg_bits(LMS7param(G_PGA_RBB), 22); //G_PGA_RBB 22 //TRF //reset TRF to defaults //SetDefaults(TRF); Modify_SPI_Reg_bits(LMS7param(L_LOOPB_TXPAD_TRF), 0); //L_LOOPB_TXPAD_TRF 0 Modify_SPI_Reg_bits(LMS7param(EN_LOOPB_TXPAD_TRF), 1); //EN_LOOPB_TXPAD_TRF 1 Modify_SPI_Reg_bits(LMS7param(EN_G_TRF), 0); //EN_G_TRF 0 if (ch == 2) Modify_SPI_Reg_bits(LMS7param(EN_NEXTTX_TRF), 1); //EN_NEXTTX_TRF 1 Modify_SPI_Reg_bits(LMS7param(LOSS_LIN_TXPAD_TRF), 0); //LOSS_LIN_TXPAD_TRF 5 Modify_SPI_Reg_bits(LMS7param(LOSS_MAIN_TXPAD_TRF), 0); //LOSS_MAIN_TXPAD_TRF 5 //TBB //reset TBB to defaults /*SetDefaults(TBB); Modify_SPI_Reg_bits(LMS7param(CG_IAMP_TBB), 9); //CG_IAMP_TBB 9 Modify_SPI_Reg_bits(LMS7param(ICT_IAMP_FRP_TBB), 1); //ICT_IAMP_FRP_TBB 1 Modify_SPI_Reg_bits(LMS7param(ICT_IAMP_GG_FRP_TBB), 6); //ICT_IAMP_GG_FRP_TBB 6 Modify_SPI_Reg_bits(LMS7param(RCAL_LPFH_TBB), 125); //RCAL_LPFH_TBB 0 */ //AFE //reset AFE to defaults uint8_t isel_dac_afe =(uint8_t) Get_SPI_Reg_bits(LMS7param(ISEL_DAC_AFE)); SetDefaults(AFE); Modify_SPI_Reg_bits(LMS7param(PD_RX_AFE2), 0); //PD_RX_AFE2 0 Modify_SPI_Reg_bits(LMS7param(ISEL_DAC_AFE), isel_dac_afe); if (ch == 2) Modify_SPI_Reg_bits(LMS7param(PD_TX_AFE2), 0); //BIAS uint16_t backup = Get_SPI_Reg_bits(LMS7param(RP_CALIB_BIAS)); SetDefaults(BIAS); Modify_SPI_Reg_bits(LMS7param(RP_CALIB_BIAS), backup); //RP_CALIB_BIAS //XBUF Modify_SPI_Reg_bits(0x0085, 2, 0, 1); //PD_XBUF_RX 0, PD_XBUF_TX 0, EN_G_XBUF 1 //CGEN //reset CGEN to defaults SetDefaults(CGEN); //power up VCO Modify_SPI_Reg_bits(LMS7param(PD_VCO_CGEN), 0); if (SetFrequencyCGEN(122.88) != LIBLMS7_SUCCESS) return LIBLMS7_FAILURE; if (TuneVCO(VCO_CGEN) != LIBLMS7_SUCCESS) return LIBLMS7_FAILURE; //SXR Modify_SPI_Reg_bits(LMS7param(MAC), 1); SetDefaults(SX); float_type SXTfreqMHz = GetFrequencySX_MHz(Tx, mRefClkSXT_MHz); float_type SXRfreqMHz = SXTfreqMHz - bandwidth_MHz / 4 - 1; if (SetFrequencySX(Rx, SXRfreqMHz, mRefClkSXR_MHz) != LIBLMS7_SUCCESS) return LIBLMS7_FAILURE; if (TuneVCO(VCO_SXR) != LIBLMS7_SUCCESS) return LIBLMS7_FAILURE; //SXT Modify_SPI_Reg_bits(LMS7param(MAC), 2); Modify_SPI_Reg_bits(LMS7param(PD_LOCH_T2RBUF), 1); //PD_LOCH_T2RBUF 1 if (SetFrequencySX(Tx, SXTfreqMHz, mRefClkSXT_MHz) != LIBLMS7_SUCCESS) return LIBLMS7_FAILURE; if (TuneVCO(VCO_SXT) != LIBLMS7_SUCCESS) return LIBLMS7_FAILURE; Modify_SPI_Reg_bits(LMS7param(MAC), ch); //TXTSP SetDefaults(TxTSP); Modify_SPI_Reg_bits(0x0200, 3, 2, 0x3); //TSGMODE 1, INSEL 1 Modify_SPI_Reg_bits(0x0208, 6, 4, 0x7); //GFIR3_BYP 1, GFIR2_BYP 1, GFIR1_BYP 1 LoadDC_REG_IQ(Tx, (int16_t)0x7FFF, (int16_t)0x8000); Modify_SPI_Reg_bits(LMS7param(MAC), ch); Modify_SPI_Reg_bits(0x0440, 4, 0, 0); //TX SEL[3:0] = 0 & MODE = 0 float_type offset = 0.2; if (bandwidth_MHz == 8) { //SXR Modify_SPI_Reg_bits(LMS7param(MAC), 1); SetDefaults(SX); float_type SXTfreqMHz = GetFrequencySX_MHz(Tx, mRefClkSXT_MHz); float_type sxrFreq = SXTfreqMHz - bandwidth_MHz / 4 - 1 - offset; if (SetFrequencySX(Rx, sxrFreq, mRefClkSXR_MHz) != LIBLMS7_SUCCESS) return LIBLMS7_FAILURE; SetNCOFrequency(Tx, 0, bandwidth_MHz / 4 + offset); } else SetNCOFrequency(Tx, 0, bandwidth_MHz / 4); //RXTSP SetDefaults(RxTSP); Modify_SPI_Reg_bits(LMS7param(AGC_MODE_RXTSP), 1); //AGC_MODE 1 Modify_SPI_Reg_bits(0x040C, 7, 0, 0xBF); Modify_SPI_Reg_bits(LMS7param(CAPSEL), 0); Modify_SPI_Reg_bits(LMS7param(HBD_OVR_RXTSP), 0); //Decimation HBD ratio Modify_SPI_Reg_bits(LMS7param(AGC_AVG_RXTSP), 0x7); //agc_avg iq corr return LIBLMS7_SUCCESS; } /** @brief Flips the CAPTURE bit and returns digital RSSI value */ uint32_t LMS7002M::GetRSSI() { Modify_SPI_Reg_bits(LMS7param(CAPTURE), 0); Modify_SPI_Reg_bits(LMS7param(CAPTURE), 1); return (Get_SPI_Reg_bits(0x040F, 15, 0) << 2) | Get_SPI_Reg_bits(0x040E, 1, 0); } /** @brief Sets Rx Dc offsets by converting two's complementary numbers to sign and magnitude */ void LMS7002M::SetRxDCOFF(int8_t offsetI, int8_t offsetQ) { uint16_t valToSend = 0; if (offsetI < 0) valToSend |= 0x40; valToSend |= labs(offsetI); valToSend = valToSend << 7; if (offsetQ < 0) valToSend |= 0x40; valToSend |= labs(offsetQ); SPI_write(0x010E, valToSend); } /** @brief Calibrates Transmitter. DC correction, IQ gains, IQ phase correction @return 0-success, other-failure */ liblms7_status LMS7002M::CalibrateTx(float_type bandwidth_MHz) { liblms7_status status; Log("Tx calibration started", LOG_INFO); BackupAllRegisters(); int16_t iqcorr = 0; uint16_t gcorrq = 0; uint16_t gcorri = 0; uint16_t dccorri; uint16_t dccorrq; int16_t corrI = 0; int16_t corrQ = 0; uint32_t minRSSI_i; uint32_t minRSSI_q; uint32_t minRSSI_iq; int16_t i; int16_t offsetI = 0; int16_t offsetQ = 0; const short firCoefs[] = { -2531, -517, 2708, 188, -3059, 216, 3569, -770, -4199, 1541, 4886, -2577, -5552, 3909, 6108, -5537, -6457, 7440, 6507, -9566, -6174, 11845, 5391, -14179, -4110, 16457, 2310, -18561, 0, 20369, -2780, -21752, 5963, 22610, -9456, -22859, 13127, 22444, -16854, -21319, 20489, 19492, -23883, -17002, 26881, 13902, -29372, -10313, 31226, 6345, -32380, -2141, 32767, -2141, -32380, 6345, 31226, -10313, -29372, 13902, 26881, -17002, -23883, 19492, 20489, -21319, -16854, 22444, 13127, -22859, -9456, 22610, 5963, -21752, -2780, 20369, 0, -18561, 2310, 16457, -4110, -14179, 5391, 11845, -6174, -9566, 6507, 7440, -6457, -5537, 6108, 3909, -5552, -2577, 4886, 1541, -4199, -770, 3569, 216, -3059, 188, 2708, -517, -2531 }; uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); //Stage 1 uint8_t sel_band1_trf = (uint8_t)Get_SPI_Reg_bits(LMS7param(SEL_BAND1_TRF)); uint8_t sel_band2_trf = (uint8_t)Get_SPI_Reg_bits(LMS7param(SEL_BAND2_TRF)); Log("Setup stage", LOG_INFO); status = CalibrateTxSetup(bandwidth_MHz); if (status != LIBLMS7_SUCCESS) goto TxCalibrationEnd; //go to ending stage to restore registers //Stage 3 //Calibrate Rx DC Log("Rx DC calibration", LOG_INFO); { uint16_t requiredRegs[] = { 0x0400, 0x040A, 0x010D, 0x040C }; uint16_t requiredMask[] = { 0x6000, 0x3007, 0x0040, 0x00FF }; //CAPSEL, AGC_MODE, AGC_AVG, EN_DCOFF, Bypasses uint16_t requiredValue[] = { 0x0000, 0x1007, 0x0040, 0x00BD }; Modify_SPI_Reg_mask(requiredRegs, requiredMask, requiredValue, 0, 3); } for (i = 0; i<6; ++i) { FindMinRSSI(LMS7param(DCOFFI_RFE), offsetI, &offsetI, 3, 2, 32 >> i); FindMinRSSI(LMS7param(DCOFFQ_RFE), offsetQ, &offsetQ, 3, 2, 32 >> i); } SetRxDCOFF((int8_t)offsetI, (int8_t)offsetQ); Modify_SPI_Reg_bits(LMS7param(DC_BYP_RXTSP), 0); // DC_BYP 0 sel_band1_trf = (uint8_t)Get_SPI_Reg_bits(LMS7param(SEL_BAND1_TRF)); sel_band2_trf = (uint8_t)Get_SPI_Reg_bits(LMS7param(SEL_BAND2_TRF)); //B Modify_SPI_Reg_bits(0x0100, 0, 0, 1); //EN_G_TRF 1 if (sel_band1_trf == 1) { Modify_SPI_Reg_bits(LMS7param(PD_RLOOPB_1_RFE), 0); //PD_RLOOPB_1_RFE 0 Modify_SPI_Reg_bits(LMS7param(EN_INSHSW_LB1_RFE), 0); //EN_INSHSW_LB1 0 } if (sel_band2_trf == 1) { Modify_SPI_Reg_bits(LMS7param(PD_RLOOPB_2_RFE), 0); //PD_RLOOPB_2_RFE 0 Modify_SPI_Reg_bits(LMS7param(EN_INSHSW_LB2_RFE), 0); // EN_INSHSW_LB2 0 } FixRXSaturation(); Modify_SPI_Reg_bits(LMS7param(GFIR3_BYP_RXTSP), 0); //GFIR3_BYP 0 Modify_SPI_Reg_bits(LMS7param(HBD_OVR_RXTSP), 2); Modify_SPI_Reg_bits(LMS7param(GFIR3_L_RXTSP), 7); Modify_SPI_Reg_bits(LMS7param(GFIR3_N_RXTSP), 7); SetGFIRCoefficients(Rx, 2, firCoefs, sizeof(firCoefs) / sizeof(int16_t)); Log("IQ correction stage", LOG_INFO); Modify_SPI_Reg_bits(LMS7param(GCORRI_TXTSP), 2047); Modify_SPI_Reg_bits(LMS7param(GCORRQ_TXTSP), 2047); Modify_SPI_Reg_bits(LMS7param(IQCORR_TXTSP), 0); Log("I gain", LOG_INFO); minRSSI_i = FindMinRSSI_Gain(LMS7param(GCORRI_TXTSP), &gcorri); Modify_SPI_Reg_bits(LMS7param(GCORRI_TXTSP), 2047); Modify_SPI_Reg_bits(LMS7param(GCORRQ_TXTSP), 2047); Log("Q gain", LOG_INFO); minRSSI_q = FindMinRSSI_Gain(LMS7param(GCORRQ_TXTSP), &gcorrq); if (minRSSI_i < minRSSI_q) gcorrq = 2047; else gcorri = 2047; Modify_SPI_Reg_bits(LMS7param(GCORRI_TXTSP), gcorri); Modify_SPI_Reg_bits(LMS7param(GCORRQ_TXTSP), gcorrq); Log("Phase", LOG_INFO); iqcorr = 0; for (uint8_t i = 0; i<9; ++i) minRSSI_iq = FindMinRSSI(LMS7param(IQCORR_TXTSP), iqcorr, &iqcorr, 3, 1, 256 >> i); Modify_SPI_Reg_bits(LMS7param(GCORRI_TXTSP), gcorri); Modify_SPI_Reg_bits(LMS7param(GCORRQ_TXTSP), gcorrq); Modify_SPI_Reg_bits(LMS7param(IQCORR_TXTSP), iqcorr); Modify_SPI_Reg_bits(LMS7param(MAC), 1); status = SetFrequencySX(Rx, GetFrequencySX_MHz(Tx, mRefClkSXT_MHz)-1, mRefClkSXR_MHz); if (status != LIBLMS7_SUCCESS) goto TxCalibrationEnd; //go to ending stage to restore registers //C Modify_SPI_Reg_bits(LMS7param(MAC), ch); Modify_SPI_Reg_bits(LMS7param(AGC_MODE_RXTSP), 1); Modify_SPI_Reg_bits(LMS7param(CAPSEL), 0); Log("TX LO calibration", LOG_INFO); //Calibrate Tx DC for (uint8_t i = 0; i<7; ++i) { FindMinRSSI(LMS7param(DCCORRI_TXTSP), corrI, &corrI, 3, 1, 64 >> i); FindMinRSSI(LMS7param(DCCORRQ_TXTSP), corrQ, &corrQ, 3, 1, 64 >> i); } dccorri = Get_SPI_Reg_bits(LMS7param(DCCORRI_TXTSP)); dccorrq = Get_SPI_Reg_bits(LMS7param(DCCORRQ_TXTSP)); // Stage 4 TxCalibrationEnd: Log("Restoring registers state", LOG_INFO); Modify_SPI_Reg_bits(LMS7param(MAC), ch); RestoreAllRegisters(); if (status != LIBLMS7_SUCCESS) { Log("Tx calibration failed", LOG_WARNING); return status; } Modify_SPI_Reg_bits(LMS7param(MAC), ch); Modify_SPI_Reg_bits(LMS7param(DCCORRI_TXTSP), dccorri); Modify_SPI_Reg_bits(LMS7param(DCCORRQ_TXTSP), dccorrq); Modify_SPI_Reg_bits(LMS7param(GCORRI_TXTSP), gcorri); Modify_SPI_Reg_bits(LMS7param(GCORRQ_TXTSP), gcorrq); Modify_SPI_Reg_bits(LMS7param(IQCORR_TXTSP), iqcorr); Modify_SPI_Reg_bits(LMS7param(DC_BYP_TXTSP), 0); //DC_BYP Modify_SPI_Reg_bits(0x0208, 1, 0, 0); //GC_BYP PH_BYP Log("Tx calibration finished", LOG_INFO); return LIBLMS7_SUCCESS; } /** @brief Performs Rx DC offsets calibration */ void LMS7002M::CalibrateRxDC_RSSI() { int16_t i; int16_t offsetI = 0; int16_t offsetQ = 0; uint16_t requiredRegs[] = { 0x0400, 0x040A, 0x010D, 0x040C }; uint16_t requiredMask[] = { 0x6000, 0x3007, 0x0040, 0x00FF }; //CAPSEL, AGC_MODE, AGC_AVG, EN_DCOFF, Bypasses uint16_t requiredValue[] = { 0x0000, 0x1007, 0x0040, 0x00BD }; Modify_SPI_Reg_mask(requiredRegs, requiredMask, requiredValue, 0, 3); for (i = 0; i<6; ++i) { FindMinRSSI(LMS7param(DCOFFI_RFE), offsetI, &offsetI, 3, 2, 32 >> i); FindMinRSSI(LMS7param(DCOFFQ_RFE), offsetQ, &offsetQ, 3, 2, 32 >> i); } Modify_SPI_Reg_bits(LMS7param(EN_DCOFF_RXFE_RFE), 1); SetRxDCOFF((int8_t)offsetI, (int8_t)offsetQ); Modify_SPI_Reg_bits(LMS7param(DC_BYP_RXTSP), 0); // DC_BYP 0 } /** @brief Tries to detect and fix gains if Rx is saturated @return 0-success, other-failure */ liblms7_status LMS7002M::FixRXSaturation() { uint8_t g_rxloopb = 0; Modify_SPI_Reg_bits(LMS7param(G_RXLOOPB_RFE), g_rxloopb); Modify_SPI_Reg_bits(LMS7param(L_LOOPB_TXPAD_TRF), 3); int8_t lLoopb = 3; const uint32_t rssi_saturation_level = 0xD000; while (g_rxloopb < 15) { g_rxloopb += 1; Modify_SPI_Reg_bits(LMS7param(G_RXLOOPB_RFE), g_rxloopb); Modify_SPI_Reg_bits(LMS7param(L_LOOPB_TXPAD_TRF), 3); if (GetRSSI() < rssi_saturation_level) { for (lLoopb = 3; lLoopb >= 0; --lLoopb) { Modify_SPI_Reg_bits(LMS7param(L_LOOPB_TXPAD_TRF), lLoopb); if (GetRSSI() > rssi_saturation_level) { ++lLoopb; Modify_SPI_Reg_bits(LMS7param(L_LOOPB_TXPAD_TRF), lLoopb); goto finished; } } } else { g_rxloopb -= 1; Modify_SPI_Reg_bits(LMS7param(G_RXLOOPB_RFE), g_rxloopb); break; } } finished: return GetRSSI() < rssi_saturation_level ? LIBLMS7_SUCCESS : LIBLMS7_FAILURE; } uint32_t LMS7002M::FindMinRSSI(const LMS7Parameter ¶m, const int16_t startValue, int16_t *result, const uint8_t scanWidth, const uint8_t twoCompl, int8_t stepMult) { return FindMinRSSI(param.address, param.msb, param.lsb, startValue, result, scanWidth, twoCompl, stepMult); } /** @brief Searches for minimal RSSI value while changing given address bits @param addr address of parameter being changed @param msb most significant bit index @param lsb least significant bit index @param startValue initial value where to start search @param result found minimal parameter value will be set here @param twoCompl varying parameter value is treated as two's complement @return found minimal RSSI value */ uint32_t LMS7002M::FindMinRSSI(const uint16_t addr, const uint8_t msb, const uint8_t lsb, const int16_t startValue, int16_t *result, const uint8_t scanWidth, const uint8_t twoCompl, int8_t stepMult) { if (scanWidth < 1) return ~0; int minI; int min = startValue; int globMin = 0; uint32_t minRSSI = ~0; unsigned int *rssiField = new unsigned int[scanWidth]; int minRSSIindex; int i; int maxVal; int minVal = 0; if (twoCompl) { maxVal = (~(~0x0 << (msb - lsb + 1))) / 2; minVal = -(~(~0x0 << (msb - lsb + 1))) / 2 - 1; } else maxVal = (~(~0x0 << (msb - lsb + 1))); Modify_SPI_Reg_bits(addr, msb, lsb, startValue); Modify_SPI_Reg_bits(LMS7param(AGC_MODE_RXTSP), 1); Modify_SPI_Reg_bits(LMS7param(CAPSEL), 0); minRSSIindex = 0; for (i = 0; i<scanWidth; ++i) { short currentValue = min + (i - scanWidth / 2)*stepMult; if (currentValue < minVal) currentValue = minVal; else if (currentValue > maxVal) currentValue = maxVal; if (twoCompl == 2) { uint16_t valToSend = 0; if (currentValue < 0) valToSend |= 0x40; valToSend |= labs(currentValue); Modify_SPI_Reg_bits(addr, msb, lsb, valToSend); } else Modify_SPI_Reg_bits(addr, msb, lsb, currentValue); rssiField[i] = GetRSSI(); } minI = min; minRSSIindex = 0; for (i = 0; i<scanWidth; ++i) if (rssiField[i] < minRSSI) { minRSSI = rssiField[i]; minRSSIindex = i; minI = min + (i - scanWidth / 2)*stepMult; if (minI > maxVal) minI = maxVal; else if (minI < minVal) minI = minVal; globMin = minI; } min = minI; Modify_SPI_Reg_bits(addr, msb, lsb, min); *result = min; return minRSSI; } /** @brief Sets given module registers to default values @return 0-success, other-failure */ liblms7_status LMS7002M::SetDefaults(MemorySection module) { liblms7_status status = LIBLMS7_SUCCESS; vector<uint16_t> addrs; vector<uint16_t> values; for(uint32_t address = MemorySectionAddresses[module][0]; address <= MemorySectionAddresses[module][1]; ++address) { addrs.push_back(address); values.push_back(mRegistersMap->GetDefaultValue(address)); } status = SPI_write_batch(&addrs[0], &values[0], addrs.size()); return status; } /** @brief Parameters setup instructions for Rx calibration @param bandwidth_MHz filter bandwidth in MHz @return 0-success, other-failure */ liblms7_status LMS7002M::CalibrateRxSetup(float_type bandwidth_MHz) { uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); //rfe if (ch == 2) Modify_SPI_Reg_bits(LMS7param(EN_NEXTTX_TRF), 1); // EN_NEXTTX_TRF 0 Modify_SPI_Reg_bits(LMS7param(G_RXLOOPB_RFE), 15); //G_RXLOOPB_RFE 15 Modify_SPI_Reg_bits(0x010C, 4, 3, 0); //PD_MXLOBUF_RFE 0, PD_QGEN_RFE 0 Modify_SPI_Reg_bits(0x010C, 1, 1, 0); //PD_TIA 0 Modify_SPI_Reg_bits(0x010C, 7, 7, 1); //PD_LNA 1 Modify_SPI_Reg_bits(0x0110, 4, 0, 31); //ICT_LO_RFE 31 Modify_SPI_Reg_bits(0x010D, 4, 1, 0xFF); // all short switches are enabled //RBB Modify_SPI_Reg_bits(0x0115, 15, 14, 0); //Loopback switches disable Modify_SPI_Reg_bits(0x0119, 15, 15, 0); //OSW_PGA 0 //TRF //reset TRF to defaults SetDefaults(TRF); Modify_SPI_Reg_bits(LMS7param(L_LOOPB_TXPAD_TRF), 0); //L_LOOPB_TXPAD_TRF 0 Modify_SPI_Reg_bits(LMS7param(EN_LOOPB_TXPAD_TRF), 1); //EN_LOOPB_TXPAD_TRF 1 Modify_SPI_Reg_bits(LMS7param(EN_G_TRF), 0); //EN_G_TRF 0 if (ch == 2) Modify_SPI_Reg_bits(LMS7param(EN_NEXTTX_TRF), 1); //EN_NEXTTX_TRF 1 Modify_SPI_Reg_bits(LMS7param(LOSS_LIN_TXPAD_TRF), 0); //LOSS_LIN_TXPAD_TRF 5 Modify_SPI_Reg_bits(LMS7param(LOSS_MAIN_TXPAD_TRF), 0); //LOSS_MAIN_TXPAD_TRF 5 //TBB //reset TBB to defaults SetDefaults(TBB); Modify_SPI_Reg_bits(LMS7param(CG_IAMP_TBB), 9); //CG_IAMP_TBB 9 Modify_SPI_Reg_bits(LMS7param(ICT_IAMP_FRP_TBB), 1); //ICT_IAMP_FRP_TBB 1 Modify_SPI_Reg_bits(LMS7param(ICT_IAMP_GG_FRP_TBB), 6); //ICT_IAMP_GG_FRP_TBB 6 //AFE //reset AFE to defaults SetDefaults(AFE); Modify_SPI_Reg_bits(LMS7param(PD_RX_AFE2), 0); //PD_RX_AFE2 if (ch == 2) { Modify_SPI_Reg_bits(LMS7param(PD_TX_AFE2), 0); //PD_TX_AFE2 } //BIAS uint16_t backup = Get_SPI_Reg_bits(0x0084, 10, 6); SetDefaults(BIAS); Modify_SPI_Reg_bits(0x0084, 10, 6, backup); //RP_CALIB_BIAS //XBUF Modify_SPI_Reg_bits(0x0085, 2, 0, 1); //PD_XBUF_RX 0, PD_XBUF_TX 0, EN_G_XBUF 1 //CGEN //reset CGEN to defaults SetDefaults(CGEN); //power up VCO Modify_SPI_Reg_bits(0x0086, 2, 2, 0); liblms7_status status = SetFrequencyCGEN(122.88); if (status != LIBLMS7_SUCCESS) return status; // //SXR Modify_SPI_Reg_bits(LMS7param(MAC), 1); float_type SXRfreqMHz = GetFrequencySX_MHz(Rx, mRefClkSXR_MHz); //SXT Modify_SPI_Reg_bits(LMS7param(MAC), 2); Modify_SPI_Reg_bits(LMS7param(PD_LOCH_T2RBUF), 1); //PD_LOCH_t2RBUF 1 status = SetFrequencySX(Tx, SXRfreqMHz + bandwidth_MHz / 4, mRefClkSXT_MHz); if ( status != LIBLMS7_SUCCESS) return status; Modify_SPI_Reg_bits(LMS7param(MAC), ch); //TXTSP SetDefaults(TxTSP); Modify_SPI_Reg_bits(0x0200, 3, 2, 0x3); //TSGMODE 1, INSEL 1 //Modify_SPI_Reg_bits(0x0208, 6, 4, 0xFFFF); //GFIR3_BYP 1, GFIR2_BYP 1, GFIR1_BYP 1 Modify_SPI_Reg_bits(0x0208, 6, 6, 1); //GFIR3_BYP 1, GFIR2_BYP 1, GFIR1_BYP 1 Modify_SPI_Reg_bits(0x0208, 5, 5, 1); //GFIR3_BYP 1, GFIR2_BYP 1, GFIR1_BYP 1 Modify_SPI_Reg_bits(0x0208, 4, 4, 1); //GFIR3_BYP 1, GFIR2_BYP 1, GFIR1_BYP 1 LoadDC_REG_IQ(Tx, (int16_t)0x7FFF, (int16_t)0x8000); SetNCOFrequency(Tx, 0, 0); //RXTSP SetDefaults(RxTSP); Modify_SPI_Reg_bits(LMS7param(AGC_MODE_RXTSP), 1); //AGC_MODE 1 Modify_SPI_Reg_bits(0x040C, 7, 7, 0x1); //CMIX_BYP 1 Modify_SPI_Reg_bits(0x040C, 6, 6, 0x0); //AGC_BYP 0 Modify_SPI_Reg_bits(0x040C, 5, 5, 1); // Modify_SPI_Reg_bits(0x040C, 4, 4, 1); // Modify_SPI_Reg_bits(0x040C, 3, 3, 1); // Modify_SPI_Reg_bits(0x040C, 2, 2, 1); // DC_BYP Modify_SPI_Reg_bits(0x040C, 1, 1, 1); // Modify_SPI_Reg_bits(0x040C, 0, 0, 1); // Modify_SPI_Reg_bits(LMS7param(CAPSEL), 0); Modify_SPI_Reg_bits(LMS7param(HBD_OVR_RXTSP), 2); Modify_SPI_Reg_bits(LMS7param(AGC_AVG_RXTSP), 0x7); //agc_avg iq corr return LIBLMS7_SUCCESS; } /** @brief Calibrates Receiver. DC offset, IQ gains, IQ phase correction @return 0-success, other-failure */ liblms7_status LMS7002M::CalibrateRx(float_type bandwidth_MHz) { liblms7_status status; uint32_t minRSSI_i; uint32_t minRSSI_q; int16_t iqcorr_rx = 0; uint32_t minRSSI_iq; int16_t dcoffi; int16_t dcoffq; const int16_t firCoefs[] = { -2531, -517, 2708, 188, -3059, 216, 3569, -770, -4199, 1541, 4886, -2577, -5552, 3909, 6108, -5537, -6457, 7440, 6507, -9566, -6174, 11845, 5391, -14179, -4110, 16457, 2310, -18561, 0, 20369, -2780, -21752, 5963, 22610, -9456, -22859, 13127, 22444, -16854, -21319, 20489, 19492, -23883, -17002, 26881, 13902, -29372, -10313, 31226, 6345, -32380, -2141, 32767, -2141, -32380, 6345, 31226, -10313, -29372, 13902, 26881, -17002, -23883, 19492, 20489, -21319, -16854, 22444, 13127, -22859, -9456, 22610, 5963, -21752, -2780, 20369, 0, -18561, 2310, 16457, -4110, -14179, 5391, 11845, -6174, -9566, 6507, 7440, -6457, -5537, 6108, 3909, -5552, -2577, 4886, 1541, -4199, -770, 3569, 216, -3059, 188, 2708, -517, -2531 }; Log("Rx calibration started", LOG_INFO); uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); Log("Saving registers state", LOG_INFO); BackupAllRegisters(); uint8_t sel_path_rfe = (uint8_t)Get_SPI_Reg_bits(LMS7param(SEL_PATH_RFE)); if (sel_path_rfe == 1 || sel_path_rfe == 0) return LIBLMS7_BAD_SEL_PATH; Log("Setup stage", LOG_INFO); status = CalibrateRxSetup(bandwidth_MHz); if (status != LIBLMS7_SUCCESS) goto RxCalibrationEndStage; Log("Rx DC calibration", LOG_INFO); CalibrateRxDC_RSSI(); dcoffi = Get_SPI_Reg_bits(LMS7param(DCOFFI_RFE)); dcoffq = Get_SPI_Reg_bits(LMS7param(DCOFFQ_RFE)); Modify_SPI_Reg_bits(LMS7param(EN_G_TRF), 1); if (sel_path_rfe == 2) { Modify_SPI_Reg_bits(LMS7param(PD_RLOOPB_2_RFE), 0); Modify_SPI_Reg_bits(0x0103, 10, 10, 1); Modify_SPI_Reg_bits(0x0103, 11, 11, 0); Modify_SPI_Reg_bits(LMS7param(EN_INSHSW_LB2_RFE), 0); } if (sel_path_rfe == 3) { Modify_SPI_Reg_bits(LMS7param(PD_RLOOPB_1_RFE), 0); Modify_SPI_Reg_bits(0x0103, 11, 11, 1); Modify_SPI_Reg_bits(0x0103, 10, 10, 0); Modify_SPI_Reg_bits(LMS7param(EN_INSHSW_LB1_RFE), 0); } Modify_SPI_Reg_bits(0x040C, 7, 7, 0); //CMIX_BYP 0 Modify_SPI_Reg_bits(0x040C, 2, 0, 0); //DC_BYP 0, GC_BYP 0, PH_BYP 0 Modify_SPI_Reg_bits(LMS7param(CMIX_GAIN_RXTSP), 1); //CMIX_GAIN 1 +6 db Modify_SPI_Reg_bits(0x040C, 13, 13, 1); //CMIX_SC 1 FixRXSaturation(); Modify_SPI_Reg_bits(0x040C, 5, 5, 0); //GFIR3_BYP 0 Modify_SPI_Reg_bits(LMS7param(HBD_OVR_RXTSP), 2); Modify_SPI_Reg_bits(LMS7param(GFIR3_L_RXTSP), 7); Modify_SPI_Reg_bits(LMS7param(GFIR3_N_RXTSP), 7); SetGFIRCoefficients(Rx, 2, firCoefs, sizeof(firCoefs) / sizeof(int16_t)); SetNCOFrequency(Rx, 0, bandwidth_MHz / 4 + 1); Modify_SPI_Reg_bits(LMS7param(GCORRI_RXTSP), 2047); Modify_SPI_Reg_bits(LMS7param(GCORRQ_RXTSP), 2047); Log("IQ correction stage", LOG_INFO); iqcorr_rx = 0; for (int i = 0; i<9; ++i) minRSSI_iq = FindMinRSSI(LMS7param(IQCORR_RXTSP), iqcorr_rx, &iqcorr_rx, 3, 1, 256 >> i); Modify_SPI_Reg_bits(LMS7param(IQCORR_RXTSP), iqcorr_rx); uint16_t mingcorri; Log("I gain", LOG_INFO); minRSSI_i = FindMinRSSI_Gain(LMS7param(GCORRI_RXTSP), &mingcorri); Modify_SPI_Reg_bits(LMS7param(GCORRI_RXTSP), 2047); Modify_SPI_Reg_bits(LMS7param(GCORRQ_RXTSP), 2047); Log("Q gain", LOG_INFO); uint16_t mingcorrq; minRSSI_q = FindMinRSSI_Gain(LMS7param(GCORRQ_RXTSP), &mingcorrq); if (minRSSI_i < minRSSI_q) mingcorrq = 2047; else mingcorri = 2047; Modify_SPI_Reg_bits(LMS7param(GCORRI_RXTSP), mingcorri); Modify_SPI_Reg_bits(LMS7param(GCORRQ_RXTSP), mingcorrq); Log("Phase", LOG_INFO); for (int i = 0; i<9; ++i) minRSSI_iq = FindMinRSSI(LMS7param(IQCORR_RXTSP), iqcorr_rx, &iqcorr_rx, 3, 1, 256 >> i); RxCalibrationEndStage: Log("Restoring registers state", LOG_INFO); RestoreAllRegisters(); if (status != LIBLMS7_SUCCESS) { Log("Rx calibration failed", LOG_WARNING); return status; } Modify_SPI_Reg_bits(LMS7param(MAC), ch); SetRxDCOFF((int8_t)dcoffi, (int8_t)dcoffq); Modify_SPI_Reg_bits(LMS7param(EN_DCOFF_RXFE_RFE), 1); Modify_SPI_Reg_bits(LMS7param(GCORRI_RXTSP), mingcorri); Modify_SPI_Reg_bits(LMS7param(GCORRQ_RXTSP), mingcorrq); Modify_SPI_Reg_bits(LMS7param(IQCORR_RXTSP), iqcorr_rx); Modify_SPI_Reg_bits(0x040C, 2, 0, 0); //DC_BYP 0, GC_BYP 0, PH_BYP 0 Modify_SPI_Reg_bits(0x0110, 4, 0, 31); //ICT_LO_RFE 31 Log("Rx calibration finished", LOG_INFO); return LIBLMS7_SUCCESS; } const uint16_t backupAddrs[] = { 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002E, 0x0081, 0x0082, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010C, 0x010D, 0x010E, 0x010F, 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x0119, 0x011A, 0x011C, 0x011D, 0x011E, 0x011F, 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0240, 0x0242, 0x0243, 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x0440, 0x0442, 0x0443 }; uint16_t backupRegs[sizeof(backupAddrs) / 2]; const uint16_t backupSXAddr[] = { 0x011C, 0x011D, 0x011E, 0x011F, 0x01200, 0x0121, 0x0122, 0x0123, 0x0124 }; uint16_t backupRegsSXR[sizeof(backupSXAddr) / 2]; uint16_t backupRegsSXT[sizeof(backupSXAddr) / 2]; /** @brief Stores chip current registers state into memory for later restoration */ void LMS7002M::BackupAllRegisters() { uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); SPI_read_batch(backupAddrs, backupRegs, sizeof(backupAddrs) / sizeof(uint16_t)); Modify_SPI_Reg_bits(LMS7param(MAC), 1); // channel A SPI_read_batch(backupSXAddr, backupRegsSXR, sizeof(backupRegsSXR) / sizeof(uint16_t)); Modify_SPI_Reg_bits(LMS7param(MAC), 2); // channel B SPI_read_batch(backupSXAddr, backupRegsSXT, sizeof(backupRegsSXR) / sizeof(uint16_t)); Modify_SPI_Reg_bits(LMS7param(MAC), ch); } /** @brief Sets chip registers to state that was stored in memory using BackupAllRegisters() */ void LMS7002M::RestoreAllRegisters() { uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); SPI_write_batch(backupAddrs, backupRegs, sizeof(backupAddrs) / sizeof(uint16_t)); Modify_SPI_Reg_bits(LMS7param(MAC), 1); // channel A SPI_write_batch(backupSXAddr, backupRegsSXR, sizeof(backupRegsSXR) / sizeof(uint16_t)); Modify_SPI_Reg_bits(LMS7param(MAC), 2); // channel B SPI_write_batch(backupSXAddr, backupRegsSXT, sizeof(backupRegsSXR) / sizeof(uint16_t)); Modify_SPI_Reg_bits(LMS7param(MAC), ch); } /** @brief Searches for minimal digital RSSI value by changing given gain parameter @param param LMS7002M gain correction parameter @param foundValue returns value which achieved minimal RSSI @return minimal found RSSI value */ uint32_t LMS7002M::FindMinRSSI_Gain(const LMS7Parameter ¶m, uint16_t *foundValue) { uint32_t RSSI = ~0 - 2; uint32_t prevRSSI = RSSI + 1; uint8_t decrement = 2; uint16_t gcorr = 2047; while (gcorr > 1024) { Modify_SPI_Reg_bits(param, gcorr); RSSI = GetRSSI(); if (RSSI < prevRSSI) { prevRSSI = RSSI; *foundValue = gcorr; gcorr -= decrement; decrement *= 2; } else { if (decrement == 2) break; gcorr -= decrement; decrement = 2; } } return prevRSSI; } /** @brief Reads all chip configuration and checks if it matches with local registers copy */ bool LMS7002M::IsSynced() { if (controlPort->IsOpen() == false) return false; bool isSynced = true; liblms7_status status; uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); vector<uint16_t> addrToRead = mRegistersMap->GetUsedAddresses(0); vector<uint16_t> dataReceived; dataReceived.resize(addrToRead.size(), 0); Modify_SPI_Reg_bits(LMS7param(MAC), 1); status = SPI_read_batch(&addrToRead[0], &dataReceived[0], addrToRead.size()); if (status != LIBLMS7_SUCCESS) { isSynced = false; goto isSyncedEnding; } //mask out readonly bits for (uint16_t j = 0; j < sizeof(readOnlyRegisters) / sizeof(uint16_t); ++j) for (uint16_t k = 0; k < addrToRead.size(); ++k) if (readOnlyRegisters[j] == addrToRead[k]) { dataReceived[k] &= readOnlyRegistersMasks[j]; break; } //check if local copy matches chip for (uint16_t i = 0; i < addrToRead.size(); ++i) { if (dataReceived[i] != mRegistersMap->GetValue(0, addrToRead[i])) { isSynced = false; goto isSyncedEnding; } } addrToRead.clear(); //add only B channel addresses addrToRead = mRegistersMap->GetUsedAddresses(1); //mask out readonly bits for (uint16_t j = 0; j < sizeof(readOnlyRegisters) / sizeof(uint16_t); ++j) for (uint16_t k = 0; k < addrToRead.size(); ++k) if (readOnlyRegisters[j] == addrToRead[k]) { dataReceived[k] &= readOnlyRegistersMasks[j]; break; } Modify_SPI_Reg_bits(LMS7param(MAC), 2); status = SPI_read_batch(&addrToRead[0], &dataReceived[0], addrToRead.size()); if (status != LIBLMS7_SUCCESS) return false; //check if local copy matches chip for (uint16_t i = 0; i < addrToRead.size(); ++i) if (dataReceived[i] != mRegistersMap->GetValue(1, addrToRead[i])) { isSynced = false; break; } isSyncedEnding: Modify_SPI_Reg_bits(LMS7param(MAC), ch); //restore previously used channel return isSynced; } /** @brief Writes all registers from host to chip When used on Novena board, also changes gpios to match rx path and tx band selections */ liblms7_status LMS7002M::UploadAll() { if (controlPort == NULL) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC)); //remember used channel liblms7_status status; vector<uint16_t> addrToWrite; vector<uint16_t> dataToWrite; uint16_t x0020_value = mRegistersMap->GetValue(0, 0x0020); Modify_SPI_Reg_bits(LMS7param(MAC), 1); //select A channel addrToWrite = mRegistersMap->GetUsedAddresses(0); //remove 0x0020 register from list, to not change MAC addrToWrite.erase( find(addrToWrite.begin(), addrToWrite.end(), 0x0020) ); for (auto address : addrToWrite) dataToWrite.push_back(mRegistersMap->GetValue(0, address)); status = SPI_write_batch(&addrToWrite[0], &dataToWrite[0], addrToWrite.size()); status = LIBLMS7_SUCCESS; if (status != LIBLMS7_SUCCESS) return status; //after all channel A registers have been written, update 0x0020 register value status = SPI_write(0x0020, x0020_value); if (status != LIBLMS7_SUCCESS) return status; Modify_SPI_Reg_bits(LMS7param(MAC), 2); if (status != LIBLMS7_SUCCESS) return status; addrToWrite = mRegistersMap->GetUsedAddresses(1); dataToWrite.clear(); for (auto address : addrToWrite) { dataToWrite.push_back(mRegistersMap->GetValue(1, address)); } Modify_SPI_Reg_bits(LMS7param(MAC), 2); //select B channel status = SPI_write_batch(&addrToWrite[0], &dataToWrite[0], addrToWrite.size()); if (status != LIBLMS7_SUCCESS) return status; Modify_SPI_Reg_bits(LMS7param(MAC), ch); //restore last used channel //in case of Novena board, need to update GPIO if(controlPort->GetInfo().device == LMS_DEV_NOVENA) { uint16_t regValue = SPI_read(0x0706) & 0xFFF8; //lms_gpio2 - tx output selection: // 0 - TX1_A and TX1_B (Band 1), // 1 - TX2_A and TX2_B (Band 2) regValue |= Get_SPI_Reg_bits(LMS7param(SEL_BAND2_TRF)) << 2; //gpio2 //RX active paths //lms_gpio0 | lms_gpio1 RX_A RX_B // 0 0 => no active path // 1 0 => LNAW_A LNAW_B // 0 1 => LNAH_A LNAH_B // 1 1 => LNAL_A LNAL_B switch(Get_SPI_Reg_bits(LMS7param(SEL_PATH_RFE))) { //set gpio1:gpio0 case 0: regValue |= 0x0; break; case 1: regValue |= 0x2; break; case 2: regValue |= 0x3; break; case 3: regValue |= 0x1; break; } SPI_write(0x0706, regValue); } return LIBLMS7_SUCCESS; } /** @brief Reads all registers from the chip to host When used on Novena board, also updates gpios to match rx path and tx band selections */ liblms7_status LMS7002M::DownloadAll() { if (controlPort == nullptr) return LIBLMS7_NO_CONNECTION_MANAGER; if (controlPort->IsOpen() == false) return LIBLMS7_NOT_CONNECTED; liblms7_status status; uint8_t ch = (uint8_t)Get_SPI_Reg_bits(LMS7param(MAC), false); vector<uint16_t> addrToRead = mRegistersMap->GetUsedAddresses(0); vector<uint16_t> dataReceived; dataReceived.resize(addrToRead.size(), 0); Modify_SPI_Reg_bits(LMS7param(MAC), 1); status = SPI_read_batch(&addrToRead[0], &dataReceived[0], addrToRead.size()); if (status != LIBLMS7_SUCCESS) return status; for (uint16_t i = 0; i < addrToRead.size(); ++i) { uint16_t adr = addrToRead[i]; uint16_t val = dataReceived[i]; mRegistersMap->SetValue(0, addrToRead[i], dataReceived[i]); } addrToRead.clear(); //add only B channel addresses addrToRead = mRegistersMap->GetUsedAddresses(1); dataReceived.resize(addrToRead.size(), 0); Modify_SPI_Reg_bits(LMS7param(MAC), 2); status = SPI_read_batch(&addrToRead[0], &dataReceived[0], addrToRead.size()); if (status != LIBLMS7_SUCCESS) return status; for (uint16_t i = 0; i < addrToRead.size(); ++i) mRegistersMap->SetValue(1, addrToRead[i], dataReceived[i]); Modify_SPI_Reg_bits(LMS7param(MAC), ch); //retore previously used channel //in case of Novena board, update GPIO if(controlPort->GetInfo().device == LMS_DEV_NOVENA) { uint16_t regValue = SPI_read(0x0706) & 0xFFF8; //lms_gpio2 - tx output selection: // 0 - TX1_A and TX1_B (Band 1), // 1 - TX2_A and TX2_B (Band 2) regValue |= Get_SPI_Reg_bits(LMS7param(SEL_BAND2_TRF)) << 2; //gpio2 //RX active paths //lms_gpio0 | lms_gpio1 RX_A RX_B // 0 0 => no active path // 1 0 => LNAW_A LNAW_B // 0 1 => LNAH_A LNAH_B // 1 1 => LNAL_A LNAL_B switch(Get_SPI_Reg_bits(LMS7param(SEL_PATH_RFE))) { //set gpio1:gpio0 case 0: regValue |= 0x0; break; case 1: regValue |= 0x2; break; case 2: regValue |= 0x3; break; case 3: regValue |= 0x1; break; } SPI_write(0x0706, regValue); } return LIBLMS7_SUCCESS; } /** @brief Configures interfaces for desired frequency Sets interpolation and decimation, changes MCLK sources and TSP clock dividers accordingly to selected interpolation and decimation */ liblms7_status LMS7002M::SetInterfaceFrequency(float_type cgen_freq_MHz, const uint8_t interpolation, const uint8_t decimation) { Modify_SPI_Reg_bits(LMS7param(HBD_OVR_RXTSP), decimation); Modify_SPI_Reg_bits(LMS7param(HBI_OVR_TXTSP), interpolation); liblms7_status status = SetFrequencyCGEN(cgen_freq_MHz); if (status != LIBLMS7_SUCCESS) return status; if (decimation == 7 || decimation == 0) //bypass { Modify_SPI_Reg_bits(LMS7param(RXTSPCLKA_DIV), 0); Modify_SPI_Reg_bits(LMS7param(RXDIVEN), false); Modify_SPI_Reg_bits(LMS7param(MCLK2SRC), 3); } else { uint8_t divider = (uint8_t)pow(2.0, decimation); if (divider > 1) Modify_SPI_Reg_bits(LMS7param(RXTSPCLKA_DIV), (divider / 2) - 1); else Modify_SPI_Reg_bits(LMS7param(RXTSPCLKA_DIV), 0); Modify_SPI_Reg_bits(LMS7param(RXDIVEN), true); Modify_SPI_Reg_bits(LMS7param(MCLK2SRC), 1); } if (interpolation == 7 || interpolation == 0) //bypass { Modify_SPI_Reg_bits(LMS7param(TXTSPCLKA_DIV), 0); Modify_SPI_Reg_bits(LMS7param(TXDIVEN), false); Modify_SPI_Reg_bits(LMS7param(MCLK1SRC), 2); } else { uint8_t divider = (uint8_t)pow(2.0, interpolation); if (divider > 1) Modify_SPI_Reg_bits(LMS7param(TXTSPCLKA_DIV), (divider / 2) - 1); else Modify_SPI_Reg_bits(LMS7param(TXTSPCLKA_DIV), 0); Modify_SPI_Reg_bits(LMS7param(TXDIVEN), true); Modify_SPI_Reg_bits(LMS7param(MCLK1SRC), 0); } return status; }