Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / usrp_e100 / codec_ctrl.cpp @ 15268898

History | View | Annotate | Download (11 KB)

1
//
2
// Copyright 2010-2011 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
#include "codec_ctrl.hpp"
19
#include "ad9862_regs.hpp"
20
#include <uhd/types/dict.hpp>
21
#include <uhd/exception.hpp>
22
#include <uhd/utils/algorithm.hpp>
23
#include <boost/cstdint.hpp>
24
#include <boost/tuple/tuple.hpp>
25
#include <boost/math/special_functions/round.hpp>
26
#include "usrp_e100_regs.hpp" //spi slave constants
27
#include <boost/assign/list_of.hpp>
28
#include <iostream>
29

    
30
using namespace uhd;
31

    
32
static const bool codec_debug = false;
33

    
34
const gain_range_t usrp_e100_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1));
35
const gain_range_t usrp_e100_codec_ctrl::rx_pga_gain_range(0, 20, 1);
36

    
37
/***********************************************************************
38
 * Codec Control Implementation
39
 **********************************************************************/
40
class usrp_e100_codec_ctrl_impl : public usrp_e100_codec_ctrl{
41
public:
42
    //structors
43
    usrp_e100_codec_ctrl_impl(usrp_e100_iface::sptr iface);
44
    ~usrp_e100_codec_ctrl_impl(void);
45

    
46
    //aux adc and dac control
47
    double read_aux_adc(aux_adc_t which);
48
    void write_aux_dac(aux_dac_t which, double volts);
49

    
50
    //pga gain control
51
    void set_tx_pga_gain(double);
52
    double get_tx_pga_gain(void);
53
    void set_rx_pga_gain(double, char);
54
    double get_rx_pga_gain(char);
55

    
56
private:
57
    usrp_e100_iface::sptr _iface;
58
    ad9862_regs_t _ad9862_regs;
59
    aux_adc_t _last_aux_adc_a, _last_aux_adc_b;
60
    void send_reg(boost::uint8_t addr);
61
    void recv_reg(boost::uint8_t addr);
62
};
63

    
64
/***********************************************************************
65
 * Codec Control Structors
66
 **********************************************************************/
67
usrp_e100_codec_ctrl_impl::usrp_e100_codec_ctrl_impl(usrp_e100_iface::sptr iface){
68
    _iface = iface;
69

    
70
    //soft reset
71
    _ad9862_regs.soft_reset = 1;
72
    this->send_reg(0);
73

    
74
    //initialize the codec register settings
75
    _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO;
76
    _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB;
77
    _ad9862_regs.soft_reset = 0;
78

    
79
    //setup rx side of codec
80
    _ad9862_regs.byp_buffer_a = 1;
81
    _ad9862_regs.byp_buffer_b = 1;
82
    _ad9862_regs.buffer_a_pd = 1;
83
    _ad9862_regs.buffer_b_pd = 1;
84
    _ad9862_regs.rx_pga_a = 0;//0x1f;  //TODO bring under api control
85
    _ad9862_regs.rx_pga_b = 0;//0x1f;  //TODO bring under api control
86
    _ad9862_regs.rx_twos_comp = 1;
87
    _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS;
88

    
89
    //setup tx side of codec
90
    _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH;
91
    _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED;
92
    _ad9862_regs.tx_retime = ad9862_regs_t::TX_RETIME_CLKOUT2;
93
    _ad9862_regs.tx_pga_gain = 199; //TODO bring under api control
94
    _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS;
95
    _ad9862_regs.interp = ad9862_regs_t::INTERP_2;
96
    _ad9862_regs.tx_twos_comp = 1;
97
    _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_BYPASS;
98
    _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS;
99
    _ad9862_regs.dac_a_coarse_gain = 0x3;
100
    _ad9862_regs.dac_b_coarse_gain = 0x3;
101
    _ad9862_regs.edges = ad9862_regs_t::EDGES_NORMAL;
102

    
103
    //setup the dll
104
    _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL;
105
    _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2;
106
    _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST;
107

    
108
    //write the register settings to the codec
109
    for (uint8_t addr = 0; addr <= 25; addr++){
110
        this->send_reg(addr);
111
    }
112

    
113
    //aux adc clock
114
    _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4;
115
    this->send_reg(34);
116
}
117

    
118
usrp_e100_codec_ctrl_impl::~usrp_e100_codec_ctrl_impl(void){
119
    //set aux dacs to zero
120
    this->write_aux_dac(AUX_DAC_A, 0);
121
    this->write_aux_dac(AUX_DAC_B, 0);
122
    this->write_aux_dac(AUX_DAC_C, 0);
123
    this->write_aux_dac(AUX_DAC_D, 0);
124

    
125
    //power down
126
    _ad9862_regs.all_rx_pd = 1;
127
    this->send_reg(1);
128
    _ad9862_regs.tx_digital_pd = 1;
129
    _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH;
130
    this->send_reg(8);
131
}
132

    
133
/***********************************************************************
134
 * Codec Control Gain Control Methods
135
 **********************************************************************/
