User Tools

Site Tools


altso3


AltSO3

Programming

Micropython code for programming the MS5351M. Not yet tested.

main.py
import machine
import time
 
I2C_ADDR = 0x60 # Default Si5351A I2C address
I2C_SDA_PIN = 8   # RP2040 GP8
I2C_SCL_PIN = 9   # RP2040 GP9
 
def setup_si5351(i2c):
    def write_reg(reg, val):
        i2c.writeto_mem(I2C_ADDR, reg, bytes([val]))
 
    # 1. Disable all outputs
    write_reg(3, 0xFF)
 
    # 2. Power down all output drivers
    for reg in range(16, 24):
        write_reg(reg, 0x80)
 
    # 3. Calculate and configure PLLA registers for 624 MHz
    # Multiplier: a=24, b=24, c=25
    a_pll, b_pll, c_pll = 24, 24, 25
    p1_pll = 128 * a_pll + int(128 * b_pll / c_pll) - 512
    p2_pll = 128 * b_pll - c_pll * int(128 * b_pll / c_pll)
    p3_pll = c_pll
 
    write_reg(26, (p3_pll >> 8) & 0xFF)
    write_reg(27, p3_pll & 0xFF)
    write_reg(28, ((p1_pll >> 16) & 0x03) | (((p2_pll >> 16) & 0x03) << 4))
    write_reg(29, (p1_pll >> 8) & 0xFF)
    write_reg(30, p1_pll & 0xFF)
    write_reg(31, (((p3_pll >> 16) & 0x0F) << 4) | ((p2_pll >> 16) & 0x0F))
    write_reg(32, (p2_pll >> 8) & 0xFF)
    write_reg(33, p2_pll & 0xFF)
 
    # 4. Calculate and configure MultiSynth 0 registers for divide-by-40
    # Divider: a=40, b=0, c=1
    a_ms, b_ms, c_ms = 40, 0, 1
    p1_ms = 128 * a_ms + int(128 * b_ms / c_ms) - 512
    p2_ms = 128 * b_ms - c_ms * int(128 * b_ms / c_ms)
    p3_ms = c_ms
 
    write_reg(42, (p3_ms >> 8) & 0xFF)
    write_reg(43, p3_ms & 0xFF)
    write_reg(44, ((p1_ms >> 16) & 0x03) | (((p2_ms >> 16) & 0x03) << 4))
    write_reg(45, (p1_ms >> 8) & 0xFF)
    write_reg(46, p1_ms & 0xFF)
    write_reg(47, (((p3_ms >> 16) & 0x0F) << 4) | ((p2_ms >> 16) & 0x0F))
    write_reg(48, (p2_ms >> 8) & 0xFF)
    write_reg(49, p2_ms & 0xFF)
 
    # 5. Connect MS0 to CLK0, Power Up, Integer Mode, PLLA Source, 8mA drive
    # 0x4F = 0b01001111
    write_reg(16, 0x4F)
 
    # 6. Reset PLLA
    write_reg(177, 0x20)
 
    # 7. Enable CLK0
    write_reg(3, 0xFE)
 
    print("Success: Si5351A configured to output 15.6 MHz on CLK0 in RAM.")
 
def burn_to_nvram(i2c):
    """
    WARNING: The Si5351A NVRAM is One-Time Programmable (OTP).
    This permanently burns the current RAM configuration into the chip.
    """
    print("⚠️ WARNING: Initiating NVRAM burn sequence...")
    print("This is a One-Time Programmable (OTP) operation!")
    time.sleep(3) # Give user a chance to interrupt execution (Ctrl+C) if accidental
 
    def write_reg(reg, val):
        i2c.writeto_mem(I2C_ADDR, reg, bytes([val]))
 
    # Write 0xC0 to register 161 (NVM_WRITE) to trigger NVRAM burn
    write_reg(161, 0xC0)
    time.sleep(1) # Wait for NVM burn to complete
    print("✅ NVRAM burn sequence completed. This configuration is now permanent.")
 
