1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
#!/usr/bin/env python
#
# Copyright 2004 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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, or (at your option)
# any later version.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
'''Control National LMX2306 based frequency synthesizer'''
from gnuradio import gr
from gnuradio import eng_notation
from gnuradio.eng_option import eng_option
from optparse import OptionParser
# bottom two bits of 21 bit word select which register to program
R_REG = 0x0
AB_REG = 0x1
F_REG = 0x2
F_counter_reset = (1 << 2)
F_phase_detector_polarity = (1 << 7)
F_LD_tri_state = (0 << 4)
F_LD_R_divider_output = (4 << 4)
F_LD_N_divider_output = (2 << 4)
F_LD_serial_data_output = (6 << 4)
F_LD_digital_lock_detect = (1 << 4)
F_LD_open_drain = (5 << 4)
F_LD_high = (3 << 4)
F_LD_low = (7 << 4)
# F_default = F_LD_digital_lock_detect | F_phase_detector_polarity
F_default = F_LD_open_drain | F_phase_detector_polarity
#
# 4 control pins:
# CE always high
# LE load enable. When LE goes high, data stored in the shift register
# is loaded into one of the three registers
# CLK data is clocked in on the rising edge
# DATA single data bit. Entered MSB first
DB_CLK = (1 << 0)
DB_DATA = (1 << 1)
DB_LE = (1 << 2)
DB_CE = (1 << 3)
class lmx2306 (object):
'''Control the National LMX2306 PLL'''
__slots__ = ['pp', 'shadow', 'fosc', 'r', 'step_size', 'verbose']
def __init__ (self, fosc, step_size, which_pp = 0):
'''FOSC is the frequency of the reference oscillator,
STEP_SIZE is the step between valid frequencies,
WHICH_PP specifies which parallel port to use
'''
self.pp = gr.make_ppio (which_pp)
self.shadow = DB_CE
self.pp.lock ()
self.pp.write_data (self.shadow)
self.pp.unlock ()
self.verbose = False
self._set_fosc (fosc)
self._set_step (step_size)
def program (self, r, a, b):
if self.verbose:
print "lmx2306: r = %d a = %d b = %d" % (r, a, b)
self.pp.lock ()
self._write_word (F_REG | F_default | F_counter_reset)
self._write_word (R_REG | ((r & 0x3fff) << 2))
self._write_word (AB_REG | ((a & 0x1f) << 2) | ((b & 0x1fff) << 7))
self._write_word (F_REG | F_default)
self.pp.unlock ()
def set_freq (self, freq):
'''Set the PLL frequency to FREQ
Return the actual freq value set. It will be rounded down to a
multiple of step_size
'''
divisor = int (freq / self.step_size)
actual = divisor * self.step_size
(a, b) = self._compute_ab (divisor)
self.program (self.r, a, b)
return actual
# ----------------------------------------------------------------
def _set_fosc (self, ref_oscillator_freq):
self.fosc = ref_oscillator_freq
def _set_step (self, step_size):
r = int (self.fosc / step_size)
if r * step_size != self.fosc:
raise ValueError, "step_size is not a factor of self.fosc"
if r < 3 or r > 16383:
raise ValueError, "r is out of range"
self.r = r
self.step_size = step_size
def _compute_ab (self, divisor):
b = divisor / 8
a = divisor - (b * 8)
if b < 3 or b > 8191 or a > b:
raise ValueError, "Invalid divisor"
return (a, b)
def _write_word (self, w):
for i in range(21):
if w & (1 << 20):
self._set_DATA_1 ()
else:
self._set_DATA_0 ()
w = (w << 1) & 0x0ffffff
self._set_CLK_1 ()
self._set_CLK_0 ()
self._set_LE_1 ()
self._set_LE_0 ()
def _set_LE_0 (self):
self.shadow = self.shadow & ~DB_LE
self.pp.write_data (self.shadow)
def _set_LE_1 (self):
self.shadow = self.shadow | DB_LE
self.pp.write_data (self.shadow)
def _set_CLK_0 (self):
self.shadow = self.shadow & ~DB_CLK
self.pp.write_data (self.shadow)
def _set_CLK_1 (self):
self.shadow = self.shadow | DB_CLK
self.pp.write_data (self.shadow)
def _set_DATA_0 (self):
self.shadow = self.shadow & ~DB_DATA
self.pp.write_data (self.shadow)
def _set_DATA_1 (self):
self.shadow = self.shadow | DB_DATA
self.pp.write_data (self.shadow)
if __name__ == '__main__':
parser = OptionParser (option_class=eng_option)
parser.add_option ("-o", "--fosc", type="eng_float", default=32e6,
help="set reference oscillator freq to FREQ", metavar="FREQ")
parser.add_option ("-s", "--step-size", type="eng_float", default=10e3,
help="set the frequency step size to STEP_SIZE")
parser.add_option ("-f", "--freq", type="eng_float", default=430e6,
help="set VCO frequency to FREQ")
parser.add_option ("-v", "--verbose", action="store_true", default=False)
(options, args) = parser.parse_args ()
if options.verbose:
print "fosc = %s step = %s fvco = %s" % (
eng_notation.num_to_str (options.fosc),
eng_notation.num_to_str (options.step_size),
eng_notation.num_to_str (options.freq))
lmx = lmx2306 (options.fosc, options.step_size)
lmx.verbose = options.verbose
actual = lmx.set_freq (options.freq)
if options.verbose:
print "fvco_actual = %s delta = %s" % (
eng_notation.num_to_str (actual),
eng_notation.num_to_str (options.freq - actual))
|