Blame view
220222_final/EP/Si5351.py
13.9 KB
35833671e
|
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 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
from Adafruit_I2C import Adafruit_I2C import smbus import time 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_SPREAD_SPECTRUM = 149 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_I2C_ADDRESS_DEFAULT = 0x60 SI5351_CRYSTAL_LOAD_6PF = (1<<6) SI5351_CRYSTAL_LOAD_8PF = (2<<6) SI5351_CRYSTAL_LOAD_10PF = (3<<6) SI5351_CRYSTAL_FREQ_25MHZ = 25000000 SI5351_CRYSTAL_FREQ_27MHZ = 27000000 class Si5351(object): PLL_A = 0 PLL_B = 1 R_DIV_1 = 0 R_DIV_2 = 1 R_DIV_4 = 2 R_DIV_8 = 3 R_DIV_16 = 4 R_DIV_32 = 5 R_DIV_64 = 6 R_DIV_128 = 7 def __init__(self, address = SI5351_I2C_ADDRESS_DEFAULT, busnum=-1): self.crystalFreq = SI5351_CRYSTAL_FREQ_25MHZ self.crystalLoad = SI5351_CRYSTAL_LOAD_10PF self.crystalPPM = 30 self.plla_freq = 0 self.pllb_freq = 0 self.i2c = Adafruit_I2C(address=address, busnum=busnum, debug=True) self.address = address # Disable all outputs setting CLKx_DIS high self.i2c.write8(SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, 0xFF) # Power down all output drivers self.i2c.write8(SI5351_REGISTER_16_CLK0_CONTROL, 0x80) self.i2c.write8(SI5351_REGISTER_17_CLK1_CONTROL, 0x80) self.i2c.write8(SI5351_REGISTER_18_CLK2_CONTROL, 0x80) self.i2c.write8(SI5351_REGISTER_19_CLK3_CONTROL, 0x80) self.i2c.write8(SI5351_REGISTER_20_CLK4_CONTROL, 0x80) self.i2c.write8(SI5351_REGISTER_21_CLK5_CONTROL, 0x80) self.i2c.write8(SI5351_REGISTER_22_CLK6_CONTROL, 0x80) self.i2c.write8(SI5351_REGISTER_23_CLK7_CONTROL, 0x80) self.i2c.write8(SI5351_REGISTER_SPREAD_SPECTRUM, 0x00) # Set the load capacitance for the XTAL self.i2c.write8(SI5351_REGISTER_183_CRYSTAL_INTERNAL_LOAD_CAPACITANCE, self.crystalLoad) def setupPLL(self, pll, mult, num=0, denom=1): # @brief Sets the multiplier for the specified PLL # @param pll The PLL to configure, which must be one of the following: # - SI5351_PLL_A # - SI5351_PLL_B # @param mult The PLL integer multiplier (must be between 15 and 90) # @param num The 20-bit numerator for fractional output (0..1,048,575). # Set this to '0' for integer output. # @param denom The 20-bit denominator for fractional output (1..1,048,575). # Set this to '1' or higher to avoid divider by zero errors. # @section PLL Configuration # fVCO is the PLL output, and must be between 600..900MHz, where: # fVCO = fXTAL * (a+(b/c)) # fXTAL = the crystal input frequency # a = an integer between 15 and 90 # b = the fractional numerator (0..1,048,575) # c = the fractional denominator (1..1,048,575) # @note Try to use integers whenever possible to avoid clock jitter # (only use the a part, setting b to '0' and c to '1'). # See: http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf # # Feedback Multisynth Divider Equation # where: a = mult, b = num and c = denom # P1[17:0] = 128 * mult + floor(128*(num/denom)) - 512 # P2[19:0] = 128 * num - denom * floor(128*(num/denom)) # P3[19:0] = denom # Set the main PLL config registers P1 = 128 * mult + int(128.0 * num / denom) - 512 P2 = 128 * num - denom * int(128.0 * num / denom) P3 = denom print(str(P1)+" "+str(P2)+" "+str(P3)) # Get the appropriate starting point for the PLL registers baseaddr = 26 if pll == self.PLL_A else 34 # The datasheet is a nightmare of typos and inconsistencies here! self.i2c.write8(baseaddr, (P3 & 0x0000FF00) >> 8) self.i2c.write8(baseaddr + 1, (P3 & 0x000000FF)) self.i2c.write8(baseaddr + 2, (P1 & 0x00030000) >> 16) self.i2c.write8(baseaddr + 3, (P1 & 0x0000FF00) >> 8) self.i2c.write8(baseaddr + 4, (P1 & 0x000000FF)) self.i2c.write8(baseaddr + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16) ) self.i2c.write8(baseaddr + 6, (P2 & 0x0000FF00) >> 8) self.i2c.write8(baseaddr + 7, (P2 & 0x000000FF)) # Reset both PLLs self.i2c.write8(SI5351_REGISTER_177_PLL_RESET, (1<<7) | (1<<5)) # Store the frequency settings for use with the Multisynth helper fvco = int(self.crystalFreq * (mult + float(num) / denom)) if pll == self.PLL_A: self.plla_freq = fvco else: self.pllb_freq = fvco def setupRdiv(self, output, div): if output == 0: Rreg = SI5351_REGISTER_44_MULTISYNTH0_PARAMETERS_3 if output == 1: Rreg = SI5351_REGISTER_52_MULTISYNTH1_PARAMETERS_3 if output == 2: Rreg = SI5351_REGISTER_60_MULTISYNTH2_PARAMETERS_3 return self.i2c.write8(Rreg, (div & 0x07) << 4) def setupMultisynth(self, output, pll, div, num=0, denom=1): # @brief Configures the Multisynth divider, which determines the # output clock frequency based on the specified PLL input. # # @param output The output channel to use (0..2) # @param pll The PLL input source to use, which must be one of: # - SI5351_PLL_A # - SI5351_PLL_B # @param div The integer divider for the Multisynth output. # If pure integer values are used, this value must # be one of: # - SI5351_MULTISYNTH_DIV_4 # - SI5351_MULTISYNTH_DIV_6 # - SI5351_MULTISYNTH_DIV_8 # If fractional output is used, this value must be # between 8 and 900. # @param num The 20-bit numerator for fractional output # (0..1,048,575). Set this to '0' for integer output. # @param denom The 20-bit denominator for fractional output # (1..1,048,575). Set this to '1' or higher to # avoid divide by zero errors. # # @section Output Clock Configuration # # The multisynth dividers are applied to the specified PLL output, # and are used to reduce the PLL output to a valid range (500kHz # to 160MHz). The relationship can be seen in this formula, where # fVCO is the PLL output frequency and MSx is the multisynth # divider: # fOUT = fVCO / MSx # Valid multisynth dividers are 4, 6, or 8 when using integers, # or any fractional values between 8 + 1/1,048,575 and 900 + 0/1 # The following formula is used for the fractional mode divider: # a + b / c # a = The integer value, which must be 4, 6 or 8 in integer mode (MSx_INT=1) # or 8..900 in fractional mode (MSx_INT=0). # b = The fractional numerator (0..1,048,575) # c = The fractional denominator (1..1,048,575) # @note Try to use integers whenever possible to avoid clock jitter # @note For output frequencies > 150MHz, you must set the divider # to 4 and adjust to PLL to generate the frequency (for example # a PLL of 640 to generate a 160MHz output clock). This is not # yet supported in the driver, which limits frequencies to # 500kHz .. 150MHz. # @note For frequencies below 500kHz (down to 8kHz) Rx_DIV must be # used, but this isn't currently implemented in the driver. # # Output Multisynth Divider Equations # where: a = div, b = num and c = denom # P1[17:0] = 128 * a + floor(128*(b/c)) - 512 # P2[19:0] = 128 * b - c * floor(128*(b/c)) # P3[19:0] = c # Set the main PLL config registers P1 = 128 * div + int(128.0 * num / denom) - 512 P2 = 128 * num - denom * int(128.0 * num / denom) P3 = denom # Get the appropriate starting point for the PLL registers if output == 0: baseaddr = SI5351_REGISTER_42_MULTISYNTH0_PARAMETERS_1 if output == 1: baseaddr = SI5351_REGISTER_50_MULTISYNTH1_PARAMETERS_1 if output == 2: baseaddr = SI5351_REGISTER_58_MULTISYNTH2_PARAMETERS_1 # Set the MSx config registers self.i2c.write8(baseaddr, (P3 & 0x0000FF00) >> 8) self.i2c.write8(baseaddr + 1, (P3 & 0x000000FF)) self.i2c.write8(baseaddr + 2, (P1 & 0x00030000) >> 16) # ToDo: Add DIVBY4 (>150MHz) and R0 support (<500kHz) later self.i2c.write8(baseaddr + 3, (P1 & 0x0000FF00) >> 8) self.i2c.write8(baseaddr + 4, (P1 & 0x000000FF)) self.i2c.write8(baseaddr + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16) ) self.i2c.write8(baseaddr + 6, (P2 & 0x0000FF00) >> 8) self.i2c.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 pll == self.PLL_B: clkControlReg |= (1 << 5) # Uses PLLB if num == 0: clkControlReg |= (1 << 6) # Integer mode if output == 0: self.i2c.write8(SI5351_REGISTER_16_CLK0_CONTROL, clkControlReg) if output == 1: self.i2c.write8(SI5351_REGISTER_17_CLK1_CONTROL, clkControlReg) if output == 2: self.i2c.write8(SI5351_REGISTER_18_CLK2_CONTROL, clkControlReg) def enableOutputs(self, enabled): # Enabled desired outputs (see Register 3) val = 0x00 if enabled else 0xFF self.i2c.write8(SI5351_REGISTER_3_OUTPUT_ENABLE_CONTROL, val) |