urukul/
lib.rs

1#![no_std]
2
3use arbitrary_int::{u2, u24, u3, u4, u7};
4use bitbybit::{bitenum, bitfield};
5use embedded_hal::digital::OutputPin;
6use embedded_hal::spi::{self, SpiBus, SpiDevice};
7use embedded_hal_bus::spi::{DeviceError, NoDelay, RefCellDevice};
8use num_traits::float::FloatCore;
9use serde::{Deserialize, Serialize};
10
11use ad9912::Ad9912;
12use encoded_pin::EncodedPin;
13
14#[derive(Debug, Clone, PartialEq, thiserror::Error)]
15pub enum Error {
16    #[error("Initialization: {0}: {1}")]
17    Initialization(&'static str, u32),
18    #[error("SPI Error {0}")]
19    Spi(spi::ErrorKind),
20    #[error("DDS")]
21    Dds(#[source] ad9912::Error),
22}
23
24impl<E: spi::Error> From<E> for Error {
25    fn from(value: E) -> Self {
26        Self::Spi(value.kind())
27    }
28}
29
30#[bitfield(u24, default = 0x000700)]
31#[derive(Debug, PartialEq, Serialize, Deserialize)]
32pub struct Cfg {
33    #[bits(0..=3, rw)]
34    rf_sw: u4,
35    #[bits(4..=7, rw)]
36    led: u4,
37    #[bits(8..=10, rw)]
38    profile: u3,
39    #[bit(12, rw)]
40    io_update: bool,
41    #[bits(13..=16, rw)]
42    mask_nu: u4,
43    #[bits([17, 21], rw)]
44    clk_sel: ClkSel,
45    #[bit(18, rw)]
46    sync_sel: bool,
47    #[bit(19, rw)]
48    rst: bool,
49    #[bit(20, rw)]
50    io_rst: bool,
51    #[bits(22..=23, rw)]
52    div_sel: DivSel,
53}
54
55#[bitfield(u24)]
56#[derive(Debug, PartialEq, Serialize, Deserialize)]
57pub struct Status {
58    #[bits(0..=3, r)]
59    pub rf_sw: u4,
60    #[bits(4..=7, r)]
61    pub smp_err: u4,
62    #[bits(8..=11, r)]
63    pub pll_lock: u4,
64    #[bits(12..=15, r)]
65    pub ifc_mode: u4,
66    #[bits(16..=22, r)]
67    pub proto_rev: u7,
68}
69
70#[bitenum(u2, exhaustive = true)]
71#[derive(Debug, PartialEq, Serialize, Deserialize)]
72pub enum ClkSel {
73    Osc = 0,
74    Sma = 1,
75    Mmcx = 2,
76    _Sma = 3,
77}
78
79#[bitenum(u2, exhaustive = true)]
80#[derive(Debug, PartialEq, Serialize, Deserialize)]
81pub enum DivSel {
82    One = 0,
83    _One = 1,
84    Two = 2,
85    Four = 3,
86}
87
88impl DivSel {
89    pub fn divider(&self) -> u32 {
90        match self {
91            Self::One => 1,
92            Self::Two => 2,
93            Self::Four => 4,
94            Self::_One => 1,
95        }
96    }
97}
98
99pub fn att_to_mu(att: f32) -> u8 {
100    255 - (att * 8.0).round() as u8
101}
102
103pub struct Urukul<'a, B, P> {
104    att_spi: RefCellDevice<'a, B, EncodedPin<'a, P, 3>, NoDelay>,
105    cfg_spi: RefCellDevice<'a, B, EncodedPin<'a, P, 3>, NoDelay>,
106    io_update: P,
107    _sync: P,
108    cfg: Cfg,
109    att: [u8; 4],
110    dds: [Ad9912<RefCellDevice<'a, B, EncodedPin<'a, P, 3>, NoDelay>>; 4],
111}
112
113impl<'a, B: SpiBus<u8>, P: OutputPin> Urukul<'a, B, P> {
114    pub fn new(
115        spi: &'a core::cell::RefCell<B>,
116        cs: &'a core::cell::RefCell<[P; 3]>,
117        io_update: P,
118        sync: P,
119    ) -> Result<Self, Error> {
120        let sel = |sel| {
121            RefCellDevice::new(spi, EncodedPin::new(cs, u3::new(sel)), NoDelay)
122                .unwrap()
123        };
124        let cfg_spi = sel(1);
125        let att_spi = sel(2);
126        let mut dev = Self {
127            cfg_spi,
128            att_spi,
129            io_update,
130            _sync: sync,
131            cfg: Cfg::default(),
132            att: [0; 4],
133            dds: [
134                Ad9912::new(sel(4)),
135                Ad9912::new(sel(5)),
136                Ad9912::new(sel(6)),
137                Ad9912::new(sel(7)),
138            ],
139        };
140        dev.init()?;
141        Ok(dev)
142    }
143
144    pub fn cfg(&self) -> Cfg {
145        self.cfg
146    }
147
148    pub fn set_cfg(
149        &mut self,
150        cfg: Cfg,
151    ) -> Result<Status, DeviceError<B::Error, P::Error>> {
152        let mut bits = [0; 3];
153        let w = cfg.raw_value().to_be_bytes();
154        self.cfg_spi.transfer(&mut bits, &w)?;
155        self.cfg = cfg;
156        Ok(Status::new_with_raw_value(u24::from_be_bytes(bits)))
157    }
158
159    pub fn att(&self, ch: u2) -> u8 {
160        self.att[(u2::new(3) - ch).value() as usize]
161    }
162
163    pub fn set_att(
164        &mut self,
165        ch: u2,
166        att: u8,
167    ) -> Result<(), DeviceError<B::Error, P::Error>> {
168        self.att[(u2::new(3) - ch).value() as usize] = att;
169        self.att_spi.write(&self.att)
170    }
171
172    pub fn init(&mut self) -> Result<(), Error> {
173        let sta = self.set_cfg(self.cfg())?;
174        if sta.proto_rev().value() != 0x8 {
175            return Err(Error::Initialization(
176                "Invalid PROTO_REV",
177                sta.proto_rev().value() as _,
178            ));
179        }
180        if sta.rf_sw().value() != 0 {
181            return Err(Error::Initialization(
182                "RF_SW driven",
183                sta.rf_sw().value() as _,
184            ));
185        }
186        if sta.ifc_mode().value() != 0 {
187            return Err(Error::Initialization(
188                "Invalid IFC_MODE",
189                sta.ifc_mode().value() as _,
190            ));
191        }
192
193        let cfg = self.cfg();
194        self.set_cfg(cfg.with_io_rst(true).with_rst(true))?;
195        self.set_cfg(cfg)?;
196
197        for want in [[0; 4], [0xff; 4], [0x5a; 4], [0xa5; 4]].iter() {
198            self.att_spi.write(want)?;
199            let mut have = [0; 4];
200            self.att_spi.read(&mut have)?;
201            if want != &have {
202                return Err(Error::Initialization(
203                    "Attenuator mismatch",
204                    u32::from_be_bytes(have),
205                ));
206            }
207        }
208
209        // This is destructive and clears attenuation
210        // https://github.com/rust-embedded/embedded-hal/issues/642
211        self.att_spi.write(&self.att)?;
212
213        for dds in self.dds.iter_mut() {
214            dds.init().map_err(Error::Dds)?;
215        }
216
217        log::info!("Urukul initialized");
218        Ok(())
219    }
220
221    pub fn io_update(&mut self) -> Result<(), P::Error> {
222        self.io_update.set_high()?;
223        self.io_update.set_low()
224    }
225
226    pub fn set_rf_sw(
227        &mut self,
228        ch: u2,
229        state: bool,
230    ) -> Result<(), DeviceError<B::Error, P::Error>> {
231        let mut v = self.cfg.rf_sw().value();
232        v &= !(1 << ch.value());
233        v |= (state as u8) << ch.value();
234        self.set_cfg(self.cfg.with_rf_sw(u4::new(v)))?;
235        Ok(())
236    }
237
238    pub fn set_led(
239        &mut self,
240        ch: u2,
241        state: bool,
242    ) -> Result<(), DeviceError<B::Error, P::Error>> {
243        let mut v = self.cfg.led().value();
244        v &= !(1 << ch.value());
245        v |= (state as u8) << ch.value();
246        self.set_cfg(self.cfg.with_led(u4::new(v)))?;
247        Ok(())
248    }
249
250    pub fn dds(
251        &mut self,
252        ch: u2,
253    ) -> &mut Ad9912<RefCellDevice<'a, B, EncodedPin<'a, P, 3>, NoDelay>> {
254        &mut self.dds[ch.value() as usize]
255    }
256}