Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / dboard / db_dbsrx2.cpp @ 634a331f

History | View | Annotate | Download (14.9 KB)

1
//
2
// Copyright 2010 Ettus Research LLC
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

    
18
// No RX IO Pins Used
19

    
20
#include "max2112_regs.hpp"
21
#include <uhd/utils/static.hpp>
22
#include <uhd/utils/assert.hpp>
23
#include <uhd/utils/algorithm.hpp>
24
#include <uhd/types/ranges.hpp>
25
#include <uhd/types/dict.hpp>
26
#include <uhd/usrp/subdev_props.hpp>
27
#include <uhd/usrp/dboard_base.hpp>
28
#include <uhd/usrp/dboard_manager.hpp>
29
#include <boost/assign/list_of.hpp>
30
#include <boost/format.hpp>
31
#include <boost/thread.hpp>
32
#include <boost/math/special_functions/round.hpp>
33
#include <utility>
34

    
35
using namespace uhd;
36
using namespace uhd::usrp;
37
using namespace boost::assign;
38

    
39
/***********************************************************************
40
 * The DBSRX2 constants
41
 **********************************************************************/
42
static const bool dbsrx2_debug = false;
43

    
44
static const freq_range_t dbsrx2_freq_range(0.8e9, 2.4e9);
45

    
46
static const int dbsrx2_ref_divider = 4; // Hitachi HMC426 divider (U7)
47

    
48
static const prop_names_t dbsrx2_antennas = list_of("J3");
49

    
50
static const uhd::dict<std::string, gain_range_t> dbsrx2_gain_ranges = map_list_of
51
    ("GC1", gain_range_t(0, 73, 0.05))
52
    ("BBG", gain_range_t(0, 15, 1))
53
;
54

    
55
/***********************************************************************
56
 * The DBSRX2 dboard class
57
 **********************************************************************/
58
class dbsrx2 : public rx_dboard_base{
59
public:
60
    dbsrx2(ctor_args_t args, boost::uint8_t max2112_addr);
61
    ~dbsrx2(void);
62

    
63
    void rx_get(const wax::obj &key, wax::obj &val);
64
    void rx_set(const wax::obj &key, const wax::obj &val);
65

    
66
private:
67
    double _lo_freq;
68
    float _bandwidth;
69
    uhd::dict<std::string, float> _gains;
70
    max2112_write_regs_t _max2112_write_regs;
71
    max2112_read_regs_t _max2112_read_regs;
72
    boost::uint8_t _max2112_addr; //0x60 or 0x61 depending on which side
73

    
74
    void set_lo_freq(double target_freq);
75
    void set_gain(float gain, const std::string &name);
76
    void set_bandwidth(float bandwidth);
77

    
78
    void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){
79
        start_reg = boost::uint8_t(std::clip(int(start_reg), 0x0, 0xB));
80
        stop_reg = boost::uint8_t(std::clip(int(stop_reg), 0x0, 0xB));
81

    
82
        for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t) - 1){
83
            int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) - 1 ? sizeof(boost::uint32_t) - 1 : stop_reg - start_addr + 1;
84

    
85
            //create buffer for register data (+1 for start address)
86
            byte_vector_t regs_vector(num_bytes + 1);
87

    
88
            //first byte is the address of first register
89
            regs_vector[0] = start_addr;
90

    
91
            //get the register data
92
            for(int i=0; i<num_bytes; i++){
93
                regs_vector[1+i] = _max2112_write_regs.get_reg(start_addr+i);
94
                if(dbsrx2_debug) std::cerr << boost::format(
95
                    "DBSRX2: send reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
96
                ) % int(start_addr+i) % int(regs_vector[1+i]) % int(start_addr) % num_bytes << std::endl;
97
            }
98

    
99
            //send the data
100
            this->get_iface()->write_i2c(
101
                _max2112_addr, regs_vector
102
            );
103
        }
104
    }
105

    
106
    void read_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){
107
        static const boost::uint8_t status_addr = 0xC;
108
        start_reg = boost::uint8_t(std::clip(int(start_reg), 0x0, 0xD));
109
        stop_reg = boost::uint8_t(std::clip(int(stop_reg), 0x0, 0xD));
110

    
111
        for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t)){
112
            int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) ? sizeof(boost::uint32_t) : stop_reg - start_addr + 1;
113

    
114
            //create address to start reading register data
115
            byte_vector_t address_vector(1);
116
            address_vector[0] = start_addr;