136
static const int mtpgw = 255; //maximum tx pga gain word
137

    
138
void usrp_e100_codec_ctrl_impl::set_tx_pga_gain(double gain){
139
    int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start()));
140
    _ad9862_regs.tx_pga_gain = uhd::clip(gain_word, 0, mtpgw);
141
    this->send_reg(16);
142
}
143

    
144
double usrp_e100_codec_ctrl_impl::get_tx_pga_gain(void){
145
    return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start();
146
}
147

    
148
static const int mrpgw = 0x14; //maximum rx pga gain word
149

    
150
void usrp_e100_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){
151
    int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start()));
152
    gain_word = uhd::clip(gain_word, 0, mrpgw);
153
    switch(which){
154
    case 'A':
155
        _ad9862_regs.rx_pga_a = gain_word;
156
        this->send_reg(2);
157
        return;
158
    case 'B':
159
        _ad9862_regs.rx_pga_b = gain_word;
160
        this->send_reg(3);
161
        return;
162
    default: UHD_THROW_INVALID_CODE_PATH();
163
    }
164
}
165

    
166
double usrp_e100_codec_ctrl_impl::get_rx_pga_gain(char which){
167
    int gain_word;
168
    switch(which){
169
    case 'A': gain_word = _ad9862_regs.rx_pga_a; break;
170
    case 'B': gain_word = _ad9862_regs.rx_pga_b; break;
171
    default: UHD_THROW_INVALID_CODE_PATH();
172
    }
173
    return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start();
174
}
175

    
176
/***********************************************************************
177
 * Codec Control AUX ADC Methods
178
 **********************************************************************/
179
static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low){
180
    return double((boost::uint16_t(high) << 2) | low)*3.3/0x3ff;
181
}
182

    
183
double usrp_e100_codec_ctrl_impl::read_aux_adc(aux_adc_t which){
184
    //check to see if the switch needs to be set
185
    bool write_switch = false;
186
    switch(which){
187

    
188
    case AUX_ADC_A1:
189
    case AUX_ADC_A2:
190
        if (which != _last_aux_adc_a){
191
            _ad9862_regs.select_a = (which == AUX_ADC_A1)?
192
                ad9862_regs_t::SELECT_A_AUX_ADC1: ad9862_regs_t::SELECT_A_AUX_ADC2;
193
            _last_aux_adc_a = which;
194
            write_switch = true;
195
        }
196
        break;
197

    
198
    case AUX_ADC_B1:
199
    case AUX_ADC_B2:
200
        if (which != _last_aux_adc_b){
201
            _ad9862_regs.select_b = (which == AUX_ADC_B1)?
202
                ad9862_regs_t::SELECT_B_AUX_ADC1: ad9862_regs_t::SELECT_B_AUX_ADC2;
203
            _last_aux_adc_b = which;
204
            write_switch = true;
205
        }
206
        break;
207

    
208
    }
209

    
210
    //write the switch if it changed
211
    if(write_switch) this->send_reg(34);
212

    
213
    //map aux adcs to register values to read
214
    static const uhd::dict<aux_adc_t, boost::uint8_t> aux_dac_to_addr = boost::assign::map_list_of
215
        (AUX_ADC_A2, 26) (AUX_ADC_A1, 28)
216
        (AUX_ADC_B2, 30) (AUX_ADC_B1, 32)
217
    ;
218

    
219
    //read the value
220
    this->recv_reg(aux_dac_to_addr[which]+0);
221
    this->recv_reg(aux_dac_to_addr[which]+1);
222

    
223
    //return the value scaled to volts
224
    switch(which){
225
    case AUX_ADC_A1: return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0);
226
    case AUX_ADC_A2: return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0);
