tinypythonpanadapter/si570control.py

297 wiersze
11 KiB
Python
Executable File

#!/usr/bin/env python
# Si570control class gives python access to the Si570 Digital
# Sythesizer via a USB connection.
# Copyright (C) 2014 Martin Ewing
#
# This program 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 of the License, or
# (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
# Contact the author by e-mail: aa6e@arrl.net
# This is "middleware" that defines an API for general user programming
# of radio systems using the Si570 Programmable VCXO as an inexpensive
# digital VFO. These routines connect via USB to an ATtiny45 USB-to-I2C
# device, which is running the usbavrsi570 code from PE0FKO.
# Tested on SoftRock RxTx Ensemble that uses the ATtiny85 MPU chip and a
# SiLabs 570 ("CAC000141G / D1HOS144") chip
# [3.3v CMOS, 61 ppm stab., 10-160 MHz]
# partially based on a subset of operations.c from Andrew Nilsson VK6JBL
# Also, see http://www.silabs.com/Support%20Documents/TechnicalDocs/si570.pdf
# and https://code.google.com/p/usbavrsi570/
# require libusb-1.0 wrapper from https://pypi.python.org/pypi/libusb1/1.2.0
import libusb1, usb1
import math, sys
from sidefs import *
# flags used for most usb i/o
#input
UFLGS1 = libusb1.LIBUSB_TYPE_VENDOR | \
libusb1.LIBUSB_RECIPIENT_DEVICE | \
libusb1.LIBUSB_ENDPOINT_IN
#output
UFLGS2 = libusb1.LIBUSB_TYPE_VENDOR | \
libusb1.LIBUSB_RECIPIENT_DEVICE | \
libusb1.LIBUSB_ENDPOINT_OUT
# Note changes from operation.c:
# 1. method names have changed to make them more regular.
# 2. get/set freq always work with floating MHz of signal frequency =
# osc frequency / multiplier.
class Si570control(object):
def __init__(self, verbose=0, fXtal=114.285, multiplier=4, i2c=0x55,
vendor_id=0x16c0, product_id=0x05dc):
self.verbose = verbose
self.fXtal = fXtal
self.multiplier = multiplier
self.i2c = i2c
self.context = usb1.USBContext()
self.device = self.context.getByVendorIDAndProductID(vendor_id, product_id)
if self.verbose:
print "device", self.device
self.handle = self.device.open()
self.version = self.getVersion()
# get version numbers
def getVersion(self):
bb = bytearray(self.handle.controlRead
(UFLGS1,REQUEST_READ_VERSION, 0x0E00, 0, 2, 500))
if len(bb)==2:
ver = "%d.%d" % (bb[1], bb[0])
if self.verbose:
print "Version", ver
return ver
else:
if self.verbose:
print "Version Unknown."
return None
def enum_devices(self):
mydevices = self.context.getDeviceList()
print "n=",len(mydevices)
for i,x in enumerate(mydevices):
print "%2d %2d %d %3d %d %d %0.4x %0.4x" % (i,
x.getBusNumber(),
x.getDeviceAddress(),
x.getDeviceClass(),
x.getDeviceProtocol(),
x.getDeviceSpeed(),
x.getVendorID(),
x.getProductID() )
def getFreqByValue(self): # return osc freq / multiplier
bb = bytearray( self.handle.controlRead
(UFLGS1, REQUEST_READ_FREQUENCY, 0, 0, 4, 500) )
fint = ((bb[3] << 8 | bb[2]) << 8 | bb[1] ) << 8 | bb[0]
if len(bb) == 4:
ans = (float(fint)/(1<<21)) / self.multiplier
return ans
else:
return None
def getRegisters(self):
bb = bytearray( self.handle.controlRead
(UFLGS1, REQUEST_READ_REGISTERS, SI570_I2C_ADDR, 0, 6, 5000) )
if len(bb) > 0:
for i in range(6):
print "Register %d = %X (%d)" % ( i+7, bb[i], bb[i] )
def calculateFreq(self, s): # s is string, could be bytearray?
si = bytearray(s)
RFREQ_int = ((si[2] & 0xf0) >> 4) + ((si[1] & 0x3f) * 16)
RFREQ_frac = ((si[2] & 0xf ) << 24) + (si[3] << 16) + (si[4] << 8) + si[5]
RFREQ = RFREQ_int + (float(RFREQ_frac) / 268435456.0)
N1 = ((si[1] & 0xc0 ) >> 6) + ((si[0] & 0x1f) << 2)
HS_DIV = (si[0] & 0xE0) >> 5
fout = self.fXtal * RFREQ / ((N1 + 1) * HS_DIV_MAP[HS_DIV])
if self.verbose >= 2:
print "RFREQ = %f" % RFREQ
print "N1 = %d" % N1
print "HS_DIV = %d" % HS_DIV
print "nHS_DIV = %d" % HS_DIV_MAP[HS_DIV]
print "fout = %f" % fout
return fout # actual osc freq.
def getFreq(self): # return osc freq / multiplier
strg = self.handle.controlRead \
(UFLGS1, REQUEST_READ_REGISTERS, SI570_I2C_ADDR, 0, 6, 5000)
# keep strg for calculateFrequency, avoiding unicode issues.
bb = bytearray( strg )
if len(bb) > 0:
if self.verbose >= 2:
for i in range(6):
print "Register %d = %X (%d)" % ( i+7, bb[i], bb[i] )
return self.calculateFreq(strg) / self.multiplier
else:
return None
def getPTT(self):
bb = bytearray( self.handle.controlRead
(UFLGS1, REQUEST_READ_KEYS, 0, 0, 1, 5000) )
if bb[0] & 0x40:
return 1
else:
return 0
def getKeys(self):
bb = bytearray( self.handle.controlRead
(UFLGS1, REQUEST_READ_KEYS, 0, 0, 1, 5000) )
if bb[0] & 0x20: # CW_KEY_1 high: not pressed low: pressed
keys = 0
else:
keys = 1
if not (bb[0] & 0x02):
keys += 2 # CW_KEY_2 high: not pressed low: pressed
return keys # keys = 3 if both pressed
def setPTT(self, value):
bb = bytearray( self.handle.controlRead
(UFLGS1, REQUEST_SET_PTT, value, 0, 3, 5000) )
if self.verbose >= 2:
print "buffer=",bb[0],bb[1],bb[2]
def calcDividers(self, f): # Returns solution = [HS_DIV, N1, f0, RFREQ]
# Instead of solution structure, use simple list for each variable.
cHS_DIV = list()
cN1 = list()
cf0 = list()
cRFREQ = list()
for i in range(7,-1,-1): # Count down through the dividers
if HS_DIV_MAP[i] > 0:
cHS_DIV.append(i)
y = (SI570_DCO_HIGH + SI570_DCO_LOW) / (2 * f)
y = y / HS_DIV_MAP[i]
if y < 1.5:
y = 1.0
else:
y = 2 * round(y/2.0)
if y > 128:
y = 128
cN1.append( math.trunc(y) - 1 )
cf0.append( f * y * HS_DIV_MAP[i] )
else:
cHS_DIV.append(None) # dummy result
cN1.append(None) # another dummy
cf0.append( 1.0E16 )
imin = -1
fmin = 1.0E16
for i in range(8):
if (cf0[i] >= SI570_DCO_LOW) & (cf0[i] <= SI570_DCO_HIGH) :
if cf0[i] < fmin:
fmin = cf0[i]
imin = i
if imin >= 0:
solution = [ cHS_DIV[imin], cN1[imin], cf0[imin], cf0[imin]/self.fXtal ]
if (self.verbose >= 2):
print "Solution:"
print " HS_DIV = %d" % solution[0]
print " N1 = %d" % solution[1]
print " f0 = %f" % solution[2]
print " RFREQ = %f" % solution[3]
else:
solution = None # This is the error return
return solution
def setLongWord(self, v ): # v = int value; return bytearray(4)
iv = int(v) # be sure of int type
b = bytearray(4)
b[0] = iv & 0xff
b[1] = ((iv & 0xff00) >> 8) & 0xff
b[2] = ((iv & 0xff0000) >> 16) & 0xff
b[3] = ((iv & 0xff000000) >> 24) & 0xff
return b # NB bytearray, not long word!
def setFreq(self, frequency):
f = self.multiplier * frequency
value = 0x700 + self.i2c
index = 0
if self.verbose:
print "Setting Si570 Frequency by registers to: %f" % f
sHS_DIV, sN1, sf0, sRFREQ = self.calcDividers(f)
RFREQ_int = math.trunc(sRFREQ)
RFREQ_frac= int( round((sRFREQ - RFREQ_int) * 268435456) ) # check int ok
intbuf = self.setLongWord( RFREQ_int )
fracbuf = self.setLongWord( RFREQ_frac)
outbuf = bytearray(6)
outbuf[5] = fracbuf[0]
outbuf[4] = fracbuf[1]
outbuf[3] = fracbuf[2]
outbuf[2] = fracbuf[3] | ((intbuf[0] & 0xf) << 4)
outbuf[1] = RFREQ_int / 16 + ((sN1 & 0x3) << 6)
outbuf[0] = sN1/4 + (sHS_DIV << 5)
sout = str()
for x in outbuf:
sout += chr(x)
r = self.handle.controlWrite \
(UFLGS2, REQUEST_SET_FREQ, value, index, sout, 5000)
if r:
if self.verbose >= 2:
print "Set Freq Buffer",
print "%x %x" % (outbuf[0], outbuf[1])
else:
print "Failed writing frequency to device"
def setFreqByValue(self, frequency):
f = self.multiplier * frequency
value = 0x700 + self.i2c
index = 0
buf = self.setLongWord(round(f * 2097152.0))
if self.verbose:
print "Setting Si570 Frequency by value to: %f" % f
if self.verbose >= 2:
print "Set Freq Buffer: %x %x %x %x" % (buf[0], buf[1],
buf[2], buf[3])
sout = str()
for x in buf:
sout += chr(x)
r = self.handle.controlWrite \
(UFLGS2, REQUEST_SET_FREQ_BY_VALUE, value, index, sout, 5000)
if r:
if self.verbose >= 2:
print "Set Freq Buffer: %x %x %x %x" % (buf[0], buf[1],
buf[2], buf[3])
else:
print "Failed setting frequency"
# End of Si570 class
if __name__ == "__main__":
# debug code goes here
si = Si570control(verbose=0)
freq = si.getFreqByValue()
print "freq by value", freq
#si.getRegisters()
#f = si.getFreq()
#print "returned freq", f
#si.setFreq( 1.8)
#print "set freq check"
#si.getFreq()
print "SET FREQ BY VALUE"
si.setFreqByValue(7.5)
print "checking"
print si.getFreqByValue()
if False:
print "Calc. dividers [HS_DIV, N1, f0, RFREQ]"
a = si.calcDividers(28.0)
print a
print "Done."