if __name__ == "__main__":
    # Initialize I2C bus 0
    i2c = machine.I2C(0, scl=machine.Pin(I2C_SCL_PIN), sda=machine.Pin(I2C_SDA_PIN), freq=400000)
 
    # 1. Configure the RAM first to test the output
    setup_si5351(i2c)
 
    # 2. ⚠️ DANGER ZONE: Uncomment the line below ONLY AFTER verifying the 15.6 MHz 
    # output with an oscilloscope or frequency counter.
    # burn_to_nvram(i2c)
mainv2.py
import machine
import time
 
I2C_ADDR = 0x60
I2C_SDA_PIN = 8
I2C_SCL_PIN = 9
 
# --- Si5351 Register Map ---
REG_ENABLE_CTRL = 3
REG_CLK0_CTRL = 16
REG_PLL_A_PARAMS = 26
REG_MS0_PARAMS = 42
REG_NVM_BURN = 161
REG_PLL_RESET = 177
REG_CRYSTAL_LOAD = 183
 
def calc_fractional_regs(a, b, c):
    """Calculates the 8 bytes of register data for Si5351 PLLs and MultiSynths."""
    # Use floor division (//) for exact integer math, avoiding float inaccuracies
    p1 = 128 * a + ((128 * b) // c) - 512
    p2 = 128 * b - c * ((128 * b) // c)
    p3 = c
 
    # Return as a bytearray so it can be sent in a single I2C block write
    return bytes([
        (p3 >> 8) & 0xFF,
        p3 & 0xFF,
        ((p1 >> 16) & 0x03) | (((p2 >> 16) & 0x03) << 4),
        (p1 >> 8) & 0xFF,
        p1 & 0xFF,
        (((p3 >> 16) & 0x0F) << 4) | ((p2 >> 16) & 0x0F),
        (p2 >> 8) & 0xFF,
        p2 & 0xFF
    ])
 
def setup(i2c):
    def write_reg(reg, val):
        i2c.writeto_mem(I2C_ADDR, reg, bytes([val]))
 
    # 1. Disable all outputs and power down clocks safely
    write_reg(REG_ENABLE_CTRL, 0xFF)
    for reg in range(16, 24):
        write_reg(reg, 0x80)
 
    # 2. Set Crystal Load Capacitance to 10pF
    # 0xD2 sets 10pF (11b) while preserving the required reserved bits (010010b)
    write_reg(REG_CRYSTAL_LOAD, 0xD2)
 
    # 3. Configure PLLA (Multiplier: a=24, b=24, c=25)
    plla_regs = calc_fractional_regs(24, 24, 25)
    i2c.writeto_mem(I2C_ADDR, REG_PLL_A_PARAMS, plla_regs)
 
    # 4. Configure MultiSynth0 (Divider: a=40, b=0, c=1)
    ms0_regs = calc_fractional_regs(40, 0, 1)
    i2c.writeto_mem(I2C_ADDR, REG_MS0_PARAMS, ms0_regs)
 
    # 5. Power up CLK0, set integer mode, route to PLLA
    write_reg(REG_CLK0_CTRL, 0x4F)
 
    # 6. Reset PLLA to apply frequency parameters
    write_reg(REG_PLL_RESET, 0x20)
 
    # 7. Re-enable CLK0 Output
    write_reg(REG_ENABLE_CTRL, 0xFE)
 
    print("Success: Si5351 configured for 15.6 MHz on CLK0.")
 
def burn(i2c):
    def write_reg(reg, val):
        i2c.writeto_mem(I2C_ADDR, reg, bytes([val]))
 
    print("Initiating NVM Burn sequence...")
    write_reg(REG_NVM_BURN, 0xC0)
    time.sleep(1) # Wait for NVM burn to complete
    print("NVM Burn complete. Settings are now permanent.")
 
if __name__ == "__main__":
    i2c = machine.I2C(0, scl=machine.Pin(I2C_SCL_PIN), sda=machine.Pin(I2C_SDA_PIN), freq=400000)
 
    setup(i2c)
 
    # Ensure VDD is stable at 3.3V before executing the burn process
    burn(i2c)
altso3.txt · Last modified: by admin

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki