altso3
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| altso3 [2026/06/16 02:39] – admin | altso3 [2026/07/18 04:17] (current) – admin | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== AltSO3 ====== | ====== AltSO3 ====== | ||
| + | Exploring modern alternatives to the Kenwood SO-3 TCXO. Watch this space, maybe? | ||
| - | ==== Programming ==== | + | {{:altso3:altso3_v001.jpeg? |
| - | Micropython code for programming the MS5351M. Not yet tested. | + | |
| - | + | ||
| - | <file python 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, | + | |
| - | i2c.writeto_mem(I2C_ADDR, | + | |
| - | + | ||
| - | # 1. Disable all outputs | + | |
| - | write_reg(3, | + | |
| - | + | ||
| - | # 2. Power down all output drivers | + | |
| - | for reg in range(16, 24): | + | |
| - | write_reg(reg, | + | |
| - | + | ||
| - | # 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, | + | |
| - | write_reg(27, | + | |
| - | write_reg(28, | + | |
| - | write_reg(29, | + | |
| - | write_reg(30, | + | |
| - | write_reg(31, | + | |
| - | write_reg(32, | + | |
| - | write_reg(33, | + | |
| - | + | ||
| - | # 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, | + | |
| - | write_reg(43, | + | |
| - | write_reg(44, | + | |
| - | write_reg(45, | + | |
| - | write_reg(46, | + | |
| - | write_reg(47, | + | |
| - | write_reg(48, | + | |
| - | write_reg(49, | + | |
| - | + | ||
| - | # 5. Connect MS0 to CLK0, Power Up, Integer Mode, PLLA Source, 8mA drive | + | |
| - | # 0x4F = 0b01001111 | + | |
| - | write_reg(16, | + | |
| - | + | ||
| - | # 6. Reset PLLA | + | |
| - | write_reg(177, | + | |
| - | + | ||
| - | # 7. Enable CLK0 | + | |
| - | write_reg(3, | + | |
| - | + | ||
| - | print(" | + | |
| - | + | ||
| - | 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(" | + | |
| - | print(" | + | |
| - | time.sleep(3) # Give user a chance to interrupt execution (Ctrl+C) if accidental | + | |
| - | + | ||
| - | def write_reg(reg, | + | |
| - | i2c.writeto_mem(I2C_ADDR, | + | |
| - | + | ||
| - | # Write 0xC0 to register 161 (NVM_WRITE) to trigger NVRAM burn | + | |
| - | write_reg(161, | + | |
| - | time.sleep(1) # Wait for NVM burn to complete | + | |
| - | print(" | + | |
| - | + | ||
| - | if __name__ == " | + | |
| - | # Initialize I2C bus 0 | + | |
| - | i2c = machine.I2C(0, | + | |
| - | + | ||
| - | # 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) | + | |
| - | </ | + | |
altso3.1781577567.txt.gz · Last modified: by admin
