c17e8d670faddaa04f89e8a8415c42a75c606dcc
[debian/gnuradio] / usrp / host / lib / db_wbxng_adf4350.cc
1 //
2 // Copyright 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 "db_wbxng_adf4350.h"
26 #include <db_base_impl.h>
27 #include <stdio.h>
28
29 #define FREQ_C(freq) uint64_t(freq)
30 #define INPUT_REF_FREQ FREQ_C(64e6)
31 #define DIV_ROUND(num, denom) (((num) + ((denom)/2))/(denom))
32 #define INPUT_REF_FREQ_2X (2*INPUT_REF_FREQ)                            /* input ref freq with doubler turned on */
33 #define MIN_INT_DIV uint16_t(23)                                        /* minimum int divider, prescaler 4/5 only */
34 #define MAX_RF_DIV uint8_t(16)                                          /* max rf divider, divides rf output */
35 #define MIN_VCO_FREQ FREQ_C(2.2e9)                                      /* minimum vco freq */
36 #define MAX_VCO_FREQ FREQ_C(4.4e9)                                      /* minimum vco freq */
37 #define MAX_FREQ DIV_ROUND(MAX_VCO_FREQ, 1)                                           /* upper bound freq (rf div = 1) */
38 #define MIN_FREQ DIV_ROUND(MIN_VCO_FREQ, MAX_RF_DIV)                    /* calculated lower bound freq */
39
40 #define CE_PIN        (1 << 3)
41 #define PDB_RF_PIN    (1 << 2)
42 #define MUX_PIN       (1 << 1)
43 #define LD_PIN        (1 << 0)
44
45 adf4350::adf4350(usrp_basic_sptr _usrp, int _which, int _spi_enable)
46 {
47     /* Initialize the pin directions. */
48
49     d_usrp = _usrp;
50     d_which = _which;
51     d_spi_enable = _spi_enable;
52     d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0;
53
54     d_regs = new adf4350_regs(this);
55
56     /* Outputs */
57     d_usrp->_write_oe(d_which, (CE_PIN | PDB_RF_PIN), (CE_PIN | PDB_RF_PIN));
58     d_usrp->write_io(d_which, (CE_PIN), (CE_PIN | PDB_RF_PIN));
59
60     /* Initialize the pin levels. */
61     _enable(true);
62     /* Initialize the registers. */
63     d_regs->_load_register(5);
64     d_regs->_load_register(4);
65     d_regs->_load_register(3);
66     d_regs->_load_register(2);
67     d_regs->_load_register(1);
68     d_regs->_load_register(0);
69 }
70
71 adf4350::~adf4350()
72 {
73     d_usrp->write_io(d_which, (0), (CE_PIN | PDB_RF_PIN));
74     delete d_regs;
75 }
76
77 freq_t
78 adf4350::_get_max_freq(void)
79 {
80     return MAX_FREQ;
81 }
82
83 freq_t
84 adf4350::_get_min_freq(void)
85 {
86     return MIN_FREQ;
87 }
88
89 bool
90 adf4350::_get_locked(void)
91 {
92     return d_usrp->read_io(d_which) & LD_PIN;
93 }
94
95 void
96 adf4350::_enable(bool enable)
97 {
98     if (enable){ /* chip enable */
99         d_usrp->write_io(d_which, (PDB_RF_PIN), (PDB_RF_PIN));
100     }else{
101         d_usrp->write_io(d_which, 0, (PDB_RF_PIN));
102     }
103 }
104
105 void
106 adf4350::_write(uint8_t addr, uint32_t data)
107 {
108     data |= addr;
109
110     // create str from data here
111     char s[4];
112     s[0] = (char)((data >> 24) & 0xff);
113     s[1] = (char)((data >> 16) & 0xff);
114     s[2] = (char)((data >>  8) & 0xff);
115     s[3] = (char)(data & 0xff);
116     std::string str(s, 4);
117
118     timespec t;
119     t.tv_sec = 0;
120     t.tv_nsec = 5e6;
121
122     nanosleep(&t, NULL);
123     d_usrp->_write_spi(0, d_spi_enable, d_spi_format, str);
124     nanosleep(&t, NULL);
125
126     //fprintf(stderr, "Wrote to WBXNG SPI address %d with data %8x\n", addr, data);
127     /* pulse latch */
128     //d_usrp->write_io(d_which, 1, LE_PIN);
129     //d_usrp->write_io(d_which, 0, LE_PIN);
130 }
131
132 bool
133 adf4350::_set_freq(freq_t freq)
134 {
135     /* Set the frequency by setting int, frac, mod, r, div */
136     if (freq > MAX_FREQ || freq < MIN_FREQ) return false;
137     int min_int_div = 23;
138     d_regs->d_prescaler = 0;
139     if (freq > FREQ_C(3e9)) {
140         min_int_div = 75;
141         d_regs->d_prescaler = 1;
142     }
143     /* Ramp up the RF divider until the VCO is within range. */
144     d_regs->d_divider_select = 0;
145     while (freq < MIN_VCO_FREQ){
146         freq <<= 1; //double the freq
147         d_regs->d_divider_select++; //double the divider
148     }
149     /* Ramp up the R divider until the N divider is at least the minimum. */
150     //d_regs->d_10_bit_r_counter = INPUT_REF_FREQ*MIN_INT_DIV/freq;
151     d_regs->d_10_bit_r_counter = 2;
152     uint64_t n_mod;
153     do{
154         d_regs->d_10_bit_r_counter++;
155         n_mod = freq;
156         n_mod *= d_regs->d_10_bit_r_counter;
157         n_mod *= d_regs->d_mod;
158         n_mod /= INPUT_REF_FREQ;
159         /* calculate int and frac */
160         d_regs->d_int = n_mod/d_regs->d_mod;
161         d_regs->d_frac = (n_mod - (freq_t)d_regs->d_int*d_regs->d_mod) & uint16_t(0xfff);
162         /*
163         fprintf(stderr,
164             "VCO %lu KHz, Int %u, Frac %u, Mod %u, R %u, Div %u\n",
165             freq, d_regs->d_int, d_regs->d_frac,
166             d_regs->d_mod, d_regs->d_10_bit_r_counter, (1 << d_regs->d_divider_select)
167         );
168         */
169     }while(d_regs->d_int < min_int_div);
170     /* calculate the band select so PFD is under 125 KHz */
171     d_regs->d_8_bit_band_select_clock_divider_value = \
172         INPUT_REF_FREQ/(FREQ_C(30e3)*d_regs->d_10_bit_r_counter) + 1;
173     /*
174     fprintf(stderr, "Band Selection: Div %u, Freq %lu\n",
175         d_regs->d_8_bit_band_select_clock_divider_value,
176         INPUT_REF_FREQ/(d_regs->d_8_bit_band_select_clock_divider_value * d_regs->d_10_bit_r_counter) + 1
177     );
178     */
179     d_regs->_load_register(5);
180     d_regs->_load_register(3);
181     d_regs->_load_register(1);
182     /* load involved registers */
183     d_regs->_load_register(2);
184     d_regs->_load_register(4);
185     d_regs->_load_register(0); /* register 0 must be last */
186     return true;
187 }
188
189 freq_t
190 adf4350::_get_freq(void)
191 {
192     /* Calculate the freq from int, frac, mod, ref, r, div:
193      *  freq = (int + frac/mod) * (ref/r)
194      * Keep precision by doing multiplies first:
195      *  freq = (((((((int)*mod) + frac)*ref)/mod)/r)/div)
196      */
197     uint64_t temp;
198     temp = d_regs->d_int;
199     temp *= d_regs->d_mod;
200     temp += d_regs->d_frac;
201     temp *= INPUT_REF_FREQ;
202     temp /= d_regs->d_mod;
203     temp /= d_regs->d_10_bit_r_counter;
204     temp /= (1 << d_regs->d_divider_select);
205     return temp;
206 }