Skip to main content

ic_md/
hl_async.rs

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