mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 02:28:41 +00:00
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:
+33
-65
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+145
-66
@@ -5,75 +5,154 @@ const AddressDefault = 0x60 // Assumes ADDR pin is low
|
||||
const AddressAlternative = 0x61 // Assumes ADDR pin is high
|
||||
|
||||
const (
|
||||
OUTPUT_ENABLE_CONTROL = 3
|
||||
XTAL_FREQ = 25000000
|
||||
PLL_FIXED = 80000000000
|
||||
FREQ_MULT = 100
|
||||
DEFAULT_CLK = 1000000000
|
||||
|
||||
CLK0_CONTROL = 16
|
||||
CLK1_CONTROL = 17
|
||||
CLK2_CONTROL = 18
|
||||
CLK3_CONTROL = 19
|
||||
CLK4_CONTROL = 20
|
||||
CLK5_CONTROL = 21
|
||||
CLK6_CONTROL = 22
|
||||
CLK7_CONTROL = 23
|
||||
PLL_VCO_MIN = 600000000
|
||||
PLL_VCO_MAX = 900000000
|
||||
MULTISYNTH_MIN_FREQ = 500000
|
||||
MULTISYNTH_DIVBY4_FREQ = 150000000
|
||||
MULTISYNTH_MAX_FREQ = 225000000
|
||||
MULTISYNTH_SHARE_MAX = 100000000
|
||||
MULTISYNTH_SHARE_MIN = 1024000
|
||||
MULTISYNTH67_MAX_FREQ = MULTISYNTH_DIVBY4_FREQ
|
||||
CLKOUT_MIN_FREQ = 4000
|
||||
CLKOUT_MAX_FREQ = MULTISYNTH_MAX_FREQ
|
||||
CLKOUT67_MS_MIN = PLL_VCO_MIN / MULTISYNTH67_A_MAX
|
||||
CLKOUT67_MIN_FREQ = CLKOUT67_MS_MIN / 128
|
||||
CLKOUT67_MAX_FREQ = MULTISYNTH67_MAX_FREQ
|
||||
|
||||
MULTISYNTH0_PARAMETERS_1 = 42
|
||||
MULTISYNTH0_PARAMETERS_3 = 44
|
||||
MULTISYNTH1_PARAMETERS_1 = 50
|
||||
MULTISYNTH1_PARAMETERS_3 = 52
|
||||
MULTISYNTH2_PARAMETERS_1 = 58
|
||||
MULTISYNTH2_PARAMETERS_3 = 60
|
||||
PLL_A_MIN = 15
|
||||
PLL_A_MAX = 90
|
||||
PLL_B_MAX = PLL_C_MAX - 1
|
||||
PLL_C_MAX = 1048575
|
||||
MULTISYNTH_A_MIN = 6
|
||||
MULTISYNTH_A_MAX = 1800
|
||||
MULTISYNTH67_A_MAX = 254
|
||||
MULTISYNTH_B_MAX = MULTISYNTH_C_MAX - 1
|
||||
MULTISYNTH_C_MAX = 1048575
|
||||
MULTISYNTH_P1_MAX = (1 << 18) - 1
|
||||
MULTISYNTH_P2_MAX = (1 << 20) - 1
|
||||
MULTISYNTH_P3_MAX = (1 << 20) - 1
|
||||
VCXO_PULL_MIN = 30
|
||||
VCXO_PULL_MAX = 240
|
||||
VCXO_MARGIN = 103
|
||||
|
||||
SPREAD_SPECTRUM_PARAMETERS = 149
|
||||
DEVICE_STATUS = 0
|
||||
INTERRUPT_STATUS = 1
|
||||
INTERRUPT_MASK = 2
|
||||
STATUS_SYS_INIT = 1 << 7
|
||||
STATUS_LOL_B = 1 << 6
|
||||
STATUS_LOL_A = 1 << 5
|
||||
STATUS_LOS = 1 << 4
|
||||
OUTPUT_ENABLE_CTRL = 3
|
||||
OEB_PIN_ENABLE_CTRL = 9
|
||||
PLL_INPUT_SOURCE = 15
|
||||
CLKIN_DIV_MASK = 3 << 6
|
||||
CLKIN_DIV_1 = 0 << 6
|
||||
CLKIN_DIV_2 = 1 << 6
|
||||
CLKIN_DIV_4 = 2 << 6
|
||||
CLKIN_DIV_8 = 3 << 6
|
||||
PLLB_SOURCE = 1 << 3
|
||||
PLLA_SOURCE = 1 << 2
|
||||
|
||||
PLL_RESET = 177
|
||||
CLK0_CTRL = 16
|
||||
CLK1_CTRL = 17
|
||||
CLK2_CTRL = 18
|
||||
CLK3_CTRL = 19
|
||||
CLK4_CTRL = 20
|
||||
CLK5_CTRL = 21
|
||||
CLK6_CTRL = 22
|
||||
CLK7_CTRL = 23
|
||||
CLK_POWERDOWN = 1 << 7
|
||||
CLK_INTEGER_MODE = 1 << 6
|
||||
CLK_PLL_SELECT = 1 << 5
|
||||
CLK_INVERT = 1 << 4
|
||||
CLK_INPUT_MASK = 3 << 2
|
||||
CLK_INPUT_XTAL = 0 << 2
|
||||
CLK_INPUT_CLKIN = 1 << 2
|
||||
CLK_INPUT_MULTISYNTH_0_4 = 2 << 2
|
||||
CLK_INPUT_MULTISYNTH_N = 3 << 2
|
||||
CLK_DRIVE_STRENGTH_MASK = 3 << 0
|
||||
CLK_DRIVE_STRENGTH_2MA = 0 << 0
|
||||
CLK_DRIVE_STRENGTH_4MA = 1 << 0
|
||||
CLK_DRIVE_STRENGTH_6MA = 2 << 0
|
||||
CLK_DRIVE_STRENGTH_8MA = 3 << 0
|
||||
|
||||
CRYSTAL_INTERNAL_LOAD_CAPACITANCE = 183
|
||||
)
|
||||
|
||||
const (
|
||||
CRYSTAL_LOAD_6PF = (1 << 6)
|
||||
CRYSTAL_LOAD_8PF = (2 << 6)
|
||||
CRYSTAL_LOAD_10PF = (3 << 6)
|
||||
)
|
||||
|
||||
const (
|
||||
CRYSTAL_FREQ_25MHZ = 25000000
|
||||
CRYSTAL_FREQ_27MHZ = 27000000
|
||||
)
|
||||
|
||||
const (
|
||||
PLL_A = iota
|
||||
PLL_B
|
||||
)
|
||||
|
||||
const (
|
||||
R_DIV_1 = iota
|
||||
R_DIV_2
|
||||
R_DIV_4
|
||||
R_DIV_8
|
||||
R_DIV_16
|
||||
R_DIV_32
|
||||
R_DIV_64
|
||||
R_DIV_128
|
||||
)
|
||||
|
||||
const (
|
||||
MULTISYNTH_DIV_4 = 4
|
||||
MULTISYNTH_DIV_6 = 6
|
||||
MULTISYNTH_DIV_8 = 8
|
||||
)
|
||||
|
||||
// Frequency constants (in Hz)
|
||||
const (
|
||||
CLKOUT_MIN_FREQ = 8000 // 8 kHz
|
||||
CLKOUT_MAX_FREQ = 150000000 // 150 MHz
|
||||
MULTISYNTH_MAX_FREQ = 150000000 // 150 MHz
|
||||
MULTISYNTH_SHARE_MAX = 100000000 // 100 MHz
|
||||
MULTISYNTH_DIVBY4_FREQ = 150000000 // 150 MHz
|
||||
PLL_VCO_MIN = 600000000 // 600 MHz
|
||||
PLL_VCO_MAX = 900000000 // 900 MHz
|
||||
)
|
||||
|
||||
const (
|
||||
SI5351_PLL_C_MAX = 1048575
|
||||
CLK3_0_DISABLE_STATE = 24
|
||||
CLK7_4_DISABLE_STATE = 25
|
||||
CLK_DISABLE_STATE_MASK = 3
|
||||
CLK_DISABLE_STATE_LOW = 0
|
||||
CLK_DISABLE_STATE_HIGH = 1
|
||||
CLK_DISABLE_STATE_FLOAT = 2
|
||||
CLK_DISABLE_STATE_NEVER = 3
|
||||
|
||||
PARAMETERS_LENGTH = 8
|
||||
PLLA_PARAMETERS = 26
|
||||
PLLB_PARAMETERS = 34
|
||||
CLK0_PARAMETERS = 42
|
||||
CLK1_PARAMETERS = 50
|
||||
CLK2_PARAMETERS = 58
|
||||
CLK3_PARAMETERS = 66
|
||||
CLK4_PARAMETERS = 74
|
||||
CLK5_PARAMETERS = 82
|
||||
CLK6_PARAMETERS = 90
|
||||
CLK7_PARAMETERS = 91
|
||||
CLK6_7_OUTPUT_DIVIDER = 92
|
||||
OUTPUT_CLK_DIV_MASK = 7 << 4
|
||||
OUTPUT_CLK6_DIV_MASK = 7 << 0
|
||||
OUTPUT_CLK_DIV_SHIFT = 4
|
||||
OUTPUT_CLK_DIV6_SHIFT = 0
|
||||
OUTPUT_CLK_DIV_1 = 0
|
||||
OUTPUT_CLK_DIV_2 = 1
|
||||
OUTPUT_CLK_DIV_4 = 2
|
||||
OUTPUT_CLK_DIV_8 = 3
|
||||
OUTPUT_CLK_DIV_16 = 4
|
||||
OUTPUT_CLK_DIV_32 = 5
|
||||
OUTPUT_CLK_DIV_64 = 6
|
||||
OUTPUT_CLK_DIV_128 = 7
|
||||
OUTPUT_CLK_DIVBY4 = 3 << 2
|
||||
|
||||
SSC_PARAM0 = 149
|
||||
SSC_PARAM1 = 150
|
||||
SSC_PARAM2 = 151
|
||||
SSC_PARAM3 = 152
|
||||
SSC_PARAM4 = 153
|
||||
SSC_PARAM5 = 154
|
||||
SSC_PARAM6 = 155
|
||||
SSC_PARAM7 = 156
|
||||
SSC_PARAM8 = 157
|
||||
SSC_PARAM9 = 158
|
||||
SSC_PARAM10 = 159
|
||||
SSC_PARAM11 = 160
|
||||
SSC_PARAM12 = 161
|
||||
|
||||
VXCO_PARAMETERS_LOW = 162
|
||||
VXCO_PARAMETERS_MID = 163
|
||||
VXCO_PARAMETERS_HIGH = 164
|
||||
|
||||
CLK0_PHASE_OFFSET = 165
|
||||
CLK1_PHASE_OFFSET = 166
|
||||
CLK2_PHASE_OFFSET = 167
|
||||
CLK3_PHASE_OFFSET = 168
|
||||
CLK4_PHASE_OFFSET = 169
|
||||
CLK5_PHASE_OFFSET = 170
|
||||
|
||||
PLL_RESET = 177
|
||||
PLL_RESET_B = 1 << 7
|
||||
PLL_RESET_A = 1 << 5
|
||||
|
||||
CRYSTAL_LOAD = 183
|
||||
CRYSTAL_LOAD_MASK = 3 << 6
|
||||
CRYSTAL_LOAD_0PF = 0 << 6
|
||||
CRYSTAL_LOAD_6PF = 1 << 6
|
||||
CRYSTAL_LOAD_8PF = 2 << 6
|
||||
CRYSTAL_LOAD_10PF = 3 << 6
|
||||
|
||||
FANOUT_ENABLE = 187
|
||||
CLKIN_ENABLE = 1 << 7
|
||||
XTAL_ENABLE = 1 << 6
|
||||
MULTISYNTH_ENABLE = 1 << 4
|
||||
)
|
||||
|
||||
+996
-556
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,214 @@
|
||||
package si5351
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSelectRDiv(t *testing.T) {
|
||||
d := &Device{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
freq Frequency
|
||||
wantDiv uint8
|
||||
wantFreq Frequency
|
||||
}{
|
||||
{"4kHz", 4000 * FREQ_MULT, OUTPUT_CLK_DIV_128, 4000 * FREQ_MULT * 128},
|
||||
{"8kHz", 8000 * FREQ_MULT, OUTPUT_CLK_DIV_64, 8000 * FREQ_MULT * 64},
|
||||
{"16kHz", 16000 * FREQ_MULT, OUTPUT_CLK_DIV_32, 16000 * FREQ_MULT * 32},
|
||||
{"32kHz", 32000 * FREQ_MULT, OUTPUT_CLK_DIV_16, 32000 * FREQ_MULT * 16},
|
||||
{"64kHz", 64000 * FREQ_MULT, OUTPUT_CLK_DIV_8, 64000 * FREQ_MULT * 8},
|
||||
{"128kHz", 128000 * FREQ_MULT, OUTPUT_CLK_DIV_4, 128000 * FREQ_MULT * 4},
|
||||
{"256kHz", 256000 * FREQ_MULT, OUTPUT_CLK_DIV_2, 256000 * FREQ_MULT * 2},
|
||||
{"512kHz", 512000 * FREQ_MULT, OUTPUT_CLK_DIV_1, 512000 * FREQ_MULT},
|
||||
{"1MHz", 1000000 * FREQ_MULT, OUTPUT_CLK_DIV_1, 1000000 * FREQ_MULT},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
freq := tt.freq
|
||||
freq, gotDiv := d.selectRDiv(freq)
|
||||
if gotDiv != tt.wantDiv {
|
||||
t.Errorf("selectRDiv() div = %v, want %v", gotDiv, tt.wantDiv)
|
||||
}
|
||||
if freq != tt.wantFreq {
|
||||
t.Errorf("selectRDiv() freq = %v, want %v", freq, tt.wantFreq)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectRDivMS67(t *testing.T) {
|
||||
d := &Device{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
freq Frequency
|
||||
wantDiv uint8
|
||||
wantFreq Frequency
|
||||
}{
|
||||
{"4kHz", 4000 * FREQ_MULT, OUTPUT_CLK_DIV_128, 4000 * FREQ_MULT * 128},
|
||||
{"8kHz", 8000 * FREQ_MULT, OUTPUT_CLK_DIV_64, 8000 * FREQ_MULT * 64},
|
||||
{"16kHz", 16000 * FREQ_MULT, OUTPUT_CLK_DIV_32, 16000 * FREQ_MULT * 32},
|
||||
{"64kHz", 64000 * FREQ_MULT, OUTPUT_CLK_DIV_8, 64000 * FREQ_MULT * 8},
|
||||
{"256kHz", 256000 * FREQ_MULT, OUTPUT_CLK_DIV_2, 256000 * FREQ_MULT * 2},
|
||||
{"512kHz", 512000 * FREQ_MULT, OUTPUT_CLK_DIV_1, 512000 * FREQ_MULT},
|
||||
{"1MHz", 1000000 * FREQ_MULT, OUTPUT_CLK_DIV_1, 1000000 * FREQ_MULT},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
freq := tt.freq
|
||||
freq, gotDiv := d.selectRDivMS67(freq)
|
||||
if gotDiv != tt.wantDiv {
|
||||
t.Errorf("selectRDivMS67() div = %v, want %v", gotDiv, tt.wantDiv)
|
||||
}
|
||||
if freq != tt.wantFreq {
|
||||
t.Errorf("selectRDivMS67() freq = %v, want %v", freq, tt.wantFreq)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculatePLL(t *testing.T) {
|
||||
d := &Device{}
|
||||
d.crystalFreq[0] = 25000000
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
freq Frequency
|
||||
wantMin Frequency
|
||||
wantMax Frequency
|
||||
}{
|
||||
{"600MHz", 600000000 * FREQ_MULT, 599000000 * FREQ_MULT, 601000000 * FREQ_MULT},
|
||||
{"750MHz", 750000000 * FREQ_MULT, 749000000 * FREQ_MULT, 751000000 * FREQ_MULT},
|
||||
{"900MHz", 900000000 * FREQ_MULT, 899000000 * FREQ_MULT, 901000000 * FREQ_MULT},
|
||||
{"BelowMin", 500000000 * FREQ_MULT, 600000000 * FREQ_MULT, 600000000 * FREQ_MULT},
|
||||
{"AboveMax", 1000000000 * FREQ_MULT, 900000000 * FREQ_MULT, 900000000 * FREQ_MULT},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, reg := d.CalculatePLL(PLL_A, tt.freq, 0, false)
|
||||
if got < tt.wantMin || got > tt.wantMax {
|
||||
t.Errorf("CalculatePLL() = %v, want between %v and %v", got, tt.wantMin, tt.wantMax)
|
||||
}
|
||||
if reg.p1 == 0 || reg.p3 == 0 {
|
||||
t.Errorf("CalculatePLL() invalid register values: p1=%v, p2=%v, p3=%v", reg.p1, reg.p2, reg.p3)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateMultisynth(t *testing.T) {
|
||||
d := &Device{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
freq Frequency
|
||||
pllFreq Frequency
|
||||
wantDiv bool
|
||||
}{
|
||||
{"10MHz from 800MHz", 10000000 * FREQ_MULT, 800000000 * FREQ_MULT, false},
|
||||
{"1MHz from 800MHz", 1000000 * FREQ_MULT, 800000000 * FREQ_MULT, false},
|
||||
{"Auto PLL 10MHz", 10000000 * FREQ_MULT, 0, false},
|
||||
{"150MHz DivBy4", 150000000 * FREQ_MULT, 600000000 * FREQ_MULT, true},
|
||||
{"BelowMin", 100000 * FREQ_MULT, 800000000 * FREQ_MULT, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, reg := d.CalculateMultisynth(tt.freq, tt.pllFreq)
|
||||
if tt.pllFreq == 0 {
|
||||
// Auto mode should return a valid PLL frequency
|
||||
if got < PLL_VCO_MIN*FREQ_MULT || got > PLL_VCO_MAX*FREQ_MULT {
|
||||
t.Errorf("CalculateMultisynth() returned invalid PLL freq %v", got)
|
||||
}
|
||||
}
|
||||
if reg.p3 == 0 {
|
||||
t.Errorf("CalculateMultisynth() p3 should not be 0")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultisynth67Calc(t *testing.T) {
|
||||
d := &Device{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
freq Frequency
|
||||
pllFreq Frequency
|
||||
wantErr bool
|
||||
}{
|
||||
{"10MHz Auto", 10000000 * FREQ_MULT, 0, false},
|
||||
{"100MHz Auto", 100000000 * FREQ_MULT, 0, false},
|
||||
{"100MHz from 800MHz", 100000000 * FREQ_MULT, 800000000 * FREQ_MULT, false},
|
||||
{"Invalid Division", 10000000 * FREQ_MULT, 777000000 * FREQ_MULT, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, reg := d.multisynth67Calc(tt.freq, tt.pllFreq)
|
||||
if tt.pllFreq == 0 {
|
||||
if got < PLL_VCO_MIN*FREQ_MULT || got > PLL_VCO_MAX*FREQ_MULT {
|
||||
t.Errorf("multisynth67Calc() returned invalid PLL freq %v", got)
|
||||
}
|
||||
} else if tt.wantErr {
|
||||
if got != 0 {
|
||||
t.Errorf("multisynth67Calc() should return 0 for invalid division, got %v", got)
|
||||
}
|
||||
}
|
||||
if reg.p1 == 0 && !tt.wantErr {
|
||||
t.Errorf("multisynth67Calc() p1 should not be 0")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetCorrection(t *testing.T) {
|
||||
// Skip this test as it requires a mock I2C bus
|
||||
t.Skip("Requires mock I2C bus implementation")
|
||||
}
|
||||
|
||||
func TestSetRefFreq(t *testing.T) {
|
||||
d := &Device{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
freq CrystalFrequency
|
||||
wantFreq CrystalFrequency
|
||||
wantDiv uint8
|
||||
}{
|
||||
{"25MHz", 25000000, 25000000, CLKIN_DIV_1},
|
||||
{"50MHz", 50000000, 25000000, CLKIN_DIV_2},
|
||||
{"100MHz", 100000000, 25000000, CLKIN_DIV_4},
|
||||
{"30MHz", 30000000, 30000000, CLKIN_DIV_1},
|
||||
{"60MHz", 60000000, 30000000, CLKIN_DIV_2},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
d.SetReferenceFrequency(PLLInputClockIn, tt.freq)
|
||||
if d.crystalFreq[PLLInputClockIn] != tt.wantFreq {
|
||||
t.Errorf("SetReferenceFrequency() freq = %v, want %v", d.crystalFreq[PLLInputClockIn], tt.wantFreq)
|
||||
}
|
||||
if d.clkinDiv != tt.wantDiv {
|
||||
t.Errorf("SetReferenceFrequency() clkinDiv = %v, want %v", d.clkinDiv, tt.wantDiv)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCorrection(t *testing.T) {
|
||||
d := &Device{}
|
||||
d.refCorrection[PLLInputXO] = 5000
|
||||
d.refCorrection[PLLInputClockIn] = -3000
|
||||
|
||||
if got := d.GetCorrection(PLLInputXO); got != 5000 {
|
||||
t.Errorf("GetCorrection(PLLInputXO) = %v, want 5000", got)
|
||||
}
|
||||
if got := d.GetCorrection(PLLInputClockIn); got != -3000 {
|
||||
t.Errorf("GetCorrection(PLLInputClockIn) = %v, want -3000", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user