pull/3/head
Jon Fox 2016-12-21 13:03:39 -06:00 zatwierdzone przez GitHub
rodzic 67a8db39c5
commit b5192feae3
2 zmienionych plików z 367 dodań i 0 usunięć

316
SI5351.py 100644
Wyświetl plik

@ -0,0 +1,316 @@
from machine import I2C
import math
SI5351_REGISTER_0_DEVICE_STATUS = 0
SI5351_REGISTER_1_INTERRUPT_STATUS_STICKY = 1
SI5351_REGISTER_2_INTERRUPT_STATUS_MASK = 2
SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL = 3
SI5351_REGISTER_9_OEB_PIN_ENABLE_CONTROL = 9
SI5351_REGISTER_15_PLL_INPUT_SOURCE = 15
SI5351_REGISTER_16_CLK0_CONTROL = 16
SI5351_REGISTER_17_CLK1_CONTROL = 17
SI5351_REGISTER_18_CLK2_CONTROL = 18
SI5351_REGISTER_19_CLK3_CONTROL = 19
SI5351_REGISTER_20_CLK4_CONTROL = 20
SI5351_REGISTER_21_CLK5_CONTROL = 21
SI5351_REGISTER_22_CLK6_CONTROL = 22
SI5351_REGISTER_23_CLK7_CONTROL = 23
SI5351_REGISTER_24_CLK3_0_DISABLE_STATE = 24
SI5351_REGISTER_25_CLK7_4_DISABLE_STATE = 25
SI5351_REGISTER_42_MULTISYNTH0_PARAMETERS_1 = 42
SI5351_REGISTER_43_MULTISYNTH0_PARAMETERS_2 = 43
SI5351_REGISTER_44_MULTISYNTH0_PARAMETERS_3 = 44
SI5351_REGISTER_45_MULTISYNTH0_PARAMETERS_4 = 45
SI5351_REGISTER_46_MULTISYNTH0_PARAMETERS_5 = 46
SI5351_REGISTER_47_MULTISYNTH0_PARAMETERS_6 = 47
SI5351_REGISTER_48_MULTISYNTH0_PARAMETERS_7 = 48
SI5351_REGISTER_49_MULTISYNTH0_PARAMETERS_8 = 49
SI5351_REGISTER_50_MULTISYNTH1_PARAMETERS_1 = 50
SI5351_REGISTER_51_MULTISYNTH1_PARAMETERS_2 = 51
SI5351_REGISTER_52_MULTISYNTH1_PARAMETERS_3 = 52
SI5351_REGISTER_53_MULTISYNTH1_PARAMETERS_4 = 53
SI5351_REGISTER_54_MULTISYNTH1_PARAMETERS_5 = 54
SI5351_REGISTER_55_MULTISYNTH1_PARAMETERS_6 = 55
SI5351_REGISTER_56_MULTISYNTH1_PARAMETERS_7 = 56
SI5351_REGISTER_57_MULTISYNTH1_PARAMETERS_8 = 57
SI5351_REGISTER_58_MULTISYNTH2_PARAMETERS_1 = 58
SI5351_REGISTER_59_MULTISYNTH2_PARAMETERS_2 = 59
SI5351_REGISTER_60_MULTISYNTH2_PARAMETERS_3 = 60
SI5351_REGISTER_61_MULTISYNTH2_PARAMETERS_4 = 61
SI5351_REGISTER_62_MULTISYNTH2_PARAMETERS_5 = 62
SI5351_REGISTER_63_MULTISYNTH2_PARAMETERS_6 = 63
SI5351_REGISTER_64_MULTISYNTH2_PARAMETERS_7 = 64
SI5351_REGISTER_65_MULTISYNTH2_PARAMETERS_8 = 65
SI5351_REGISTER_66_MULTISYNTH3_PARAMETERS_1 = 66
SI5351_REGISTER_67_MULTISYNTH3_PARAMETERS_2 = 67
SI5351_REGISTER_68_MULTISYNTH3_PARAMETERS_3 = 68
SI5351_REGISTER_69_MULTISYNTH3_PARAMETERS_4 = 69
SI5351_REGISTER_70_MULTISYNTH3_PARAMETERS_5 = 70
SI5351_REGISTER_71_MULTISYNTH3_PARAMETERS_6 = 71
SI5351_REGISTER_72_MULTISYNTH3_PARAMETERS_7 = 72
SI5351_REGISTER_73_MULTISYNTH3_PARAMETERS_8 = 73
SI5351_REGISTER_74_MULTISYNTH4_PARAMETERS_1 = 74
SI5351_REGISTER_75_MULTISYNTH4_PARAMETERS_2 = 75
SI5351_REGISTER_76_MULTISYNTH4_PARAMETERS_3 = 76
SI5351_REGISTER_77_MULTISYNTH4_PARAMETERS_4 = 77
SI5351_REGISTER_78_MULTISYNTH4_PARAMETERS_5 = 78
SI5351_REGISTER_79_MULTISYNTH4_PARAMETERS_6 = 79
SI5351_REGISTER_80_MULTISYNTH4_PARAMETERS_7 = 80
SI5351_REGISTER_81_MULTISYNTH4_PARAMETERS_8 = 81
SI5351_REGISTER_82_MULTISYNTH5_PARAMETERS_1 = 82
SI5351_REGISTER_83_MULTISYNTH5_PARAMETERS_2 = 83
SI5351_REGISTER_84_MULTISYNTH5_PARAMETERS_3 = 84
SI5351_REGISTER_85_MULTISYNTH5_PARAMETERS_4 = 85
SI5351_REGISTER_86_MULTISYNTH5_PARAMETERS_5 = 86
SI5351_REGISTER_87_MULTISYNTH5_PARAMETERS_6 = 87
SI5351_REGISTER_88_MULTISYNTH5_PARAMETERS_7 = 88
SI5351_REGISTER_89_MULTISYNTH5_PARAMETERS_8 = 89
SI5351_REGISTER_90_MULTISYNTH6_PARAMETERS = 90
SI5351_REGISTER_91_MULTISYNTH7_PARAMETERS = 91
SI5351_REGISTER_092_CLOCK_6_7_OUTPUT_DIVIDER = 92
SI5351_REGISTER_165_CLK0_INITIAL_PHASE_OFFSET = 165
SI5351_REGISTER_166_CLK1_INITIAL_PHASE_OFFSET = 166
SI5351_REGISTER_167_CLK2_INITIAL_PHASE_OFFSET = 167
SI5351_REGISTER_168_CLK3_INITIAL_PHASE_OFFSET = 168
SI5351_REGISTER_169_CLK4_INITIAL_PHASE_OFFSET = 169
SI5351_REGISTER_170_CLK5_INITIAL_PHASE_OFFSET = 170
SI5351_REGISTER_177_PLL_RESET = 177
SI5351_REGISTER_183_CRYSTAL_INTERNAL_LOAD_CAPACITANCE = 183
SI5351_CRYSTAL_FREQ_25MHZ = 25000000
SI5351_CRYSTAL_FREQ_27MHZ = 27000000
SI5351_CRYSTAL_LOAD_6PF = 1<<6
SI5351_CRYSTAL_LOAD_8PF = 2<<6
SI5351_CRYSTAL_LOAD_10PF = 3<<6
si5351_15to92 = bytearray(b'\x00OOo\x80\x80\x80\x80\x80\x00\x00\x00\x05\x00\x0cf\x00\x00\x02\x02q\x00\x0c\x1a\x00\x00\x86\x00\x01\x00\x01\x00\x00\x00\x00\x00\x01\x00\x1c\x00\x00\x00\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
SI5351_MULTISYNTH_DIV_4 = 4
SI5351_MULTISYNTH_DIV_6 = 6
SI5351_MULTISYNTH_DIV_8 = 8
class SI5351:
def __init__( self, i2c, address=0x60, crystalFreq=25000000):
self.i2c = i2c
self.address = address
self.initialized = False
self.crystalFreq = crystalFreq
self.crystalLoad = SI5351_CRYSTAL_LOAD_10PF
self.crystalPPM = 30
self.plla_configured = False
self.plla_freq = 0
self.pllb_configured = False
self.pllb_freq = 0
return
def write8( self, register, value):
self.i2c.start()
buffera = bytearray(1)
buffera[0] = value & 0xff
self.i2c.writeto_mem( self.address, register, buffera)
self.i2c.stop()
return
def read8( self, register, value):
self.i2c.start()
buffera = bytearray(1)
self.i2c.readfrom_mem_into( self.address, register, buffera)
self.i2c.stop()
return
def begin( self):
self.write8( SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0xFF)
# Power down all output drivers */
self.write8(SI5351_REGISTER_16_CLK0_CONTROL, 0x80)
self.write8(SI5351_REGISTER_17_CLK1_CONTROL, 0x80)
self.write8(SI5351_REGISTER_18_CLK2_CONTROL, 0x80)
self.write8(SI5351_REGISTER_19_CLK3_CONTROL, 0x80)
self.write8(SI5351_REGISTER_20_CLK4_CONTROL, 0x80)
self.write8(SI5351_REGISTER_21_CLK5_CONTROL, 0x80)
self.write8(SI5351_REGISTER_22_CLK6_CONTROL, 0x80)
self.write8(SI5351_REGISTER_23_CLK7_CONTROL, 0x80)
# Set the load capacitance for the XTAL */
self.write8(SI5351_REGISTER_183_CRYSTAL_INTERNAL_LOAD_CAPACITANCE,
self.crystalLoad)
# Set interrupt masks as required (see Register 2 description in AN619).
# By default, ClockBuilder Desktop sets this register to 0x18.
# Note that the least significant nibble must remain 0x8, but the most
# significant nibble may be modified to suit your needs.
# Reset the PLL config fields just in case we call init again
self.plla_configured = False
self.plla_freq = 0
self.pllb_configured = False
self.pllb_freq = 0
# All done!
self.initialized = True
return
def setClockBuilderData(self ):
i = 0
# Make sure we've called init first
assert self.initialized == True, "you have not initialized the object"
# Disable all outputs setting CLKx_DIS high
self.write8( SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0xFF)
# Writes configuration data to device using the register map contents
# generated by ClockBuilder Desktop (registers 15-92 + 149-170)
for i, x in enumerate( range(15,93)):
#print( x, si5351_15to92[i] )
self.write8( x, si5351_15to92[i] )
for i in range(149, 171):
self.write8( i, 0x00)
# Apply soft reset
self.write8(SI5351_REGISTER_177_PLL_RESET, 0xAC)
# Enabled desired outputs (see Register 3)
self.write8(SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0x00)
return None
def setupPLL( self, mult, num, denom, pllsource = 'A'):
assert self.initialized == True, "you have not initialized the object"
assert ( (mult > 14) and (mult < 91) ), "invalid mult parameter"
assert denom > 0, "denom must be > 0"
assert num <= 0xfffff, "invalid parameter num"
assert denom <= 0xfffff, "invalid parameter denom"
if num ==0:
P1 = 128*mult -512
P2 = num
P3 = denom
else:
P1 = 128*mult + math.floor( 128 * num/denom ) -512
P2 = 128*num - denom * math.floor( 128 * num/denom)
P3 = denom
if pllsource == 'A':
baseaddr = 26
else:
baseaddr = 34
self.write8( baseaddr, (P3 & 0x0000FF00) >> 8)
self.write8( baseaddr+1, (P3 & 0x000000FF))
self.write8( baseaddr+2, (P1 & 0x00030000) >> 16)
self.write8( baseaddr+3, (P1 & 0x0000FF00) >> 8)
self.write8( baseaddr+4, (P1 & 0x000000FF))
self.write8( baseaddr+5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16) )
self.write8( baseaddr+6, (P2 & 0x0000FF00) >> 8)
self.write8( baseaddr+7, (P2 & 0x000000FF))
self.write8(SI5351_REGISTER_177_PLL_RESET, (1<<7) | (1<<5) )
if pllsource =='A':
fvco = self.crystalFreq*( mult + num/denom)
self.plla_configured = True
self.plla_freq = int(math.floor( fvco))
else:
fvco = self.crystalFreq*(mult + num/denom)
self.pllb_configured = True
self.pllb_freq = int(math.floor(fvco))
return None
def setupRdiv( self, output, div):
assert output in [0,1,2], "output value invalid"
assert div in [1,2,4,8,16,32,64,128], "div invalid"
divdict = {1: 0, 2: 1, 4: 2, 8: 3, 16: 4, 32: 5, 64: 6, 128: 7}
registers = [ 44, 52, 60]
Rreg = registers[output]
buf = bytearray( 1)
self.read8(Rreg, buf)
regval = buf[0] & 0x0F
divider = divdict[div]
divider &= 0x07
divider <<= 4
regval |= divider
self.write8(Rreg, regval)
return None
def setupMultisynth( self, output, div, num, denom, pllsource):
assert self.initialized == True, "device not initialized"
assert output in [0,1,2], "output out of range"
assert div > 3, "div out of range"
assert denom >0, "denom out of range"
assert num <= 0xfffff, "num has a 20-bit limit"
assert denom <= 0xfffff, "denom as a 20-bit limit"
if pllsource=="A":
assert self.plla_configured == True, "plla has not been configured"
else:
assert self.pllb_configured == True, 'pllb has not been configured'
# Output Multisynth Divider Equations
# where: a = div, b = num and c = denom
#
# P1 register is an 18-bit value using following formula:
#
# P1[17:0] = 128 * a + floor(128*(b/c)) - 512
#
# P2 register is a 20-bit value using the following formula:
#
# P2[19:0] = 128 * b - c * floor(128*(b/c))
#
# P3 register is a 20-bit value using the following formula:
#
# P3[19:0] = c
if num==0:
# integer mode
P1 = 128 *div -512
P2 = num
P3 = denom
else:
# Fractional mode */
P1 = int( 128 * div + math.floor(128 * (num/denom)) - 512 )
P2 = int( 128 * num - denom * math.floor(128 * (num/denom)))
P3 = denom
baseaddrs = [ 42, 50, 58]
baseaddr = baseaddrs[output]
self.write8( baseaddr, (P3 & 0x0000FF00) >> 8)
self.write8( baseaddr+1, (P3 & 0x000000FF))
self.write8( baseaddr+2, (P1 & 0x00030000) >> 16) # ToDo: Add DIVBY4 (>150MHz) and R0 support (<500kHz) later */
self.write8( baseaddr+3, (P1 & 0x0000FF00) >> 8)
self.write8( baseaddr+4, (P1 & 0x000000FF))
self.write8( baseaddr+5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16) )
self.write8( baseaddr+6, (P2 & 0x0000FF00) >> 8)
self.write8( baseaddr+7, (P2 & 0x000000FF))
# Configure the clk control and enable the output
clkControlReg = 0x0F # 8mA drive strength, MS0 as CLK0 source, Clock not inverted, powered up
if pllsource == 'B':
clkControlReg |= (1 << 5) # /* Uses PLLB */
if num == 0:
clkControlReg |= (1 << 6) # Integer mode */
if output == 0:
self.write8(SI5351_REGISTER_16_CLK0_CONTROL, clkControlReg)
if output == 1:
self.write8(SI5351_REGISTER_17_CLK1_CONTROL, clkControlReg)
if output == 2:
self.write8(SI5351_REGISTER_18_CLK2_CONTROL, clkControlReg)
def enableOutputs( self, enabled=True):
assert self.initialized == True, "Error Device not initialized"
if enabled:
self.write8( SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0x00)
else:
self.write8( SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0xff)
return