117

    
118
            //send the address
119
            this->get_iface()->write_i2c(
120
                _max2112_addr, address_vector
121
            );
122

    
123
            //create buffer for register data
124
            byte_vector_t regs_vector(num_bytes);
125

    
126
            //read from i2c
127
            regs_vector = this->get_iface()->read_i2c(
128
                _max2112_addr, num_bytes
129
            );
130

    
131
            for(boost::uint8_t i=0; i < num_bytes; i++){
132
                if (i + start_addr >= status_addr){
133
                    _max2112_read_regs.set_reg(i + start_addr, regs_vector[i]);
134
                    /*
135
                    if(dbsrx2_debug) std::cerr << boost::format(
136
                        "DBSRX2: set reg 0x%02x, value 0x%04x"
137
                    ) % int(i + start_addr) % int(_max2112_read_regs.get_reg(i + start_addr)) << std::endl;
138
                    */
139
                }
140
                if(dbsrx2_debug) std::cerr << boost::format(
141
                    "DBSRX2: read reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
142
                ) % int(start_addr+i) % int(regs_vector[i]) % int(start_addr) % num_bytes << std::endl;
143
            }
144
        }
145
    }
146

    
147
    /*!
148
     * Is the LO locked?
149
     * \return true for locked
150
     */
151
    bool get_locked(void){
152
        read_reg(0xC, 0xD);
153

    
154
        //mask and return lock detect
155
        bool locked = _max2112_read_regs.ld & _max2112_read_regs.vasa & _max2112_read_regs.vase;
156

    
157
        if(dbsrx2_debug) std::cerr << boost::format(
158
            "DBSRX2 locked: %d"
159
        ) % locked << std::endl;
160

    
161
        return locked;
162
    }
163

    
164
};
165

    
166
/***********************************************************************
167
 * Register the DBSRX2 dboard
168
 **********************************************************************/
169
// FIXME 0x67 is the default i2c address on USRP2
170
//       need to handle which side for USRP1 with different address
171
static dboard_base::sptr make_dbsrx2(dboard_base::ctor_args_t args){
172
    return dboard_base::sptr(new dbsrx2(args, 0x61));
173
}
174

    
175
UHD_STATIC_BLOCK(reg_dbsrx2_dboard){
176
    //register the factory function for the rx dbid
177
    dboard_manager::register_dboard(0x0012, &make_dbsrx2, "DBSRX2");
178
}
179

    
180
/***********************************************************************
181
 * Structors
182
 **********************************************************************/
183
dbsrx2::dbsrx2(ctor_args_t args, boost::uint8_t max2112_addr) : rx_dboard_base(args){
184
    //enable only the clocks we need
185
    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
186

    
187
    //set the gpio directions and atr controls (identically)
188
    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr
189
    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
190

    
191
    //set the i2c address for the max2112
192
    _max2112_addr = max2112_addr;
193

    
194
    //send initial register settings
195
    send_reg(0x0, 0xB);
196
    //for (boost::uint8_t addr=0; addr<=12; addr++) this->send_reg(addr, addr);
197

    
198
    //set defaults for LO, gains
199
    set_lo_freq(dbsrx2_freq_range.min);
200
    BOOST_FOREACH(const std::string &name, dbsrx2_gain_ranges.keys()){
201
        set_gain(dbsrx2_gain_ranges[name].min, name);
202
    }
203

    
204
    set_bandwidth(40e6); // default bandwidth from datasheet
205
    get_locked();
206

    
207
    _max2112_write_regs.bbg = int (std::clip<float>(boost::math::iround(0.0), dbsrx2_gain_ranges["BBG"].min, dbsrx2_gain_ranges["BBG"].max));
208
    send_reg(0x9, 0x9);
209
}
210

    
211
dbsrx2::~dbsrx2(void){
212
}
213

    
214

    
215
/***********************************************************************
216
 * Tuning
217
 **********************************************************************/
