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}