51
test_clockgen.py 100644
Wyświetl plik

@ -0,0 +1,51 @@
from machine import SPI, Pin, I2C
import SI5351
import time
i2c = I2C( scl=Pin(5), sda=Pin(4), freq=400000)
devlist = i2c.scan()
print(devlist)
clockgen = SI5351.SI5351( i2c)
clockgen.begin()
clockgen.setClockBuilderData()
# INTEGER ONLY MODE --> most accurate output */
# Setup PLLA to integer only mode @ 900MHz (must be 600..900MHz) */
# Set Multisynth 0 to 112.5MHz using integer only mode (div by 4/6/8) */
# 25MHz * 36 = 900 MHz, then 900 MHz / 8 = 112.5 MHz */
print("Set PLLA to 900MHz")
clockgen.setupPLL(36, 0, 1, pllsource='A')
print("Set Output #0 to {:2.2f}MHz".format(900/8))
clockgen.setupMultisynth( output=0, div=8, num=0, denom=1, pllsource="A")
# FRACTIONAL MODE --> More flexible but introduce clock jitter
# Setup PLLB to fractional mode @616.66667MHz (XTAL * 24 + 2/3)
# Setup Multisynth 1 to 13.55311MHz (PLLB/45.5) */
mult = 32
pllb = 25e6*mult
print( 'PLLB = {:5.2e} Hz'.format(pllb))
clockgen.setupPLL( mult, 0, 1, "B")
divider = 32
num2 = 2
denom2 =10
m2 = pllb/(divider+num2/denom2)
print( "Set Output #1 to {:5.4E} Hz".format(m2) )
clockgen.setupMultisynth( 1, divider, num2, denom2, pllsource="B")
# Multisynth 2 is not yet used and won't be enabled, but can be */
# Use PLLB @ 616.66667MHz, then divide by 900 -> 685.185 KHz */
# then divide by 64 for 10.706 KHz */
# configured using either PLL in either integer or fractional mode */
print("Set Output #2 to {:5.1e} Hz".format(400e6/200/2.0))
clockgen.setupMultisynth(2, 100, 0, 1, pllsource="B")
clockgen.setupRdiv(2, 4)
# Enable the clocks
clockgen.enableOutputs(True)