mirror of
				https://github.com/fairwaves/UHD-Fairwaves.git
				synced 2025-11-03 21:43:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			460 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			460 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2012-2013 Fairwaves LLC
 | 
						||
//
 | 
						||
// This program is free software: you can redistribute it and/or modify
 | 
						||
// it under the terms of the GNU General Public License as published by
 | 
						||
// the Free Software Foundation, either version 3 of the License, or
 | 
						||
// (at your option) any later version.
 | 
						||
//
 | 
						||
// This program is distributed in the hope that it will be useful,
 | 
						||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						||
// GNU General Public License for more details.
 | 
						||
//
 | 
						||
// You should have received a copy of the GNU General Public License
 | 
						||
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						||
//
 | 
						||
 | 
						||
#include "lms6002d.hpp"
 | 
						||
#include <boost/thread.hpp>
 | 
						||
 | 
						||
#define usleep(x) boost::this_thread::sleep(boost::posix_time::microseconds(x))
 | 
						||
 | 
						||
static int verbosity = 0;
 | 
						||
 | 
						||
/************************************************************************/
 | 
						||
/* LMS6002D Control Class implementation                                */
 | 
						||
/************************************************************************/
 | 
						||
 | 
						||
void lms6002d_dev::dump()
 | 
						||
{
 | 
						||
    for (int i = 0; i < 128; i++) {
 | 
						||
        switch (i) {
 | 
						||
            case 0x0C:
 | 
						||
            case 0x0D:
 | 
						||
            case 0x37:
 | 
						||
            case 0x38:
 | 
						||
            case 0x39:
 | 
						||
            case 0x3A:
 | 
						||
            case 0x3B:
 | 
						||
            case 0x3C:
 | 
						||
            case 0x3D:
 | 
						||
            case 0x69:
 | 
						||
            case 0x6A:
 | 
						||
            case 0x6B:
 | 
						||
            case 0x6C:
 | 
						||
            case 0x6D:
 | 
						||
                continue;
 | 
						||
        }
 | 
						||
        printf("reg[0x%02x] = 0x%02x\n", i, read_reg(i));
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
double lms6002d_dev::txrx_pll_tune(uint8_t reg, double ref_clock, double out_freq)
 | 
						||
{
 | 
						||
    // Supported frequency ranges and corresponding FREQSEL values.
 | 
						||
    static const struct vco_sel { double fmin; double fmax; int8_t value; } freqsel[] = {
 | 
						||
        { 0.2325e9,   0.285625e9, 0x27 },
 | 
						||
        { 0.285625e9, 0.336875e9, 0x2f },
 | 
						||
        { 0.336875e9, 0.405e9,    0x37 },
 | 
						||
        { 0.405e9,    0.465e9,    0x3f },
 | 
						||
        { 0.465e9,    0.57125e9,  0x26 },
 | 
						||
        { 0.57125e9,  0.67375e9,  0x2e },
 | 
						||
        { 0.67375e9,  0.81e9,     0x36 },
 | 
						||
        { 0.81e9,     0.93e9,     0x3e },
 | 
						||
        { 0.93e9,     1.1425e9,   0x25 },
 | 
						||
        { 1.1425e9,   1.3475e9,   0x2d },
 | 
						||
        { 1.3475e9,   1.62e9,     0x35 },
 | 
						||
        { 1.62e9,     1.86e9,     0x3d },
 | 
						||
        { 1.86e9,     2.285e9,    0x24 },
 | 
						||
        { 2.285e9,    2.695e9,    0x2c },
 | 
						||
        { 2.695e9,    3.24e9,     0x34 },
 | 
						||
        { 3.24e9,     3.72e9,     0x3c },
 | 
						||
    };
 | 
						||
 | 
						||
    if (verbosity>0) printf("lms6002d_dev::txrx_pll_tune(ref_clock=%f, out_freq=%f)\n", ref_clock, out_freq);
 | 
						||
 | 
						||
    // Find frequency range and FREQSEL for the given frequency
 | 
						||
    int8_t found_freqsel = -1;
 | 
						||
    for (unsigned i = 0; i < (int)sizeof(freqsel) / sizeof(freqsel[0]); i++) {
 | 
						||
        if (out_freq > freqsel[i].fmin && out_freq <= freqsel[i].fmax) {
 | 
						||
            found_freqsel = freqsel[i].value; break;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    if (found_freqsel == -1)
 | 
						||
    {
 | 
						||
        // Unsupported frequency range
 | 
						||
        return -1;
 | 
						||
    }
 | 
						||
 | 
						||
    // Calculate NINT, NFRAC
 | 
						||
    int64_t vco_x = 1 << ((found_freqsel & 0x7) - 3);
 | 
						||
    int64_t nint = vco_x * out_freq / ref_clock;
 | 
						||
    int64_t nfrac = (1 << 23) * (vco_x * out_freq - nint * ref_clock) / ref_clock;
 | 
						||
    double actual_freq = (nint + nfrac/double(1<<23)) * (ref_clock/vco_x);
 | 
						||
 | 
						||
    // DEBUG
 | 
						||
    if (verbosity>0) printf("FREQSEL=%d VCO_X=%d NINT=%d  NFRAC=%d ACTUAL_FREQ=%f\n\n", (int)found_freqsel, (int)vco_x, (int)nint, (int)nfrac, actual_freq);
 | 
						||
 | 
						||
    // Write NINT, NFRAC
 | 
						||
    write_reg(reg + 0x0, (nint >> 1) & 0xff);    // NINT[8:1]
 | 
						||
    write_reg(reg + 0x1, ((nfrac >> 16) & 0x7f) | ((nint & 0x1) << 7)); //NINT[0] nfrac[22:16]
 | 
						||
    write_reg(reg + 0x2, (nfrac >> 8) & 0xff);  // NFRAC[15:8]
 | 
						||
    write_reg(reg + 0x3, (nfrac) & 0xff);     // NFRAC[7:0]
 | 
						||
    // Write FREQSEL
 | 
						||
    lms_write_bits(reg + 0x5, (0x3f << 2), (found_freqsel << 2)); // FREQSEL[5:0]
 | 
						||
    // Reset VOVCOREG, OFFDOWN to default
 | 
						||
    // -- I think this is not needed here, as it changes settings which
 | 
						||
    //    we may want to set beforehand.
 | 
						||
//    write_reg(reg + 0x8, 0x40); // VOVCOREG[3:1] OFFDOWN[4:0]
 | 
						||
//    write_reg(reg + 0x9, 0x94); // VOVCOREG[0] VCOCAP[5:0]
 | 
						||
 | 
						||
    // DEBUG
 | 
						||
    //reg_dump();
 | 
						||
 | 
						||
    // Poll VOVCO
 | 
						||
    int start_i = -1;
 | 
						||
    int stop_i = -1;
 | 
						||
    enum State { VCO_HIGH, VCO_NORM, VCO_LOW } state = VCO_HIGH;
 | 
						||
    for (int i = 0; i < 64; i++) {
 | 
						||
        // Update VCOCAP
 | 
						||
        lms_write_bits(reg + 0x9, 0x3f, i);
 | 
						||
        usleep(50);
 | 
						||
 | 
						||
        int comp = read_reg(reg + 0x0a);
 | 
						||
        switch (comp >> 6) {
 | 
						||
        case 0x02: //HIGH
 | 
						||
            break;
 | 
						||
        case 0x01: //LOW
 | 
						||
            if (state == VCO_NORM) {
 | 
						||
                stop_i = i - 1;
 | 
						||
                state = VCO_LOW;
 | 
						||
                if (verbosity>1) printf("Low\n");
 | 
						||
            }
 | 
						||
            break;
 | 
						||
        case 0x00: //NORMAL
 | 
						||
            if (state == VCO_HIGH) {
 | 
						||
                start_i = i;
 | 
						||
                state = VCO_NORM;
 | 
						||
                if (verbosity>1) printf("Norm\n");
 | 
						||
            }
 | 
						||
            break;
 | 
						||
        default: //ERROR
 | 
						||
            printf("ERROR: Incorrect VCOCAP reading while tuning\n");
 | 
						||
            return -1;
 | 
						||
        }
 | 
						||
        if (verbosity>1) printf("VOVCO[%d]=%x\n", i, (comp>>6));
 | 
						||
    }
 | 
						||
    if (VCO_NORM == state)
 | 
						||
        stop_i = 63;
 | 
						||
 | 
						||
    if (start_i == -1 || stop_i == -1) {
 | 
						||
        printf("ERROR: Can't find VCOCAP value while tuning\n");
 | 
						||
        return -1;
 | 
						||
    }
 | 
						||
 | 
						||
    // Tune to the middle of the found VCOCAP range
 | 
						||
    int avg_i = (start_i + stop_i) / 2;
 | 
						||
    if (verbosity>0) printf("START=%d STOP=%d SET=%d\n", start_i, stop_i, avg_i);
 | 
						||
    lms_write_bits(reg + 0x09, 0x3f, avg_i);
 | 
						||
 | 
						||
    // Return actual frequency we've tuned to
 | 
						||
    return actual_freq;
 | 
						||
}
 | 
						||
 | 
						||
void lms6002d_dev::init()
 | 
						||
{
 | 
						||
    if (verbosity>0) printf("lms6002d_dev::init()\n");
 | 
						||
    write_reg(0x09, 0x00); // RXOUTSW (disabled), CLK_EN (all disabled)
 | 
						||
    write_reg(0x17, 0xE0);
 | 
						||
    write_reg(0x27, 0xE3);
 | 
						||
    write_reg(0x70, 0x01);
 | 
						||
 | 
						||
    // FAQ v1.0r12, 5.27:
 | 
						||
    write_reg(0x47, 0x40); // Improving Tx spurious emission performance
 | 
						||
    write_reg(0x59, 0x29); // Improving ADC’s performance
 | 
						||
    write_reg(0x64, 0x36); // Common Mode Voltage For ADC’s
 | 
						||
    write_reg(0x79, 0x37); // Higher LNA Gain
 | 
						||
 | 
						||
    // Power down DC comparators to improve the receiver linearity
 | 
						||
    // (see FAQ v1.0r12, 5.26)
 | 
						||
    lms_set_bits(0x6E, (0x3 << 6));
 | 
						||
    lms_set_bits(0x5F, (0x1 << 7));
 | 
						||
 | 
						||
    // Disable AUX PA
 | 
						||
    // PA_EN[0]:AUXPA = 0 (powered up) - for mask set v1
 | 
						||
    // PD_DRVAUX = 0 (powered up) - for mask set v0,  test mode only
 | 
						||
    lms_set_bits(0x44, (3 << 1));
 | 
						||
 | 
						||
    // Icp=0.2mA
 | 
						||
    // This gives much better results for the GMSK modulation
 | 
						||
    lms_write_bits(0x16, 0x1f, 0x02);
 | 
						||
}
 | 
						||
 | 
						||
void lms6002d_dev::set_txrx_polarity_and_interleaving(int rx_fsync_polarity,
 | 
						||
                                                      txrx_interleaving rx_interleaving,
 | 
						||
                                                      int tx_fsync_polarity,
 | 
						||
                                                      txrx_interleaving tx_interleaving)
 | 
						||
{
 | 
						||
    uint8_t data = (rx_fsync_polarity << 7)
 | 
						||
                 | ((rx_interleaving==INTERLEAVE_IQ)?0:(1 << 6))
 | 
						||
                 | (tx_fsync_polarity << 4)
 | 
						||
                 | ((tx_interleaving==INTERLEAVE_IQ)?0:(1 << 3));
 | 
						||
    lms_write_bits(0x5A, 0xD8, data);
 | 
						||
}
 | 
						||
 | 
						||
int lms6002d_dev::general_dc_calibration_loop(uint8_t dc_addr, uint8_t calibration_reg_base)
 | 
						||
{
 | 
						||
    uint8_t reg_val;
 | 
						||
    int try_cnt_limit = 10;
 | 
						||
    uint8_t DC_REGVAL = 0;
 | 
						||
 | 
						||
    if (verbosity > 0) printf("DC Offset Calibration for addr %d:\n", dc_addr);
 | 
						||
    reg_val = read_reg(calibration_reg_base+0x03);
 | 
						||
    // DC_ADDR := ADDR
 | 
						||
    reg_val = (reg_val & 0xf8) | dc_addr;
 | 
						||
    write_reg(calibration_reg_base+0x03, reg_val);
 | 
						||
    // DC_START_CLBR := 1
 | 
						||
    reg_val = reg_val | (1 << 5);
 | 
						||
    write_reg(calibration_reg_base+0x03, reg_val);
 | 
						||
    // DC_START_CLBR := 0
 | 
						||
    reg_val = reg_val ^ (1 << 5);
 | 
						||
    write_reg(calibration_reg_base+0x03, reg_val);
 | 
						||
 | 
						||
    while (try_cnt_limit--)
 | 
						||
    {
 | 
						||
        if (verbosity > 1) printf("cnt=%d\n", try_cnt_limit);
 | 
						||
 | 
						||
        // Wait for 6.4(1.6) us
 | 
						||
        usleep(6.4);
 | 
						||
 | 
						||
        // Read DC_CLBR_DONE
 | 
						||
        reg_val  = read_reg(calibration_reg_base+0x01);
 | 
						||
        int DC_CLBR_DONE = (reg_val >> 1) & 0x1;
 | 
						||
        if (verbosity > 1) printf(" DC_CLBR_DONE=%d\n", DC_CLBR_DONE);
 | 
						||
 | 
						||
        // DC_CLBR_DONE == 1?
 | 
						||
        if (DC_CLBR_DONE == 1)
 | 
						||
            continue;
 | 
						||
 | 
						||
        // Read DC_LOCK
 | 
						||
        reg_val  = read_reg(calibration_reg_base+0x01);
 | 
						||
        int DC_LOCK = (reg_val >> 2) & 0x7;
 | 
						||
        if (verbosity > 1) printf(" DC_LOCK=%d\n", DC_LOCK);
 | 
						||
 | 
						||
        // Read DC_REGVAL
 | 
						||
        DC_REGVAL = read_reg(calibration_reg_base+0x00);
 | 
						||
        if (verbosity > 1) printf("DC_REGVAL = %d\n", DC_REGVAL);
 | 
						||
 | 
						||
        // DC_LOCK != 0 or 7? We're done.
 | 
						||
        if (DC_LOCK != 0 && DC_LOCK != 7) break;
 | 
						||
    }
 | 
						||
 | 
						||
    // Return the value
 | 
						||
    return DC_REGVAL;
 | 
						||
}
 | 
						||
 | 
						||
int lms6002d_dev::general_dc_calibration(uint8_t dc_addr, uint8_t calibration_reg_base)
 | 
						||
{
 | 
						||
    // Power up DC comparators
 | 
						||
    lms_clear_bits(0x6E, (0x3 << 6));
 | 
						||
    lms_clear_bits(0x5F, (0x1 << 7));
 | 
						||
 | 
						||
    // Set DC_REGVAL to 31
 | 
						||
    write_reg(calibration_reg_base+0x00, 31);
 | 
						||
    // Run the calibration first time
 | 
						||
    int DC_REGVAL = general_dc_calibration_loop(dc_addr, calibration_reg_base);
 | 
						||
    // Unchanged DC_REGVAL may mean either calibration failure or that '31' is
 | 
						||
    // the best calibration vaue. We're going to re-check that.
 | 
						||
    if (31 == DC_REGVAL)
 | 
						||
    {
 | 
						||
        // Set DC_REGVAL to a value other then 31, e.g. 0
 | 
						||
        write_reg(calibration_reg_base+0x00, 0);
 | 
						||
        // Retry the calibration
 | 
						||
        DC_REGVAL = general_dc_calibration_loop(dc_addr, calibration_reg_base);
 | 
						||
        // If DC_REGVAL has been changed, then calibration succeeded.
 | 
						||
        if (0 == DC_REGVAL)
 | 
						||
        {
 | 
						||
            // PANIC: Algorithm does Not Converge!
 | 
						||
            // From LimeMicro FAQ: "[This] condition should not happen as this is being
 | 
						||
            // checked in our production test."
 | 
						||
            printf("Error: DC Offset Calibration does not converge!\n");
 | 
						||
            return -1;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    // Power down DC comparators to improve the receiver linearity
 | 
						||
    // (see FAQ v1.0r12, 5.26)
 | 
						||
    lms_set_bits(0x6E, (0x3 << 6));
 | 
						||
    lms_set_bits(0x5F, (0x1 << 7));
 | 
						||
 | 
						||
    if (verbosity > 0) printf("Successful DC Offset Calibration for register bank 0x%X, DC addr %d. Result: 0x%X\n",
 | 
						||
                              calibration_reg_base, dc_addr, DC_REGVAL);
 | 
						||
    return DC_REGVAL;
 | 
						||
}
 | 
						||
 | 
						||
bool lms6002d_dev::lpf_tuning_dc_calibration()
 | 
						||
{
 | 
						||
    bool result = false;
 | 
						||
    // Save TopSPI::CLK_EN[5] Register
 | 
						||
    // TopSPI::CLK_EN[5] := 1
 | 
						||
    uint8_t clk_en_save = read_reg(0x09);
 | 
						||
    lms_set_bits(0x09, (1 << 5));
 | 
						||
 | 
						||
    // Perform DC Calibration Procedure in TopSPI with ADDR := 0 and get Result
 | 
						||
    // DCCAL := TopSPI::DC_REGVAL
 | 
						||
    int DCCAL = general_dc_calibration(0, 0x0);
 | 
						||
    if (DCCAL >= 0)
 | 
						||
    {
 | 
						||
        // RxLPFSPI::DCO_DACCAL := DCCAL
 | 
						||
        lms_write_bits(0x35, 0x3f, DCCAL);
 | 
						||
        // TxLPFSPI::DCO_DACCAL := DCCAL
 | 
						||
        lms_write_bits(0x55, 0x3f, DCCAL);
 | 
						||
        // Success
 | 
						||
        result = true;
 | 
						||
    }
 | 
						||
 | 
						||
    // Restore TopSPI::CLK_EN[5] Register
 | 
						||
    write_reg(0x09, clk_en_save);
 | 
						||
 | 
						||
    return result;
 | 
						||
}
 | 
						||
 | 
						||
bool lms6002d_dev::txrx_lpf_dc_calibration(bool is_tx)
 | 
						||
{
 | 
						||
    bool result;
 | 
						||
 | 
						||
    // Determine base address for control registers
 | 
						||
    int control_reg_base = (is_tx)?0x30:0x50;
 | 
						||
 | 
						||
    // Save TopSPI::CLK_EN Register
 | 
						||
    // TopSPI::CLK_EN := 1
 | 
						||
    uint8_t clk_en_save = read_reg(0x09);
 | 
						||
    lms_set_bits(0x09, (is_tx)?(1 << 1):(1 << 3));
 | 
						||
 | 
						||
    // Perform DC Calibration Procedure in LPFSPI with ADDR := 0 (For channel I)
 | 
						||
    result = general_dc_calibration(0, control_reg_base) >= 0;
 | 
						||
    // Perform DC Calibration Procedure in LPFSPI with ADDR := 1 (For channel Q)
 | 
						||
    result = general_dc_calibration(1, control_reg_base) >= 0 && result;
 | 
						||
 | 
						||
    // Restore TopSPI::CLK_EN Register
 | 
						||
    write_reg(0x09, clk_en_save);
 | 
						||
 | 
						||
    return result;
 | 
						||
}
 | 
						||
 | 
						||
int lms6002d_dev::rxvga2_dc_calibration()
 | 
						||
{
 | 
						||
    bool result;
 | 
						||
 | 
						||
    // Set base address for control registers
 | 
						||
    uint8_t control_reg_base = 0x60;
 | 
						||
 | 
						||
    // Save TopSPI::CLK_EN Register
 | 
						||
    // TopSPI::CLK_EN := 1
 | 
						||
    uint8_t clk_en_save = read_reg(0x09);
 | 
						||
    lms_set_bits(0x09, (1 << 4));
 | 
						||
 | 
						||
    // Perform DC Calibration Procedure in RxVGA2SPI with ADDR := 0 (For DC Reference channel)
 | 
						||
    result = general_dc_calibration(0, control_reg_base) >= 0;
 | 
						||
    // Perform DC Calibration Procedure in RxVGA2SPI with ADDR := 1 (For VGA2A_I channel)
 | 
						||
    result = general_dc_calibration(1, control_reg_base) >= 0 && result;
 | 
						||
    // Perform DC Calibration Procedure in RxVGA2SPI with ADDR := 2 (For VGA2A_Q channel)
 | 
						||
    result = general_dc_calibration(2, control_reg_base) >= 0 && result;
 | 
						||
    // Perform DC Calibration Procedure in RxVGA2SPI with ADDR := 3 (For VGA2B_I channel)
 | 
						||
    result = general_dc_calibration(3, control_reg_base) >= 0 && result;
 | 
						||
    // Perform DC Calibration Procedure in RxVGA2SPI with ADDR := 4 (For VGA2B_Q channel)
 | 
						||
    result = general_dc_calibration(4, control_reg_base) >= 0 && result;
 | 
						||
 | 
						||
    // Restore TopSPI::CLK_EN Register
 | 
						||
    write_reg(0x09, clk_en_save);
 | 
						||
 | 
						||
    return result;
 | 
						||
}
 | 
						||
 | 
						||
void lms6002d_dev::lpf_bandwidth_tuning(int ref_clock, uint8_t lpf_bandwidth_code)
 | 
						||
{
 | 
						||
    // Save registers 0x05 and 0x09, because we will modify them during tx_enable()
 | 
						||
    uint8_t reg_save_05 = read_reg(0x05);
 | 
						||
    uint8_t reg_save_09 = read_reg(0x09);
 | 
						||
 | 
						||
    // Enable TxPLL and tune it to 320MHz
 | 
						||
    tx_enable();
 | 
						||
    tx_pll_tune(ref_clock, int(320e6));
 | 
						||
 | 
						||
    // Use 40MHz generatedFrom TxPLL: TopSPI::CLKSEL_LPFCAL := 0
 | 
						||
    // Power Up LPF tuning clock generation block: TopSPI::PD_CLKLPFCAL := 0
 | 
						||
    uint8_t reg_save_06 = read_reg(0x06);
 | 
						||
    lms_clear_bits(0x06, (1 << 3) | (1 << 2));
 | 
						||
 | 
						||
    // Set TopSPI::BWC_LPFCAL
 | 
						||
    // Set EN_CAL_LPFCAL := 1 (Block enabled)
 | 
						||
    uint8_t t = lms_write_bits(0x07, 0x8f, (1<<7)|lpf_bandwidth_code);
 | 
						||
    if (verbosity >= 3) printf("code = %x %x %x\n", lpf_bandwidth_code, t, read_reg(0x07));
 | 
						||
    // TopSPI::RST_CAL_LPFCAL := 1 (Rst Active)
 | 
						||
    lms_set_bits(0x06, 0x01);
 | 
						||
    // ...Delay 100ns...
 | 
						||
    // TopSPI::RST_CAL_LPFCAL := 0 (Rst Inactive)
 | 
						||
    lms_clear_bits(0x06, 0x01);
 | 
						||
    // RCCAL := TopSPI::RCCAL_LPFCAL
 | 
						||
    _lpf_rccal = read_reg(0x01) >> 5;
 | 
						||
    if (verbosity >= 3) printf("RCCAL = %d\n", _lpf_rccal);
 | 
						||
    // RxLPFSPI::RCCAL_LPF := RCCAL
 | 
						||
    lms_write_bits(0x56, (7 << 4), (_lpf_rccal << 4));
 | 
						||
    // TxLPFSPI::RCCAL_LPF := RCCAL
 | 
						||
    lms_write_bits(0x36, (7 << 4), (_lpf_rccal << 4));
 | 
						||
 | 
						||
    // Shut down calibration unit
 | 
						||
    // TopSPI::RST_CAL_LPFCAL := 1 (Rst Active)
 | 
						||
    lms_set_bits(0x06, 0x01);
 | 
						||
    // Set EN_CAL_LPFCAL := 0 (Block disabled)
 | 
						||
    lms_clear_bits(0x07, (1 << 7));
 | 
						||
 | 
						||
    // Restore registers 0x05, 0x06 and 0x09
 | 
						||
    write_reg(0x06, reg_save_06);
 | 
						||
    write_reg(0x05, reg_save_05);
 | 
						||
    write_reg(0x09, reg_save_09);
 | 
						||
}
 | 
						||
 | 
						||
void lms6002d_dev::auto_calibration(int ref_clock, int lpf_bandwidth_code)
 | 
						||
{
 | 
						||
    if (verbosity > 0) printf("LPF Tuning...\n");
 | 
						||
    lpf_tuning_dc_calibration();
 | 
						||
    if (verbosity > 0) printf("LPF Bandwidth Tuning...\n");
 | 
						||
    lpf_bandwidth_tuning(ref_clock, lpf_bandwidth_code);
 | 
						||
 | 
						||
    if (verbosity > 0) printf("Tx LPF DC calibration...\n");
 | 
						||
    txrx_lpf_dc_calibration(true);
 | 
						||
 | 
						||
    // Disable Rx
 | 
						||
    // We use this way of disabling Rx, because we have to leave
 | 
						||
    // RXMIX working. If we disable MIX, calibration will not fail,
 | 
						||
    // but DC cancellation will be a bit worse. And setting LNASEL_RXFE
 | 
						||
    // to 0 disables RXMIX. So instead of that we select LNA1 and then
 | 
						||
    // connect it to the internal termination resistor with
 | 
						||
    // IN1SEL_MIX_RXFE and RINEN_MIX_RXFE configuration bits.
 | 
						||
    uint8_t lna = get_rx_lna();
 | 
						||
    //   1. Select LNA1
 | 
						||
    set_rx_lna(1);
 | 
						||
    //   2. Connect LNA to external inputs.
 | 
						||
    //      IN1SEL_MIX_RXFE: Selects the input to the mixer
 | 
						||
    uint8_t reg_save_71 = read_reg(0x71);
 | 
						||
    lms_clear_bits(0x71, (1 << 7));
 | 
						||
    //   3. Enable internal termination resistor.
 | 
						||
    //      RINEN_MIX_RXFE: Termination resistor on external mixer input enable
 | 
						||
    uint8_t reg_save_7C = read_reg(0x7C);
 | 
						||
    lms_set_bits(0x7C, (1 << 2));
 | 
						||
    // Set RxVGA2 gain to max
 | 
						||
    uint8_t rx_vga2gain = set_rx_vga2gain(30);
 | 
						||
    // Calibrate!
 | 
						||
    if (verbosity > 0) printf("Rx LPF DC calibration...\n");
 | 
						||
    txrx_lpf_dc_calibration(false);
 | 
						||
    if (verbosity > 0) printf("RxVGA2 DC calibration...\n");
 | 
						||
    rxvga2_dc_calibration();
 | 
						||
 | 
						||
    // Restore saved values
 | 
						||
    set_rx_vga2gain(rx_vga2gain);
 | 
						||
    write_reg(0x71, reg_save_71);
 | 
						||
    write_reg(0x7C, reg_save_7C);
 | 
						||
    set_rx_lna(lna);
 | 
						||
}
 |