1#![no_std]
2use core::slice;
3use embedded_hal::i2c::I2c;
4
5const ADDRESS: u8 = 0x60;
6#[allow(dead_code)]
7const READBIT: u8 = 0x01;
8
9const REGS_15_TO_92: [u8; 92 - 15 + 1] = [
10 0x00, 0x4f, 0x4f, 0x6f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x66, 0x00, 0x00, 0x02, 0x02, 0x71, 0x00, 0x0c, 0x1a, 0x00, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00,
14 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16 0x00, 0x00, 0x00,
17];
18
19const REGS_149_TO_170: [u8; 170 - 149 + 1] = [
20 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
21 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
22];
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[repr(u16)]
26pub enum Error {
27 OperationTimeOut = 0x1,
28 AddressOutOfRange = 0x2,
29 BufferOverflow = 0x3,
30 InvalidParameter = 0x4,
31 DeviceNotInitialsed = 0x5,
32 UnexpectedValue = 0x6,
33 I2CDeviceNotFound = 0x101,
34 I2CNoACK = 0x102,
35 I2CTimeOut = 0x103,
36 I2CTransaction = 0x104,
37}
38
39fn check(conditon: bool, error: Error) -> Result<(), Error> {
40 if conditon { Ok(()) } else { Err(error) }
41}
42
43#[allow(dead_code)]
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45#[repr(u8)]
46enum Registers {
47 DeviceStatus = 0,
48 InterruptStatusSticky = 1,
49 InterruptStatusMask = 2,
50 OutputEnableControl = 3,
51 ObePinEnableControl = 9,
52 PLLInputSource = 15,
53 CLK0Control = 16,
54 CLK1Control = 17,
55 CLK2Control = 18,
56 CLK3Control = 19,
57 CLK4Control = 20,
58 CLK5Control = 21,
59 CLK6Control = 22,
60 CLK7Control = 23,
61 CLK3_0DisableState = 24,
62 CLK7_4DisableState = 25,
63 Multisynth0Parameters1 = 42,
64 Multisynth0Parameters2 = 43,
65 Multisynth0Parameters3 = 44,
66 Multisynth0Parameters4 = 45,
67 Multisynth0Parameters5 = 46,
68 Multisynth0Parameters6 = 47,
69 Multisynth0Parameters7 = 48,
70 Multisynth0Parameters8 = 49,
71 Multisynth1Parameters1 = 50,
72 Multisynth1Parameters2 = 51,
73 Multisynth1Parameters3 = 52,
74 Multisynth1Parameters4 = 53,
75 Multisynth1Parameters5 = 54,
76 Multisynth1Parameters6 = 55,
77 Multisynth1Parameters7 = 56,
78 Multisynth1Parameters8 = 57,
79 Multisynth2Parameters1 = 58,
80 Multisynth2Parameters2 = 59,
81 Multisynth2Parameters3 = 60,
82 Multisynth2Parameters4 = 61,
83 Multisynth2Parameters5 = 62,
84 Multisynth2Parameters6 = 63,
85 Multisynth2Parameters7 = 64,
86 Multisynth2Parameters8 = 65,
87 Multisynth3Parameters1 = 66,
88 Multisynth3Parameters2 = 67,
89 Multisynth3Parameters3 = 68,
90 Multisynth3Parameters4 = 69,
91 Multisynth3Parameters5 = 70,
92 Multisynth3Parameters6 = 71,
93 Multisynth3Parameters7 = 72,
94 Multisynth3Parameters8 = 73,
95 Multisynth4Parameters1 = 74,
96 Multisynth4Parameters2 = 75,
97 Multisynth4Parameters3 = 76,
98 Multisynth4Parameters4 = 77,
99 Multisynth4Parameters5 = 78,
100 Multisynth4Parameters6 = 79,
101 Multisynth4Parameters7 = 80,
102 Multisynth4Parameters8 = 81,
103 Multisynth5Parameters1 = 82,
104 Multisynth5Parameters2 = 83,
105 Multisynth5Parameters3 = 84,
106 Multisynth5Parameters4 = 85,
107 Multisynth5Parameters5 = 86,
108 Multisynth5Parameters6 = 87,
109 Multisynth5Parameters7 = 88,
110 Multisynth5Parameters8 = 89,
111 Multisynth6Parameters = 90,
112 Multisynth7Parameters = 91,
113 CLK6_7RDiv = 92,
114 SpreadSpectrumParameters = 149,
115 CLK0InitialPhaseOffset = 165,
116 CLK1InitialPhaseOffset = 166,
117 CLK2InitialPhaseOffset = 167,
118 CLK3InitialPhaseOffset = 168,
119 CLK4InitialPhaseOffset = 169,
120 CLK5InitialPhaseOffset = 170,
121 PLLReset = 177,
122 CrystalInternalLoadCapacitance = 183,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126#[repr(u8)]
127pub enum PLL {
128 A = 0,
129 B,
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133#[repr(u8)]
134pub enum CrystalLoad {
135 PF6 = 1 << 6,
136 PF8 = 2 << 6,
137 PF10 = 3 << 6,
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141#[repr(u32)]
142pub enum CrystalFreq {
143 MHZ25 = 25_000_000,
144 MHZ27 = 27_000_000,
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148#[repr(u8)]
149pub enum MultisynthDiv {
150 Div4 = 4,
151 Div6 = 6,
152 Div8 = 8,
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156#[repr(u8)]
157pub enum RDiv {
158 Div1 = 0,
159 Div2 = 1,
160 Div4 = 2,
161 Div8 = 3,
162 Div16 = 4,
163 Div32 = 5,
164 Div64 = 6,
165 Div128 = 7,
166}
167
168impl RDiv {
169 fn min_divider(desired_divider: u16) -> Result<Self, Error> {
170 match 16 - (desired_divider.max(1) - 1).leading_zeros() {
171 0 => Ok(RDiv::Div1),
172 1 => Ok(RDiv::Div2),
173 2 => Ok(RDiv::Div4),
174 3 => Ok(RDiv::Div8),
175 4 => Ok(RDiv::Div16),
176 5 => Ok(RDiv::Div32),
177 6 => Ok(RDiv::Div64),
178 7 => Ok(RDiv::Div128),
179 _ => Err(Error::InvalidParameter),
180 }
181 }
182
183 fn denominator_u8(&self) -> u8 {
184 1 << (*self as u8)
185 }
186}
187
188#[allow(dead_code)]
189struct Config {
190 initialised: bool,
191 crystal_freq: CrystalFreq,
192 crystal_load: CrystalLoad,
193 crystal_ppm: u32,
194 plla_configured: bool,
195 plla_freq: u32,
196 pllb_configured: bool,
197 pllb_freq: u32,
198}
199
200pub struct Si5351<I2C: I2c> {
201 config: Config,
202 last_rdiv_value: [u8; 3],
203 i2c_dev: Option<I2C>,
204}
205
206impl<I2C: I2c> Si5351<I2C> {
207 pub fn new() -> Self {
208 Self {
209 config: Config {
210 initialised: false,
211 crystal_freq: CrystalFreq::MHZ25,
212 crystal_load: CrystalLoad::PF10,
213 crystal_ppm: 30,
214 plla_configured: false,
215 plla_freq: 0,
216 pllb_configured: false,
217 pllb_freq: 0,
218 },
219 last_rdiv_value: [0; 3],
220 i2c_dev: None,
221 }
222 }
223
224 fn write8(&mut self, reg: u8, value: u8) -> Result<(), Error> {
226 if let Some(i2c) = &mut self.i2c_dev {
227 match i2c.write(ADDRESS, &[reg, value]) {
228 Ok(_) => Ok(()),
229 Err(_) => Err(Error::I2CTransaction),
230 }
231 } else {
232 Err(Error::I2CTransaction)
233 }
234 }
235
236 fn read8(&mut self, reg: u8, value: &mut u8) -> Result<(), Error> {
238 if let Some(i2c) = &mut self.i2c_dev {
239 match i2c.write_read(ADDRESS, &[reg], slice::from_mut(value)) {
240 Ok(_) => Ok(()),
241 Err(_) => Err(Error::I2CTransaction),
242 }
243 } else {
244 Err(Error::I2CTransaction)
245 }
246 }
247
248 fn write_n(&mut self, data: &[u8]) -> Result<(), Error> {
249 if let Some(i2c) = &mut self.i2c_dev {
250 match i2c.write(ADDRESS, &data) {
251 Ok(_) => Ok(()),
252 Err(_) => Err(Error::I2CTransaction),
253 }
254 } else {
255 Err(Error::I2CTransaction)
256 }
257 }
258
259 pub fn begin(&mut self, i2c: I2C) -> Result<(), Error> {
264 self.i2c_dev = Some(i2c);
265 self.write8(Registers::OutputEnableControl as u8, 0xff)?;
267 self.write8(Registers::CLK0Control as u8, 0x80)?;
269 self.write8(Registers::CLK1Control as u8, 0x80)?;
270 self.write8(Registers::CLK2Control as u8, 0x80)?;
271 self.write8(Registers::CLK3Control as u8, 0x80)?;
272 self.write8(Registers::CLK4Control as u8, 0x80)?;
273 self.write8(Registers::CLK5Control as u8, 0x80)?;
274 self.write8(Registers::CLK6Control as u8, 0x80)?;
275 self.write8(Registers::CLK7Control as u8, 0x80)?;
276 self.write8(
278 Registers::CrystalInternalLoadCapacitance as u8,
279 self.config.crystal_load as u8,
280 )?;
281 self.enable_spread_spectrum(false)?;
283 self.config.plla_configured = false;
290 self.config.plla_freq = 0;
291 self.config.pllb_configured = false;
292 self.config.pllb_freq = 0;
293 self.config.initialised = true;
295 Ok(())
296 }
297
298 pub fn set_clock_builder_data(&mut self) -> Result<(), Error> {
310 check(self.config.initialised, Error::DeviceNotInitialsed)?;
312 self.write8(Registers::OutputEnableControl as u8, 0xff)?;
314 for (i, &value) in REGS_15_TO_92.iter().enumerate() {
317 self.write8((15 + i) as u8, value)?;
318 }
319 for (i, &value) in REGS_149_TO_170.iter().enumerate() {
320 self.write8((149 + i) as u8, value)?;
321 }
322 self.write8(Registers::PLLReset as u8, 0xac)?;
324 self.write8(Registers::OutputEnableControl as u8, 0x00)?;
326 Ok(())
327 }
328
329 pub fn setup_pll(&mut self, pll: PLL, mult: u32, num: u32, denom: u32) -> Result<(), Error> {
361 check(self.config.initialised, Error::DeviceNotInitialsed)?; check(mult > 14 && mult < 91, Error::InvalidParameter)?; check(denom > 0 && denom <= 0xfffff, Error::InvalidParameter)?; check(num <= 0xfffff, Error::InvalidParameter)?; let (p1, p2, p3) = if num == 0 {
385 (128 * mult - 512, num, denom)
387 } else {
388 let ratio = (128.0 * num as f32 / denom as f32) as u32;
390 (128 * mult + ratio - 512, 128 * num - denom * ratio, denom)
391 };
392 let base_addr = match pll {
394 PLL::A => 26_u8,
395 PLL::B => 34_u8,
396 };
397 self.write8(base_addr, ((p3 & 0xff00) >> 8) as u8)?;
399 self.write8(base_addr + 1, (p3 & 0xff) as u8)?;
400 self.write8(base_addr + 2, ((p1 & 0x30000) >> 16) as u8)?;
401 self.write8(base_addr + 3, ((p1 & 0xff00) >> 8) as u8)?;
402 self.write8(base_addr + 4, (p1 & 0xff) as u8)?;
403 self.write8(
404 base_addr + 5,
405 (((p3 & 0xf0000) >> 12) as u8) | (((p2 & 0xf0000) >> 16) as u8),
406 )?;
407 self.write8(base_addr + 6, ((p2 & 0xff00) >> 8) as u8)?;
408 self.write8(base_addr + 7, (p2 & 0xff) as u8)?;
409 self.write8(Registers::PLLReset as u8, (1 << 7) | (1 << 5))?;
411 let ratio = mult as f32 + num as f32 / denom as f32;
413 let fvco = (self.config.crystal_freq as u32 as f32 * ratio) as u32;
414 match pll {
415 PLL::A => {
416 self.config.plla_configured = true;
417 self.config.plla_freq = fvco;
418 }
419 PLL::B => {
420 self.config.pllb_configured = true;
421 self.config.pllb_freq = fvco;
422 }
423 }
424 Ok(())
425 }
426
427 pub fn setup_pll_int(&mut self, pll: PLL, mult: u32) -> Result<(), Error> {
433 self.setup_pll(pll, mult, 0, 1)
434 }
435
436 pub fn setup_multisynth(
483 &mut self,
484 output: usize,
485 pll_source: PLL,
486 div: u32,
487 num: u32,
488 denom: u32,
489 ) -> Result<(), Error> {
490 check(self.config.initialised, Error::DeviceNotInitialsed)?; check(output < 3, Error::InvalidParameter)?; check(div > 3 && div < 2049, Error::InvalidParameter)?; check(denom > 0 && denom <= 0xfffff, Error::InvalidParameter)?; check(num <= 0xfffff, Error::InvalidParameter)?; match pll_source {
497 PLL::A => check(self.config.plla_configured, Error::InvalidParameter)?,
498 PLL::B => check(self.config.pllb_configured, Error::InvalidParameter)?,
499 }
500
501 let (p1, p2, p3) = if num == 0 {
520 (128 * div - 512, 0_u32, denom)
522 } else if denom == 1 {
523 (128 * div + 128 * num - 512, 128 * num - 128, 1)
525 } else {
526 let ratio = (128.0 * num as f32 / denom as f32) as u32;
528 (128 * div + ratio - 512, 128 * num - denom * ratio, denom)
529 };
530 let base_addr = match output {
532 0 => Registers::Multisynth0Parameters1,
533 1 => Registers::Multisynth1Parameters1,
534 2 => Registers::Multisynth2Parameters1,
535 _ => unreachable!(),
536 } as u8;
537 let send_buffer = [
540 base_addr,
541 ((p3 & 0xff00) >> 8) as u8,
542 (p3 * 0xff) as u8,
543 ((p1 & 0x30000) >> 16) as u8 | self.last_rdiv_value[output],
544 ((p1 & 0xff00) >> 8) as u8,
545 (p1 & 0xff) as u8,
546 ((p3 & 0xf0000) >> 12) as u8 | ((p2 & 0xf0000) >> 16) as u8,
547 ((p2 & 0xff00) >> 8) as u8,
548 (p2 & 0xff) as u8,
549 ];
550 self.write_n(&send_buffer)?;
551 let mut clk_control_reg = 0x0f_u8; if pll_source == PLL::B {
555 clk_control_reg |= 1 << 5; }
557 if num == 0 {
558 clk_control_reg |= 1 << 6; }
560 let reg = match output {
561 0 => Registers::CLK0Control,
562 1 => Registers::CLK1Control,
563 2 => Registers::CLK2Control,
564 _ => unreachable!(),
565 } as u8;
566 self.write8(reg, clk_control_reg)
567 }
568
569 pub fn setup_multisynth_int(
577 &mut self,
578 output: usize,
579 pll_source: PLL,
580 div: MultisynthDiv,
581 ) -> Result<(), Error> {
582 self.setup_multisynth(output, pll_source, div as u32, 0, 1)
583 }
584
585 pub fn enable_spread_spectrum(&mut self, enabled: bool) -> Result<(), Error> {
589 let mut regval = 0;
590 self.read8(Registers::SpreadSpectrumParameters as u8, &mut regval)?;
591 if enabled {
592 regval |= 0x80;
593 } else {
594 regval &= !0x80;
595 }
596 self.write8(Registers::SpreadSpectrumParameters as u8, regval)
597 }
598
599 pub fn enable_outputs(&mut self, enabled: bool) -> Result<(), Error> {
603 check(self.config.initialised, Error::DeviceNotInitialsed)?;
605 self.write8(
607 Registers::OutputEnableControl as u8,
608 if enabled { 0x00 } else { 0xff },
609 )
610 }
611
612 pub fn setup_rdiv(&mut self, output: usize, div: RDiv) -> Result<(), Error> {
613 let r_reg = match output {
614 0 => Registers::Multisynth0Parameters3,
615 1 => Registers::Multisynth1Parameters3,
616 2 => Registers::Multisynth2Parameters3,
617 _ => return Err(Error::InvalidParameter),
618 } as u8;
619 let mut regval = 0;
620 self.read8(r_reg, &mut regval)?;
621 regval &= 0x0f;
622 let mut divider = div as u8;
623 divider &= 0x07;
624 divider <<= 4;
625 regval |= divider;
626 self.last_rdiv_value[output] = divider;
627 self.write8(r_reg, regval)
628 }
629
630 pub fn set_freq(&mut self, output: usize, pll: PLL, freq: u32) -> Result<(), Error> {
631 let denom: u32 = 1048575;
632 let crystal_freq = self.config.crystal_freq as u32;
633 let total_divider = (900_000_000 / freq) as u16;
634 let r_div = RDiv::min_divider(total_divider / 900)?;
635 let ms_div = (total_divider / (2 * r_div.denominator_u8() as u16) * 2).max(6);
636 if ms_div > 1800 {
637 return Err(Error::InvalidParameter);
638 }
639 let total_div = ms_div as u32 * r_div.denominator_u8() as u32;
640 let pll_freq = freq * total_div;
641
642 let mult = pll_freq / crystal_freq;
643 let num = ((pll_freq % crystal_freq) as u64 * denom as u64 / crystal_freq as u64) as u32;
644
645 self.setup_pll(pll, mult, num, denom)?;
646 self.setup_multisynth(output, pll, ms_div as u32, 0, 1)?;
647 self.setup_rdiv(output, r_div)
648 }
649}