si5351: complete refactor for more complete interface

This completely refactors the interface and implementation
for the si5351 clock generator. The interface based on the
Arduino implementation was both somewhat hard to work with
and also missing a number of important features that are
needed to use this chip for RF communication.

Instead this new implementation draws inspiration from the
efforts of the Traquino community mostly using the rp2040
processor.

The TinyGo implementation is based on the patterns and code
in the drivers repo for other i2c devices. It also includes
some basic unit tests which are not comprehensive but at
least provide some coverage.

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram
2026-01-06 10:25:09 +01:00
committed by Ron Evans
parent a35786be70
commit c21cd39813
4 changed files with 1388 additions and 687 deletions
+33 -65
View File
@@ -29,92 +29,60 @@ func main() {
// Create driver instance
clockgen := si5351.New(machine.I2C0)
// Verify device wired properly
connected, err := clockgen.Connected()
if err != nil {
println("Unable to read device status")
time.Sleep(time.Second)
}
if !connected {
for {
println("Unable to detect si5351 device")
time.Sleep(time.Second)
}
// Initialize device
cnf := si5351.Config{
Capacitance: si5351.CrystalLoad10PF,
}
// Initialise device
clockgen.Configure()
if err := clockgen.Configure(cnf); err != nil {
println("Failed to configure Si5351:", err.Error())
return
}
println("Si5351 configured")
// Now configue the PLLs and clock outputs.
// The PLLs can be configured with a multiplier and division of the on-board
// 25mhz reference crystal. For example configure PLL A to 900mhz by multiplying
// by 36. This uses an integer multiplier which is more accurate over time
// but allows less of a range of frequencies compared to a fractional
// multiplier shown next.
clockgen.ConfigurePLL(si5351.PLL_A, 36, 0, 1) // Multiply 25mhz by 36
println("PLL A frequency: 900mhz")
// And next configure PLL B to 616.6667mhz by multiplying 25mhz by 24.667 using
// the fractional multiplier configuration. Notice you specify the integer
// multiplier and then a numerator and denominator as separate values, i.e.
// numerator 2 and denominator 3 means 2/3 or 0.667. This fractional
// configuration is susceptible to some jitter over time but can set a larger
// range of frequencies.
clockgen.ConfigurePLL(si5351.PLL_B, 24, 2, 3) // Multiply 25mhz by 24.667 (24 2/3)
println("PLL B frequency: 616.6667mhz")
// Now configure the clock outputs. Each is driven by a PLL frequency as input
// and then further divides that down to a specific frequency.
// Configure clock 0 output to be driven by PLL A divided by 8, so an output
// of 112.5mhz (900mhz / 8). Again this uses the most precise integer division
// but can't set as wide a range of values.
clockgen.ConfigureMultisynth(0, si5351.PLL_A, 8, 0, 1) // Divide by 8 (8 0/1)
// Now configure the clock outputs.
clockgen.SetFrequency(si5351.Clock0, 112_500_000)
println("Clock 0: 112.5mhz")
// Next configure clock 1 to be driven by PLL B divided by 45.5 to get
// 13.5531mhz (616.6667mhz / 45.5). This uses fractional division and again
// notice the numerator and denominator are explicitly specified. This is less
// precise but allows a large range of frequencies.
clockgen.ConfigureMultisynth(1, si5351.PLL_B, 45, 1, 2) // Divide by 45.5 (45 1/2)
// Next configure clock 1 for 13.5531mhz (616.6667mhz / 45.5).
// This uses fractional division.
clockgen.SetFrequency(si5351.Clock1, 13_553_125)
println("Clock 1: 13.5531mhz")
// Finally configure clock 2 to be driven by PLL B divided once by 900 to get
// down to 685.15 khz and then further divided by a special R divider that
// divides 685.15 khz by 64 to get a final output of 10.706khz.
clockgen.ConfigureMultisynth(2, si5351.PLL_B, 900, 0, 1) // Divide by 900 (900 0/1)
// Set the R divider, this can be a value of:
// - R_DIV_1: divider of 1
// - R_DIV_2: divider of 2
// - R_DIV_4: divider of 4
// - R_DIV_8: divider of 8
// - R_DIV_16: divider of 16
// - R_DIV_32: divider of 32
// - R_DIV_64: divider of 64
// - R_DIV_128: divider of 128
clockgen.ConfigureRdiv(2, si5351.R_DIV_64)
// Finally configure clock 2 to output of 10.706khz.
clockgen.SetFrequency(si5351.Clock2, 10_706)
println("Clock 2: 10.706khz")
// After configuring PLLs and clocks, enable the outputs.
clockgen.EnableOutputs()
// After configuring the clocks enable the outputs.
clockgen.EnableOutput(si5351.Clock0, true)
clockgen.EnableOutput(si5351.Clock1, true)
clockgen.EnableOutput(si5351.Clock2, true)
println("All outputs enabled")
time.Sleep(time.Second)
clockgen.DisableOutputs()
clockgen.EnableOutput(si5351.Clock0, false)
clockgen.EnableOutput(si5351.Clock1, false)
clockgen.EnableOutput(si5351.Clock2, false)
println("All outputs disabled for 5 seconds")
time.Sleep(5 * time.Second)
// Now use SetFrequency to re-set the frequencies of the outputs
// Now turn clock outputs on and off repeatedly
on := false
for {
if on {
println("Setting Clock 0 output off")
clockgen.OutputEnable(0, false)
println("Setting clock outputs off")
clockgen.EnableOutput(si5351.Clock0, false)
clockgen.EnableOutput(si5351.Clock1, false)
clockgen.EnableOutput(si5351.Clock2, false)
on = false
} else {
println("Setting Clock 0 output to 100mhz")
clockgen.SetFrequency(100*machine.MHz, 0, si5351.PLL_A)
println("Setting clock outputs on")
clockgen.EnableOutput(si5351.Clock0, true)
clockgen.EnableOutput(si5351.Clock1, true)
clockgen.EnableOutput(si5351.Clock2, true)
on = true
}
time.Sleep(5 * time.Second)
time.Sleep(1 * time.Second)
}
}