ic_md/dd.rs
1//! The iC-MD device driver, created with the `device_driver` crate.
2//!
3//! Please refer to the iC-MD datasheet to better understand what each command does.
4
5use core::fmt::Debug;
6
7use embedded_hal::spi::{Operation, SpiDevice};
8
9device_driver::create_device! {
10 device_name: Device,
11 dsl: {
12 config {
13 type RegisterAddressType = u8;
14 }
15 /// Counter configuration
16 /// The iC-MD can be configured for 1 up to 3 channels with counter lengths of 16 to 48
17 /// bits. Here, the counter configuration is selected as a u8 value. The higher-level
18 /// driver takes care of converting from a meaningful configuration to the 8-bit value.
19 register CounterConfiguration {
20 type Access = RW;
21 const ADDRESS = 0x00;
22 const SIZE_BITS = 8;
23 value: uint = 0..8,
24 },
25 /// Read the 24 bit counter configuration, 24+2 bits to read (4 bytes)
26 /// This corresponds to counter configuration `0b000`.
27 register ReadCntCfg0 {
28 type Access = RO;
29 type ByteOrder = BE;
30 const ADDRESS = 0x08;
31 const SIZE_BITS = 32;
32 const ALLOW_ADDRESS_OVERLAP = true;
33
34 /// Counter 0 value, bits 0-24
35 cnt0: int = 8..32,
36 nerr: bool = 7,
37 nwarn: bool = 6,
38 },
39 /// Read the 24 bit, 2 counters configuration, 48+2 bits to read (7 bytes)
40 /// This corresponds to counter configuration `0b001`.
41 register ReadCntCfg1 {
42 type Access = RO;
43 type ByteOrder = BE;
44 const ADDRESS = 0x08;
45 const SIZE_BITS = 56;
46 const ALLOW_ADDRESS_OVERLAP = true;
47
48 /// Counter 1 value, bits 32-48
49 cnt1: int = 32..56,
50 /// Counter 0 value, bits 0-24
51 cnt0: int = 8..32,
52 nerr: bool = 7,
53 nwarn: bool = 6,
54 },
55 /// Read the 48 bit counter register, 48+2 bits to read (7 bytes)
56 /// This corresponds to counter configuration `0b010`.
57 register ReadCntCfg2 {
58 type Access = RO;
59 type ByteOrder = BE;
60 const ADDRESS = 0x08;
61 const SIZE_BITS = 56;
62 const ALLOW_ADDRESS_OVERLAP = true;
63
64 /// Counter 0 value, bits 0-48
65 cnt0: int = 8..56,
66 nerr: bool = 7,
67 nwarn: bool = 6,
68 },
69 /// Read the 16 bit counter configuration, 16+2 bits to read (3 bytes)
70 /// This corresponds to counter configuration `0b011`.
71 register ReadCntCfg3 {
72 type Access = RO;
73 type ByteOrder = BE;
74 const ADDRESS = 0x08;
75 const SIZE_BITS = 24;
76 const ALLOW_ADDRESS_OVERLAP = true;
77
78 /// Counter 0 value, bits 0-16
79 cnt0: int = 8..24,
80 nerr: bool = 7,
81 nwarn: bool = 6,
82 },
83 /// Read the 32 bit counter configuration, 32+2 bits to read (5 bytes)
84 /// This corresponds to counter configuration `0b100`.
85 register ReadCntCfg4 {
86 type Access = RO;
87 type ByteOrder = BE;
88 const ADDRESS = 0x08;
89 const SIZE_BITS = 40;
90 const ALLOW_ADDRESS_OVERLAP = true;
91
92 /// Counter 0 value, bits 0-32
93 cnt0: int = 8..40,
94 nerr: bool = 7,
95 nwarn: bool = 6,
96 },
97 /// Read the 32 bit and 16 bit counter configuration, 32+16+2 bits to read (7 bytes)
98 /// This corresponds to counter configuration `0b101`.
99 register ReadCntCfg5 {
100 type Access = RO;
101 type ByteOrder = BE;
102 const ADDRESS = 0x08;
103 const SIZE_BITS = 56;
104 const ALLOW_ADDRESS_OVERLAP = true;
105
106 /// Counter 1 value, bits 16-48
107 cnt1: int = 24..56,
108 /// Counter 0 value, bits 0-16
109 cnt0: int = 8..24,
110 nerr: bool = 7,
111 nwarn: bool = 6,
112 },
113 /// Read the 16 bit and 16 bit counter configuration, 16+16+2 bits to read (5 bytes)
114 /// This corresponds to counter configuration `0b110`.
115 register ReadCntCfg6 {
116 type Access = RO;
117 type ByteOrder = BE;
118 const ADDRESS = 0x08;
119 const SIZE_BITS = 40;
120 const ALLOW_ADDRESS_OVERLAP = true;
121
122 /// Counter 1 value, bits 16-32
123 cnt1: int = 24..40,
124 /// Counter 0 value, bits 0-16
125 cnt0: int = 8..24,
126 nerr: bool = 7,
127 nwarn: bool = 6,
128 },
129 /// Read the 3 x 16 bit counter configuration, 16+16+16+2 bits to read (7 bytes)
130 /// This corresponds to counter configuration `0b111`.
131 register ReadCntCfg7 {
132 type Access = RO;
133 type ByteOrder = BE;
134 const ADDRESS = 0x08;
135 const SIZE_BITS = 64;
136 const ALLOW_ADDRESS_OVERLAP = true;
137
138 /// Counter 2 value, bits 32-48
139 cnt2: int = 40..56,
140 /// Counter 1 value, bits 16-32
141 cnt1: int = 24..40,
142 /// Counter 0 value, bits 0-16
143 cnt0: int = 8..24,
144 nerr: bool = 7,
145 nwarn: bool = 6,
146 },
147 /// Read the references registers 24 bits.
148 /// TODO: It is unclear if this works, as I assume the address for reading is
149 /// auto-incremented as when reading the data. This should be tested once the actual
150 /// hardware setup is available with an encoder connected.
151 register ReferenceCounter {
152 type Access = RO;
153 type ByteOrder = BE;
154 const ADDRESS = 0x10;
155 const SIZE_BITS = 24;
156 value: int = 0..24,
157 },
158 /// Instruction byte (write only)
159 /// Allows writing of the instruction bytes. When one of these bits is set to 1, the
160 /// corresponding instruction is executed and the bit set back to zero, except in the
161 /// case of `Act0` and `Act1`, which remain set to the written value.
162 register InstructionByte {
163 type Access = WO;
164 const ADDRESS = 0x30;
165 const SIZE_BITS = 8;
166
167 /// Reset counter 0
168 AbRes0: bool = 0,
169 /// Reset counter 1
170 AbRes1: bool = 1,
171 /// Reset counter 2
172 AbRes2: bool = 2,
173 /// Enable zero codification
174 ZCEn: bool = 3,
175 /// Load touch probe 2 with touch probe 1 value and touch probe 1 with AB counter value
176 TP: bool = 4,
177 /// Set actuator pin 0 to VDD if enabled, otherwise to GND
178 Act0: bool = 5,
179 /// Set actuator pin 1 to VDD if enabled, otherwise to GND
180 Act1: bool = 6,
181 },
182 /// `Status0`: Status of counter 0
183 /// Returns the status of counter 0 plus several other status bits. See also `Status1` and
184 /// `Status2` for the other counters and more status bits.
185 register Status0 {
186 type Access = RO;
187 const ADDRESS = 0x48;
188 const SIZE_BITS = 8;
189
190 /// Touch probe registers TP1/TP2 loaded or new values loaded.
191 TpVal: bool = 0,
192 /// Overflow of the reference counter. There were too many edges detected between two
193 /// index pulses. The value of the UPD and REF registers is not valid.
194 OvfRef: bool = 1,
195 /// UPD value: Every time that the UPD register is loaded, the status bit UpDval is set
196 /// to 1 until the status bit UPD or the register UPD is read out.
197 UpdVal: bool = 2,
198 /// Status bit that indicates that the reference value was loaded in the REF register
199 /// after the "Zero codification" process. After power-on, this bit remains at 0 until
200 /// the second different index pulse.
201 RVal: bool = 3,
202 /// Power down: If VDD reaches the power off supply level, the iC-MD is reset and the
203 /// RAM initialized to the default value. This status bit indicates that this
204 /// initialization has taken place.
205 PDwn: bool = 4,
206 /// Zero of counter 0 reached: The counter has reached the zero value.
207 Zero0: bool = 5,
208 /// Overflow of counter 0.
209 Ovf0: bool = 6,
210 /// AB input decodification error for counter 0. It occurs if the counting frequency is
211 /// too high or if two incrmeental edges are too close together.
212 AbErr0: bool = 7,
213 },
214 /// `Status1`: Status of counter 1
215 /// Returns the status of counter 1 plus several other status bits. See also `Status0` and
216 /// `Status2` for the other counters and more status bits.
217 register Status1 {
218 type Access = RO;
219 const ADDRESS = 0x49;
220 const SIZE_BITS = 8;
221
222 /// TPS signal: Status of the signal on input pin TPI.
223 Tps: bool = 0,
224 /// Communication collision took place.
225 ComCol: bool = 1,
226 /// ExtWarn: Status bit that indicates if the `NWARN` pin was either pulled-down from
227 /// outside or set to 0 from inside (an internal masked error has occured).
228 ExtWarn: bool = 2,
229 /// ExtErr: Status bit that indicates if the `NERR` pin was either pulled-down from
230 /// outside or set to 0 from inside (an internal masked error has occured).
231 ExtErr: bool = 3,
232 /// Power down: If VDD reaches the power off supply level, the iC-MD is reset and the
233 /// RAM initialized to the default value. This status bit indicates that this
234 /// initialization has taken place.
235 PDwn: bool = 4,
236 /// Zero of counter 1 reached: The counter has reached the zero value.
237 Zero1: bool = 5,
238 /// Overflow of counter 1.
239 Ovf1: bool = 6,
240 /// AB input decodification error for counter 1. It occurs if the counting frequency is
241 /// too high or if two incrmeental edges are too close together.
242 AbErr1: bool = 7,
243 },
244 /// `Status2`: Status of counter 2
245 /// Returns the status of counter 2 plus several other status bits. See also `Status0` and
246 /// `Status1` for the other counters and more status bits.
247 register Status2 {
248 type Access = RO;
249 const ADDRESS = 0x4A;
250 const SIZE_BITS = 8;
251
252 /// EnSSI: Status of the SSI pin. If closed, the SSI interface is not enabled and the
253 /// status bit is 0. Otherwise, if SSI is enabled (SLI pin is open), the status bit
254 /// returns 1.
255 EnSsi: bool = 0,
256 /// Communication collision took place.
257 ComCol: bool = 1,
258 /// ExtWarn: Status bit that indicates if the `NWARN` pin was either pulled-down from
259 /// outside or set to 0 from inside (an internal masked error has occured).
260 ExtWarn: bool = 2,
261 /// ExtErr: Status bit that indicates if the `NERR` pin was either pulled-down from
262 /// outside or set to 0 from inside (an internal masked error has occured).
263 ExtErr: bool = 3,
264 /// Power down: If VDD reaches the power off supply level, the iC-MD is reset and the
265 /// RAM initialized to the default value. This status bit indicates that this
266 /// initialization has taken place.
267 PDwn: bool = 4,
268 /// Zero of counter 1 reached: The counter has reached the zero value.
269 Zero2: bool = 5,
270 /// Overflow of counter 1.
271 Ovf2: bool = 6,
272 /// AB input decodification error for counter 1. It occurs if the counting frequency is
273 /// too high or if two incrmeental edges are too close together.
274 AbErr2: bool = 7,
275 },
276
277 }
278}
279
280/// The SPI Device wrapper interface to the driver
281#[derive(Debug)]
282pub struct DeviceInterface<Spi> {
283 /// The SPI device used to communicate with the iC-MD device.
284 pub spi: Spi,
285}
286
287impl<Spi> DeviceInterface<Spi> {
288 /// Construct a new instance of the device.
289 ///
290 /// Spi mode 0, max 10 MHz according to the datasheet.
291 pub const fn new(spi: Spi) -> Self {
292 Self { spi }
293 }
294}
295
296impl<Spi: SpiDevice> device_driver::RegisterInterface for DeviceInterface<Spi> {
297 type Error = DeviceError<Spi::Error>;
298
299 type AddressType = u8;
300
301 fn write_register(
302 &mut self,
303 address: Self::AddressType,
304 _size_bits: u32,
305 data: &[u8],
306 ) -> Result<(), Self::Error> {
307 Ok(SpiDevice::transaction(
308 &mut self.spi,
309 &mut [Operation::Write(&[address]), Operation::Write(data)],
310 )?)
311 }
312
313 fn read_register(
314 &mut self,
315 address: Self::AddressType,
316 _size_bits: u32,
317 data: &mut [u8],
318 ) -> Result<(), Self::Error> {
319 SpiDevice::transaction(
320 &mut self.spi,
321 &mut [Operation::Write(&[0x80 | address]), Operation::Read(data)],
322 )?;
323
324 Ok(())
325 }
326}
327
328/// Low level interface error that wraps the SPI error
329#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
330#[cfg_attr(feature = "defmt", derive(defmt::Format))]
331pub struct DeviceError<Spi>(pub Spi);
332
333impl<Spi> From<Spi> for DeviceError<Spi> {
334 fn from(value: Spi) -> Self {
335 Self(value)
336 }
337}
338
339impl<Spi> core::ops::Deref for DeviceError<Spi> {
340 type Target = Spi;
341
342 fn deref(&self) -> &Self::Target {
343 &self.0
344 }
345}
346
347impl<Spi> core::ops::DerefMut for DeviceError<Spi> {
348 fn deref_mut(&mut self) -> &mut Self::Target {
349 &mut self.0
350 }
351}