227
    case AUX_ADC_B1: return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0);
228
    case AUX_ADC_B2: return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0);
229
    }
230
    UHD_THROW_INVALID_CODE_PATH();
231
}
232

    
233
/***********************************************************************
234
 * Codec Control AUX DAC Methods
235
 **********************************************************************/
236
void usrp_e100_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts){
237
    //special case for aux dac d (aka sigma delta word)
238
    if (which == AUX_DAC_D){
239
        boost::uint16_t dac_word = uhd::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff);
240
        _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4);
241
        _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf);
242
        this->send_reg(42);
243
        this->send_reg(43);
244
        return;
245
    }
246

    
247
    //calculate the dac word for aux dac a, b, c
248
    boost::uint8_t dac_word = uhd::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff);
249

    
250
    //setup a lookup table for the aux dac params (reg ref, reg addr)
251
    typedef boost::tuple<boost::uint8_t*, boost::uint8_t> dac_params_t;
252
    uhd::dict<aux_dac_t, dac_params_t> aux_dac_to_params = boost::assign::map_list_of
253
        (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36))
254
        (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37))
255
        (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38))
256
    ;
257

    
258
    //set the aux dac register
259
    UHD_ASSERT_THROW(aux_dac_to_params.has_key(which));
260
    boost::uint8_t *reg_ref, reg_addr;
261
    boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which];
262
    *reg_ref = dac_word;
263
    this->send_reg(reg_addr);
264
}
265

    
266
/***********************************************************************
267
 * Codec Control SPI Methods
268
 **********************************************************************/
269
void usrp_e100_codec_ctrl_impl::send_reg(boost::uint8_t addr){
270
    boost::uint32_t reg = _ad9862_regs.get_write_reg(addr);
271
    if (codec_debug) std::cout << "codec control write reg: " << std::hex << reg << std::endl;
272
    _iface->transact_spi(
273
        UE_SPI_SS_AD9862,
274
        spi_config_t::EDGE_RISE,
275
        reg, 16, false /*no rb*/
276
    );
277
}
278

    
279
void usrp_e100_codec_ctrl_impl::recv_reg(boost::uint8_t addr){
280
    boost::uint32_t reg = _ad9862_regs.get_read_reg(addr);
281
    if (codec_debug) std::cout << "codec control read reg: " << std::hex << reg << std::endl;
282
    boost::uint32_t ret = _iface->transact_spi(
283
        UE_SPI_SS_AD9862,
284
        spi_config_t::EDGE_RISE,
285
        reg, 16, true /*rb*/
286
    );
287
    if (codec_debug) std::cout << "codec control read ret: " << std::hex << ret << std::endl;
288
    _ad9862_regs.set_reg(addr, boost::uint16_t(ret));
289
}
290

    
291
/***********************************************************************
292
 * Codec Control Make
293
 **********************************************************************/
294
usrp_e100_codec_ctrl::sptr usrp_e100_codec_ctrl::make(usrp_e100_iface::sptr iface){
295
    return sptr(new usrp_e100_codec_ctrl_impl(iface));
296
}