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 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}