Skip to main content

ic_md/
hl_blocking.rs

1//! This module implements the blocking high-level driver.
2//!
3//! This module is available on the "blocking" feature, which is activated by default.
4
5use embedded_hal::spi::SpiDevice;
6
7use crate::{
8    ActuatorStatus, CntCfg, CntCount, CntSetup, DeviceStatus, ErrorStatus, FullDeviceStatus,
9    PinStatus, WarningStatus,
10    dd::{Device, DeviceError, DeviceInterface},
11};
12
13/// The main driver struct of the crate representing the iC-MD quadrature counter.
14/// You can also access the underlying device driver directly via the `device` field.
15/// You are then yourself responsible for reading the correct counter configurations.
16///
17/// Note that the "blocking" feature must be activated (which it is by default).
18#[derive(Debug)]
19pub struct IcMd<Spi> {
20    /// Provides acces to the underlying device driver.
21    pub device: Device<DeviceInterface<Spi>>,
22    /// Configuration of the counter, set only prior to calling `init()`.
23    counter_config: CntCfg,
24    /// Status of the device (error and warning flags). Read only, updated when reading the
25    /// counter.
26    device_status: DeviceStatus,
27    actuator_status: ActuatorStatus,
28}
29
30impl<Spi: SpiDevice> IcMd<Spi> {
31    /// Creates a new instance of the iC-MD driver.
32    /// By default, the counter is configured to 48-bit mode.
33    pub fn new(spi: Spi) -> Self {
34        Self {
35            device: Device::new(DeviceInterface::new(spi)),
36            counter_config: CntCfg::Cnt1Bit48(CntSetup::default()),
37            actuator_status: ActuatorStatus::default(),
38            device_status: DeviceStatus::default(),
39        }
40    }
41
42    /// Initialize the iC-MD device with the given configuration.
43    pub fn init(&mut self) -> Result<(), DeviceError<Spi::Error>> {
44        self.device
45            .counter_configuration()
46            .write(|reg| reg.set_value(self.counter_config.into()))?;
47
48        Ok(())
49    }
50
51    /// Set the actuator pins output to the given status.
52    /// Note that as far as the iC-MD is concerned, this status is "write only". Thus, there is no
53    /// function available to read the current status of the actuator pins. However, the stored
54    /// `actuator_status` variable will be updated according to what you set here.
55    ///
56    /// # Arguments
57    /// * `act0`: The status of actuator pin 0 (ACT0).
58    /// * `act1`: The status of actuator pin 1 (ACT1).
59    pub fn configure_actuator_pins(
60        &mut self,
61        act0: &PinStatus,
62        act1: &PinStatus,
63    ) -> Result<(), DeviceError<Spi::Error>> {
64        self.device.instruction_byte().write(|reg| {
65            reg.set_act_0(act0.into());
66            reg.set_act_1(act1.into());
67        })?;
68        self.actuator_status.act0 = *act0;
69        self.actuator_status.act1 = *act1;
70        Ok(())
71    }
72
73    /// Get current device status.
74    /// This is a cached value that is updated when reading the counter. It contains the error and
75    /// warning flags of the device. For a full device status, use `get_full_device_status()`.
76    pub fn get_device_status(&self) -> DeviceStatus {
77        self.device_status
78    }
79
80    /// Get the full device status by reading all the status registers.
81    /// This will reset many of the status bits to wait for the next event, problem, issue to
82    /// occur.
83    pub fn get_full_device_status(&mut self) -> Result<FullDeviceStatus, DeviceError<Spi::Error>> {
84        let status0 = self.device.status_0().read()?;
85        let status1 = self.device.status_1().read()?;
86        let status2 = self.device.status_2().read()?;
87
88        Ok(FullDeviceStatus {
89            cnt0_overflow: status0.ovf_0().into(),
90            cnt0_aberr: status0.ab_err_0().into(),
91            cnt0_zero: status0.zero_0().into(),
92            cnt1_overflow: status1.ovf_1().into(),
93            cnt1_aberr: status1.ab_err_1().into(),
94            cnt1_zero: status1.zero_1().into(),
95            cnt2_overflow: status2.ovf_2().into(),
96            cnt2_aberr: status2.ab_err_2().into(),
97            cnt2_zero: status2.zero_2().into(),
98            power_status: status0.p_dwn().into(),
99            ref_reg_status: status0.r_val().into(),
100            upd_reg_status: status0.upd_val().into(),
101            ref_cnt_status: status0.ovf_ref().into(),
102            ext_err_status: status1.ext_err().into(),
103            ext_warn_status: status1.ext_warn().into(),
104            comm_status: status1.com_col().into(),
105            tp_status: status0.tp_val().into(),
106            tpi_status: status1.tps().into(),
107            ssi_enabled: status2.en_ssi().into(),
108        })
109    }
110
111    /// Read the current counter value and return it.
112    pub fn read_counter(&mut self) -> Result<CntCount, DeviceError<Spi::Error>> {
113        match self.counter_config {
114            CntCfg::Cnt1Bit24(_) => {
115                let res = self.device.read_cnt_cfg_0().read()?;
116                self.set_device_status(res.nwarn(), res.nerr());
117                Ok(CntCount::Cnt1Bit24(res.cnt_0()))
118            }
119            CntCfg::Cnt2Bit24(_, _) => {
120                let res = self.device.read_cnt_cfg_1().read()?;
121                self.set_device_status(res.nwarn(), res.nerr());
122                Ok(CntCount::Cnt2Bit24(res.cnt_0(), res.cnt_1()))
123            }
124            CntCfg::Cnt1Bit48(_) => {
125                let res = self.device.read_cnt_cfg_2().read()?;
126                self.set_device_status(res.nwarn(), res.nerr());
127                Ok(CntCount::Cnt1Bit48(res.cnt_0()))
128            }
129            CntCfg::Cnt1Bit16(_) => {
130                let res = self.device.read_cnt_cfg_3().read()?;
131                self.set_device_status(res.nwarn(), res.nerr());
132                Ok(CntCount::Cnt1Bit16(res.cnt_0()))
133            }
134            CntCfg::Cnt1Bit32(_) => {
135                let res = self.device.read_cnt_cfg_4().read()?;
136                self.set_device_status(res.nwarn(), res.nerr());
137                Ok(CntCount::Cnt1Bit32(res.cnt_0()))
138            }
139            CntCfg::Cnt2Bit32Bit16(_, _) => {
140                let res = self.device.read_cnt_cfg_5().read()?;
141                self.set_device_status(res.nwarn(), res.nerr());
142                Ok(CntCount::Cnt2Bit32Bit16(res.cnt_0(), res.cnt_1()))
143            }
144            CntCfg::Cnt2Bit16(_, _) => {
145                let res = self.device.read_cnt_cfg_6().read()?;
146                self.set_device_status(res.nwarn(), res.nerr());
147                Ok(CntCount::Cnt2Bit16(res.cnt_0(), res.cnt_1()))
148            }
149            CntCfg::Cnt3Bit16(_, _, _) => {
150                let res = self.device.read_cnt_cfg_7().read()?;
151                self.set_device_status(res.nwarn(), res.nerr());
152                Ok(CntCount::Cnt3Bit16(res.cnt_0(), res.cnt_1(), res.cnt_2()))
153            }
154        }
155    }
156
157    /// Reset counters to zero.
158    /// You can select which counters should be set to zero using the specific arguments.
159    ///
160    /// # Arguments
161    /// * `cnt0`: If true, counter 0 is reset, else not.
162    /// * `cnt1`: If true, counter 1 is reset, else not.
163    /// * `cnt2`: If true, counter 2 is reset, else not.
164    pub fn reset_counters(
165        &mut self,
166        cnt0: bool,
167        cnt1: bool,
168        cnt2: bool,
169    ) -> Result<(), DeviceError<Spi::Error>> {
170        let act0 = &self.actuator_status.act0;
171        let act1 = &self.actuator_status.act1;
172        self.device.instruction_byte().write(|reg| {
173            reg.set_ab_res_0(cnt0);
174            reg.set_ab_res_1(cnt1);
175            reg.set_ab_res_2(cnt2);
176            reg.set_act_0(act0.into());
177            reg.set_act_1(act1.into());
178        })?;
179        Ok(())
180    }
181
182    /// Reset all counters.
183    /// Can be used to send reset commands to all counters.
184    pub fn reset_all_counters(&mut self) -> Result<(), DeviceError<Spi::Error>> {
185        self.reset_counters(true, true, true)?;
186        Ok(())
187    }
188
189    /// Touch probe instruction
190    /// Load touch probe 2 with touch probe 1 value and touch probe 1 wiht ABCNT value.
191    pub fn touch_probe_instruction(&mut self) -> Result<(), DeviceError<Spi::Error>> {
192        let act0 = &self.actuator_status.act0;
193        let act1 = &self.actuator_status.act1;
194        self.device.instruction_byte().write(|reg| {
195            reg.set_tp(true);
196            reg.set_act_0(act0.into());
197            reg.set_act_1(act1.into());
198        })?;
199        Ok(())
200    }
201
202    /// Set the counter configuration.
203    /// This should be done prior to calling `init()`.
204    pub fn set_counter_config(&mut self, config: CntCfg) {
205        self.counter_config = config;
206    }
207
208    /// Set device status from two bools that were read and passed on to here.
209    /// Note that the inputs are from nerr and nwarn!
210    fn set_device_status(&mut self, nwarn: bool, nerr: bool) {
211        self.device_status.warning = match nwarn {
212            true => WarningStatus::Ok,
213            false => WarningStatus::Warning,
214        };
215        self.device_status.error = match nerr {
216            true => ErrorStatus::Ok,
217            false => ErrorStatus::Error,
218        };
219    }
220}