218
void dbsrx2::set_lo_freq(double target_freq){
219
    //target_freq = std::clip(target_freq, dbsrx2_freq_range.min, dbsrx2_freq_range.max);
220

    
221
    //variables used in the calculation below
222
    int scaler = target_freq > 1125e6 ? 2 : 4;
223
    double ref_freq = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
224
    int R, intdiv, fracdiv, ext_div;
225
    double N;
226

    
227
    //compute tuning variables
228
    ext_div = dbsrx2_ref_divider; // 12MHz < ref_freq/ext_divider < 30MHz
229

    
230
    R = 1; //Divide by 1 is the only tested value
231

    
232
    N = (target_freq*R*ext_div)/(ref_freq); //actual spec range is (19, 251)
233
    intdiv = int(std::floor(N)); //  if (intdiv < 19  or intdiv > 251) continue;
234
    fracdiv = boost::math::iround((N - intdiv)*double(1 << 20));
235

    
236
    //calculate the actual freq from the values above
237
    N = double(intdiv) + double(fracdiv)/double(1 << 20);
238
    _lo_freq = (N*ref_freq)/(R*ext_div);
239

    
240
    //load new counters into registers
241
    _max2112_write_regs.set_n_divider(intdiv);
242
    _max2112_write_regs.set_f_divider(fracdiv);
243
    _max2112_write_regs.r_divider = R;
244
    _max2112_write_regs.d24 = scaler == 4 ? max2112_write_regs_t::D24_DIV4 : max2112_write_regs_t::D24_DIV2;
245

    
246
    //debug output of calculated variables
247
    if (dbsrx2_debug) std::cerr
248
        << boost::format("DBSRX2 tune:\n")
249
        << boost::format("    R=%d, N=%f, scaler=%d, ext_div=%d\n") % R % N % scaler % ext_div
250
        << boost::format("    int=%d, frac=%d, d24=%d\n") % intdiv % fracdiv % int(_max2112_write_regs.d24)
251
        << boost::format("    Ref    Freq=%fMHz\n") % (ref_freq/1e6)
252
        << boost::format("    Target Freq=%fMHz\n") % (target_freq/1e6)
253
        << boost::format("    Actual Freq=%fMHz\n") % (_lo_freq/1e6)
254
        << std::endl;
255

    
256
    //send the registers
257
    send_reg(0x0, 0x7);
258

    
259
    //FIXME: probably unnecessary to call get_locked here
260
    //get_locked();
261

    
262
}
263

    
264
/***********************************************************************
265
 * Gain Handling
266
 **********************************************************************/
267
/*!
268
 * Convert a requested gain for the BBG vga into the integer register value.
269
 * The gain passed into the function will be set to the actual value.
270
 * \param gain the requested gain in dB
271
 * \return 4 bit the register value
272
 */
273
static int gain_to_bbg_vga_reg(float &gain){
274
    int reg = std::clip<float>(boost::math::iround(gain), dbsrx2_gain_ranges["BBG"].min, dbsrx2_gain_ranges["BBG"].max);
275

    
276
    gain = float(reg);
277

    
278
    if (dbsrx2_debug) std::cerr 
279
        << boost::format("DBSRX2 BBG Gain:\n")
280
        << boost::format("    %f dB, bbg: %d") % gain % reg 
281
        << std::endl;
282

    
283
    return reg;
284
}
285

    
286
/*!
287
 * Convert a requested gain for the GC1 rf vga into the dac_volts value.
288
 * The gain passed into the function will be set to the actual value.
289
 * \param gain the requested gain in dB
290
 * \return dac voltage value
291
 */
292
static float gain_to_gc1_rfvga_dac(float &gain){
293
    //clip the input
294
    gain = std::clip<float>(gain, dbsrx2_gain_ranges["GC1"].min, dbsrx2_gain_ranges["GC1"].max);
295

    
296
    //voltage level constants
297
    static const float max_volts = float(0.5), min_volts = float(2.7);
298
    static const float slope = (max_volts-min_volts)/dbsrx2_gain_ranges["GC1"].max;
299

    
300
    //calculate the voltage for the aux dac
301
    float dac_volts = gain*slope + min_volts;
302

    
303
    if (dbsrx2_debug) std::cerr 
304
        << boost::format("DBSRX2 GC1 Gain:\n")
305
        << boost::format("    %f dB, dac_volts: %f V") % gain % dac_volts 
306
        << std::endl;
307

    
308
    //the actual gain setting
309
    gain = (dac_volts - min_volts)/slope;
310

    
311
    return dac_volts;
312
}
313

    
314
void dbsrx2::set_gain(float gain, const std::string &name){
315
    assert_has(dbsrx2_gain_ranges.keys(), name, "dbsrx2 gain name");
316
    if (name == "BBG"){
317
        _max2112_write_regs.bbg = gain_to_bbg_vga_reg(gain);
318
        send_reg(0x9, 0x9);
319
    }
320
    else if(name == "GC1"){
321
        //write the new voltage to the aux dac
322
        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, gain_to_gc1_rfvga_dac(gain));
323
    }
