WBXNG: power on/off TXMOD and Synth/VCO with set_enable
[debian/gnuradio] / usrp / host / lib / db_wbxng.cc
1 //
2 // Copyright 2008,2009 Free Software Foundation, Inc.
3 //
4 // This file is part of GNU Radio
5 //
6 // GNU Radio is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either asversion 3, or (at your option)
9 // any later version.
10 //
11 // GNU Radio is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with GNU Radio; see the file COPYING.  If not, write to
18 // the Free Software Foundation, Inc., 51 Franklin Street,
19 // Boston, MA 02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <usrp/db_wbxng.h>
26 #include "db_wbxng_adf4350.h"
27 #include <db_base_impl.h>
28 #include <stdio.h>
29
30 // d'board i/o pin defs
31 // Tx and Rx have shared defs, but different i/o regs
32 #define ENABLE_5        (1 << 7)         // enables 5.0V power supply
33 #define ENABLE_33       (1 << 6)         // enables 3.3V supply
34 #define RX_TXN          (1 << 5)         // Tx only: T/R antenna switch for TX/RX port
35 #define RX2_RX1N        (1 << 5)         // Rx only: antenna switch between RX2 and TX/RX port
36 #define RXBB_EN         (1 << 4)
37 #define TXMOD_EN        (1 << 4)
38 #define PLL_CE          (1 << 3)
39 #define PLL_PDBRF       (1 << 2)
40 #define PLL_MUXOUT      (1 << 1)
41 #define PLL_LOCK_DETECT (1 << 0)
42
43 // RX Attenuator constants
44 #define ATTN_SHIFT      8
45 #define ATTN_MASK       (63 << ATTN_SHIFT)
46
47 wbxng_base::wbxng_base(usrp_basic_sptr _usrp, int which, int _power_on)
48   : db_base(_usrp, which), d_power_on(_power_on)
49 {
50   /*
51     @param usrp: instance of usrp.source_c
52     @param which: which side: 0 or 1 corresponding to side A or B respectively
53     @type which: int
54   */
55
56   usrp()->_write_oe(d_which, 0, 0xffff);   // turn off all outputs
57
58   d_first = true;
59   d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0;
60
61   _enable_refclk(false);                // disable refclk
62
63   set_auto_tr(false);
64 }
65
66 wbxng_base::~wbxng_base()
67 {
68   if (d_common)
69     delete d_common;
70 }
71
72 struct freq_result_t
73 wbxng_base::set_freq(double freq)
74 {
75   /*
76     @returns (ok, actual_baseband_freq) where:
77     ok is True or False and indicates success or failure,
78     actual_baseband_freq is the RF frequency that corresponds to DC in the IF.
79   */
80
81   freq_t int_freq = freq_t(freq);
82   bool ok = d_common->_set_freq(int_freq*2);
83   double freq_result = (double) d_common->_get_freq()/2.0;
84   struct freq_result_t args = {ok, freq_result};
85
86   /* Wait before reading Lock Detect*/
87   timespec t;
88   t.tv_sec = 0;
89   t.tv_nsec = 10000000;
90   nanosleep(&t, NULL);
91
92   fprintf(stderr,"Setting WBXNG frequency, requested %d, obtained %f, lock_detect %d\n",
93           int_freq, freq_result, d_common->_get_locked());
94
95   // FIXME
96   // Offsetting the LO helps get the Tx carrier leakage out of the way.
97   // This also ensures that on Rx, we're not getting hosed by the
98   // FPGA's DC removal loop's time constant.  We were seeing a
99   // problem when running with discontinuous transmission.
100   // Offsetting the LO made the problem go away.
101   //freq += d_lo_offset;
102
103   return args;
104 }
105
106 bool
107 wbxng_base::_set_pga(float pga_gain)
108 {
109   if(d_which == 0) {
110     usrp()->set_pga(0, pga_gain);
111     usrp()->set_pga(1, pga_gain);
112   }
113   else {
114     usrp()->set_pga(2, pga_gain);
115     usrp()->set_pga(3, pga_gain);
116   }
117   return true;
118 }
119
120 bool
121 wbxng_base::is_quadrature()
122 {
123   /*
124     Return True if this board requires both I & Q analog channels.
125
126     This bit of info is useful when setting up the USRP Rx mux register.
127   */
128   return true;
129 }
130
131 double
132 wbxng_base::freq_min()
133 {
134   return (double) d_common->_get_min_freq()/2.0;
135 }
136
137 double
138 wbxng_base::freq_max()
139 {
140   return (double) d_common->_get_max_freq()/2.0;
141 }
142
143 // ----------------------------------------------------------------
144
145 wbxng_base_tx::wbxng_base_tx(usrp_basic_sptr _usrp, int which, int _power_on)
146   : wbxng_base(_usrp, which, _power_on)
147 {
148   /*
149     @param usrp: instance of usrp.sink_c
150     @param which: 0 or 1 corresponding to side TX_A or TX_B respectively.
151   */
152
153   if(which == 0) {
154     d_spi_enable = SPI_ENABLE_TX_A;
155   }
156   else {
157     d_spi_enable = SPI_ENABLE_TX_B;
158   }
159
160   d_common = new adf4350(_usrp, d_which, d_spi_enable);
161
162   // power up the transmit side, but don't enable the mixer
163   usrp()->_write_oe(d_which,(RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5), (RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5));
164   usrp()->write_io(d_which, (power_on()|RX_TXN|ENABLE_33|ENABLE_5), (RX_TXN|ENABLE_33|ENABLE_5));
165   //set_lo_offset(4e6);
166   
167   // Disable VCO/PLL
168   d_common->_enable(false);
169
170   set_gain((gain_min() + gain_max()) / 2.0);  // initialize gain
171 }
172
173 wbxng_base_tx::~wbxng_base_tx()
174 {
175   shutdown();
176 }
177
178
179 void
180 wbxng_base_tx::shutdown()
181 {
182   // fprintf(stderr, "wbxng_base_tx::shutdown  d_is_shutdown = %d\n", d_is_shutdown);
183
184   if (!d_is_shutdown){
185     d_is_shutdown = true;
186     // do whatever there is to do to shutdown
187
188     // Disable VCO/PLL
189     d_common->_enable(false);
190
191     // Power down and leave the T/R switch in the R position
192     usrp()->write_io(d_which, (power_off()|RX_TXN), (RX_TXN|ENABLE_33|ENABLE_5));
193
194
195     /*
196     _write_control(_compute_control_reg());
197     */
198     _enable_refclk(false);                       // turn off refclk
199     set_auto_tr(false);
200   }
201 }
202
203 bool
204 wbxng_base_tx::set_auto_tr(bool on)
205 {
206   bool ok = true;
207   if(on) {
208     ok &= set_atr_mask (RX_TXN | TXMOD_EN);
209     ok &= set_atr_txval(0      | TXMOD_EN);
210     ok &= set_atr_rxval(RX_TXN);
211   }
212   else {
213     ok &= set_atr_mask (0);
214     ok &= set_atr_txval(0);
215     ok &= set_atr_rxval(0);
216   }
217   return ok;
218 }
219
220 bool
221 wbxng_base_tx::set_enable(bool on)
222 {
223   /*
224     Enable transmitter if on is true
225   */
226
227   int v;
228   int mask = RX_TXN | TXMOD_EN;
229   if(on) {
230     v = TXMOD_EN;
231     // Enable VCO/PLL
232     d_common->_enable(true);
233   }
234   else {
235     v = RX_TXN;
236     // Disable VCO/PLL
237     d_common->_enable(false);
238   }
239   return usrp()->write_io(d_which, v, mask);
240 }
241
242 float
243 wbxng_base_tx::gain_min()
244 {
245   return usrp()->pga_max();
246 }
247
248 float
249 wbxng_base_tx::gain_max()
250 {
251   return usrp()->pga_max() + 25.0;
252 }
253
254 float
255 wbxng_base_tx::gain_db_per_step()
256 {
257   return 1;
258 }
259
260 bool
261 wbxng_base_tx::set_gain(float gain)
262 {
263   /*
264     Set the gain.
265
266     @param gain:  gain in decibels
267     @returns True/False
268   */
269
270   // clamp gain
271   gain = std::max(gain_min(), std::min(gain, gain_max()));
272
273   float pga_gain, agc_gain;
274   float V_maxgain, V_mingain, V_fullscale, dac_value;
275
276   float maxgain = gain_max() - usrp()->pga_max();
277   float mingain = gain_min();
278   if(gain > maxgain) {
279     pga_gain = gain-maxgain;
280     assert(pga_gain <= usrp()->pga_max());
281     agc_gain = maxgain;
282   }
283   else {
284     pga_gain = 0;
285     agc_gain = gain;
286   }
287
288   V_maxgain = 0.7;
289   V_mingain = 1.4;
290   V_fullscale = 3.3;
291   dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale;
292
293   fprintf(stderr, "TXGAIN: %f dB, Dac Code: %d, Voltage: %f\n", gain, int(dac_value), float((dac_value/4096.0)*V_fullscale));
294   assert(dac_value>=0 && dac_value<4096);
295
296   return (usrp()->write_aux_dac(d_which, 0, int(dac_value))
297           && _set_pga(int(pga_gain)));
298 }
299
300
301 /**************************************************************************/
302
303
304 wbxng_base_rx::wbxng_base_rx(usrp_basic_sptr _usrp, int which, int _power_on)
305   : wbxng_base(_usrp, which, _power_on)
306 {
307   /*
308     @param usrp: instance of usrp.source_c
309     @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
310   */
311
312   if(which == 0) {
313     d_spi_enable = SPI_ENABLE_RX_A;
314   }
315   else {
316     d_spi_enable = SPI_ENABLE_RX_B;
317   }
318
319   d_common = new adf4350(_usrp, d_which, d_spi_enable);
320   
321   // Disable VCO/PLL
322   d_common->_enable(true);
323
324   usrp()->_write_oe(d_which, (RX2_RX1N|RXBB_EN|ATTN_MASK|ENABLE_33|ENABLE_5), (RX2_RX1N|RXBB_EN|ATTN_MASK|ENABLE_33|ENABLE_5));
325   usrp()->write_io(d_which,  (power_on()|RX2_RX1N|RXBB_EN|ENABLE_33|ENABLE_5), (RX2_RX1N|RXBB_EN|ATTN_MASK|ENABLE_33|ENABLE_5));
326   fprintf(stderr,"Setting WBXNG RXBB on");
327
328   // set up for RX on TX/RX port
329   select_rx_antenna("TX/RX");
330
331   bypass_adc_buffers(true);
332
333   /*
334   set_lo_offset(-4e6);
335   */
336 }
337
338 wbxng_base_rx::~wbxng_base_rx()
339 {
340   shutdown();
341 }
342
343 void
344 wbxng_base_rx::shutdown()
345 {
346   // fprintf(stderr, "wbxng_base_rx::shutdown  d_is_shutdown = %d\n", d_is_shutdown);
347
348   if (!d_is_shutdown){
349     d_is_shutdown = true;
350     // do whatever there is to do to shutdown
351
352     // Power down
353     usrp()->common_write_io(C_RX, d_which, power_off(), (ENABLE_33|ENABLE_5));
354
355     // Power down VCO/PLL
356     d_common->_enable(false);
357
358     // fprintf(stderr, "wbxng_base_rx::shutdown  before _write_control\n");
359     //_write_control(_compute_control_reg());
360
361     // fprintf(stderr, "wbxng_base_rx::shutdown  before _enable_refclk\n");
362     _enable_refclk(false);                       // turn off refclk
363
364     // fprintf(stderr, "wbxng_base_rx::shutdown  before set_auto_tr\n");
365     set_auto_tr(false);
366
367     // fprintf(stderr, "wbxng_base_rx::shutdown  after set_auto_tr\n");
368   }
369 }
370
371 bool
372 wbxng_base_rx::set_auto_tr(bool on)
373 {
374   bool ok = true;
375   if(on) {
376     ok &= set_atr_mask (RXBB_EN|RX2_RX1N);
377     ok &= set_atr_txval(      0|RX2_RX1N);
378     ok &= set_atr_rxval(RXBB_EN|       0);
379   }
380   else {
381     ok &= set_atr_mask (0);
382     ok &= set_atr_txval(0);
383     ok &= set_atr_rxval(0);
384   }
385   return true;
386 }
387
388 bool
389 wbxng_base_rx::select_rx_antenna(int which_antenna)
390 {
391   /*
392     Specify which antenna port to use for reception.
393     @param which_antenna: either 'TX/RX' or 'RX2'
394   */
395
396   if(which_antenna == 0) {
397     usrp()->write_io(d_which, 0,RX2_RX1N);
398   }
399   else if(which_antenna == 1) {
400     usrp()->write_io(d_which, RX2_RX1N, RX2_RX1N);
401   }
402   else {
403     return false;
404   }
405   return true;
406 }
407
408 bool
409 wbxng_base_rx::select_rx_antenna(const std::string &which_antenna)
410 {
411   /*
412     Specify which antenna port to use for reception.
413     @param which_antenna: either 'TX/RX' or 'RX2'
414   */
415
416
417   if(which_antenna == "TX/RX") {
418     usrp()->write_io(d_which, 0, RX2_RX1N);
419   }
420   else if(which_antenna == "RX2") {
421     usrp()->write_io(d_which, RX2_RX1N, RX2_RX1N);
422   }
423   else {
424     return false;
425   }
426
427   return true;
428 }
429
430 bool
431 wbxng_base_rx::set_gain(float gain)
432 {
433   /*
434     Set the gain.
435
436     @param gain:  gain in decibels
437     @returns True/False
438   */
439
440   // clamp gain
441   gain = std::max(gain_min(), std::min(gain, gain_max()));
442
443   float pga_gain, agc_gain;
444
445   float maxgain = gain_max() - usrp()->pga_max();
446   float mingain = gain_min();
447   if(gain > maxgain) {
448     pga_gain = gain-maxgain;
449     assert(pga_gain <= usrp()->pga_max());
450     agc_gain = maxgain;
451   }
452   else {
453     pga_gain = 0;
454     agc_gain = gain;
455   }
456
457   return _set_attn(maxgain-agc_gain) && _set_pga(int(pga_gain));
458 }
459
460 bool
461 wbxng_base_rx::_set_attn(float attn)
462 {
463   int attn_code = int(floor(attn/0.5));
464   unsigned int iobits = (~attn_code) << ATTN_SHIFT;
465   fprintf(stderr, "Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x \n", attn, attn_code, iobits & ATTN_MASK, ATTN_MASK);
466   return usrp()->write_io(d_which, iobits, ATTN_MASK);
467 }
468
469 // ----------------------------------------------------------------
470
471 db_wbxng_tx::db_wbxng_tx(usrp_basic_sptr usrp, int which)
472   : wbxng_base_tx(usrp, which)
473 {
474 }
475
476 db_wbxng_tx::~db_wbxng_tx()
477 {
478 }
479
480 db_wbxng_rx::db_wbxng_rx(usrp_basic_sptr usrp, int which)
481   : wbxng_base_rx(usrp, which)
482 {
483   set_gain((gain_min() + gain_max()) / 2.0);  // initialize gain
484 }
485
486 db_wbxng_rx::~db_wbxng_rx()
487 {
488 }
489
490 float
491 db_wbxng_rx::gain_min()
492 {
493   return usrp()->pga_min();
494 }
495
496 float
497 db_wbxng_rx::gain_max()
498 {
499   return usrp()->pga_max()+30.5;
500 }
501
502 float
503 db_wbxng_rx::gain_db_per_step()
504 {
505   return 0.05;
506 }
507
508
509 bool
510 db_wbxng_rx::i_and_q_swapped()
511 {
512   return false;
513 }