324
    else UHD_THROW_INVALID_CODE_PATH();
325
    _gains[name] = gain;
326
}
327

    
328
/***********************************************************************
329
 * Bandwidth Handling
330
 **********************************************************************/
331
void dbsrx2::set_bandwidth(float bandwidth){
332
    //clip the input
333
    bandwidth = std::clip<float>(bandwidth, 4e6, 40e6);
334
    
335
    _max2112_write_regs.lp = int((bandwidth/1e6 - 4)/0.29 + 12);
336
    _bandwidth = float(4 + (_max2112_write_regs.lp - 12) * 0.29)*1e6;
337

    
338
    if (dbsrx2_debug) std::cerr 
339
        << boost::format("DBSRX2 Bandwidth:\n")
340
        << boost::format("    %f MHz, lp: %f V") % (_bandwidth/1e6) % int(_max2112_write_regs.lp)
341
        << std::endl;
342

    
343
    this->send_reg(0x8, 0x8);
344
}
345

    
346
/***********************************************************************
347
 * RX Get and Set
348
 **********************************************************************/
349
void dbsrx2::rx_get(const wax::obj &key_, wax::obj &val){
350
    named_prop_t key = named_prop_t::extract(key_);
351

    
352
    //handle the get request conditioned on the key
353
    switch(key.as<subdev_prop_t>()){
354
    case SUBDEV_PROP_NAME:
355
        val = get_rx_id().to_pp_string();
356
        return;
357

    
358
    case SUBDEV_PROP_OTHERS:
359
        val = prop_names_t(); //empty
360
        return;
361

    
362
    case SUBDEV_PROP_GAIN:
363
        assert_has(_gains.keys(), key.name, "dbsrx2 gain name");
364
        val = _gains[key.name];
365
        return;
366

    
367
    case SUBDEV_PROP_GAIN_RANGE:
368
        assert_has(dbsrx2_gain_ranges.keys(), key.name, "dbsrx2 gain name");
369
        val = dbsrx2_gain_ranges[key.name];
370
        return;
371

    
372
    case SUBDEV_PROP_GAIN_NAMES:
373
        val = prop_names_t(dbsrx2_gain_ranges.keys());
374
        return;
375

    
376
    case SUBDEV_PROP_FREQ:
377
        val = _lo_freq;
378
        return;
379

    
380
    case SUBDEV_PROP_FREQ_RANGE:
381
        val = dbsrx2_freq_range;
382
        return;
383

    
384
    case SUBDEV_PROP_ANTENNA:
385
        val = std::string("J3");
386
        return;
387

    
388
    case SUBDEV_PROP_ANTENNA_NAMES:
389
        val = dbsrx2_antennas;
390
        return;
391

    
392
    case SUBDEV_PROP_CONNECTION:
393
        val = SUBDEV_CONN_COMPLEX_QI;
394
        return;
395

    
396
    case SUBDEV_PROP_ENABLED:
397
        val = true; //always enabled
398
        return;
399

    
400
    case SUBDEV_PROP_USE_LO_OFFSET:
401
        val = false;
402
        return;
403

    
404
    case SUBDEV_PROP_LO_LOCKED:
405
        val = this->get_locked();
406
        return;
407

    
408
    case SUBDEV_PROP_BANDWIDTH:
409
        val = _bandwidth;
410
        return;
411

    
412
    default: UHD_THROW_PROP_GET_ERROR();
413
    }
414
}
415

    
416
void dbsrx2::rx_set(const wax::obj &key_, const wax::obj &val){
417
    named_prop_t key = named_prop_t::extract(key_);
418

    
419
    //handle the get request conditioned on the key
420
    switch(key.as<subdev_prop_t>()){
421

    
422
    case SUBDEV_PROP_FREQ:
423
        this->set_lo_freq(val.as<double>());
424
        return;
425

    
426
    case SUBDEV_PROP_GAIN:
427
        this->set_gain(val.as<float>(), key.name);
428
        return;
429

    
430
    case SUBDEV_PROP_ENABLED:
431
        return; //always enabled
432

    
433
    case SUBDEV_PROP_BANDWIDTH:
434
        this->set_bandwidth(val.as<float>());
435
        return;
436

    
437
    default: UHD_THROW_PROP_SET_ERROR();
438
    }
439
}
440