itla/
lib.rs

1//! Non-blocking library to interface with ITLA lasers based upon embedded-hal
2//! See https://www.oiforum.com/wp-content/uploads/2019/01/OIF-ITLA-MSA-01.3.pdf
3//!   for all informations concerning ITLA lasers.
4//! This library also includes special vendor-specific commands for the PPCL200
5//!   laser used by PolyOrbite
6//!
7//! For any questions, please contact Xavier L'Heureux at xavier.lheureux@polymtl.ca
8
9// ITLA-rs
10// Copyright (C) 2021  PolyOrbite
11//
12// This program is free software: you can redistribute it and/or modify
13// it under the terms of the GNU Lesser General Public License as published
14// by the Free Software Foundation, either version 3 of the License, or
15// (at your option) any later version.
16//
17// This program is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20// GNU General Public License for more details.
21//
22// You should have received a copy of the GNU General Public License
23// along with this program.  If not, see <http://www.gnu.org/licenses/>.
24//
25
26#![cfg_attr(not(test), no_std)]
27
28use core::convert::TryInto;
29use core::fmt;
30#[cfg(not(test))]
31use defmt::*;
32use embedded_hal::{
33    digital::v2::{InputPin, OutputPin},
34    serial::{Read, Write},
35};
36use heapless::Vec;
37#[cfg(test)]
38use log::*;
39
40#[cfg(not(test))]
41use defmt as logger;
42#[cfg(test)]
43use std as logger;
44
45// Registers that are available for requests to the ITLA laser
46pub mod registers;
47#[cfg(test)]
48pub mod tests;
49
50pub use registers::{CommandStatus, ReadRegister, Request, Response, WriteRegister};
51
52// Used by `wait_for_clear` to determine what to do after a request
53#[derive(Debug, Clone, Copy, defmt::Format, PartialEq, Eq)]
54pub enum AfterRequest {
55    Continue,
56    Exit,
57}
58
59#[derive(Debug, Clone, Copy, defmt::Format, PartialEq, Eq)]
60enum LaserTransferState {
61    // Sending a command
62    Command(Request),
63    // Wait state when commands return pending
64    WaitForClear(Response),
65    // Requesting more info when commands fail
66    ReadErrorStatus(Request),
67    // Ready for new commands
68    Ready,
69}
70
71pub enum Error<S: Read<u8> + Write<u8>, I: InputPin, R: OutputPin, M: OutputPin> {
72    Tx(<S as Write<u8>>::Error),
73    Rx(<S as Read<u8>>::Error),
74    SrqPin(<I as InputPin>::Error),
75    ResetPin(<R as OutputPin>::Error),
76    CommResetPin(<M as OutputPin>::Error),
77    Srq(Response),
78    CommandError(Request, Response),
79    TooManyRetries(u32),
80}
81
82impl<S: Read<u8> + Write<u8>, I: InputPin, R: OutputPin, M: OutputPin> fmt::Debug
83    for Error<S, I, R, M>
84where
85    <S as Write<u8>>::Error: fmt::Debug,
86    <S as Read<u8>>::Error: fmt::Debug,
87    <I as InputPin>::Error: fmt::Debug,
88    <R as OutputPin>::Error: fmt::Debug,
89    <M as OutputPin>::Error: fmt::Debug,
90{
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            Self::Tx(tx) => core::write!(f, "Error while transmitting: {:?}", tx),
94            Self::Rx(rx) => core::write!(f, "Error while receiving: {:?}", rx),
95            Self::SrqPin(srq) => core::write!(f, "Could not read srq pin: {:?}", srq),
96            Self::ResetPin(reset) => core::write!(f, "Could not reset pin: {:?}", reset),
97            Self::CommResetPin(comms) => {
98                core::write!(f, "Could not change communication reset pin: {:?}", comms)
99            }
100            Self::Srq(_response) => core::write!(f, "Requested attention"),
101            Self::CommandError(req, resp) => {
102                core::write!(f, "Command {:?} returned error {:?}", req, resp)
103            }
104            Self::TooManyRetries(retries) => {
105                core::write!(f, "The laser was not ready after {} retries", retries)
106            }
107        }
108    }
109}
110
111#[derive(Debug)]
112pub struct Laser<R, D, M, I, S, F> {
113    // Pins for physical communication (See page 21 of RM)
114    n_reset: R,
115    n_disable: D,
116    n_ms: M,
117    srq: I,
118    serial: S,
119    // Each command/response is 4 bytes long (See page 26 of RM)
120    tx_buf: Vec<u8, 4>,
121    rx_buf: Vec<u8, 4>,
122    state: LaserTransferState,
123    // Was the last command/response ok? (indicates retry)
124    last_ok: bool,
125    // to add delay between successive waiting
126    after_request: Option<F>,
127    retries: u32,
128}
129
130#[derive(Debug, defmt::Format, Clone, Copy, PartialEq, Eq)]
131#[non_exhaustive]
132// Execution error during communication with the laser
133// See *Execution Error Field Conditions* at page 19 of RM
134pub enum LastCommandError {
135    Ok = 0x00,
136    RegisterNotImplemented = 0x01,
137    RegisterNotWriteable = 0x02,
138    RegisterValueRangeError = 0x03,
139    Pending = 0x04,
140    Initializing = 0x05,
141    AddressInvalid = 0x06,
142    AddressReadOnly = 0x07,
143    ExecutionFailure = 0x08,
144    OutputEnabled = 0x09,
145    InvalidConfig = 0x0A,
146    Vendor = 0x0F,
147}
148
149impl<R, D, M, I, S> Laser<R, D, M, I, S, fn(u32) -> AfterRequest>
150where
151    R: OutputPin,
152    D: OutputPin,
153    M: OutputPin,
154    I: InputPin,
155    S: Read<u8> + Write<u8>,
156{
157    // Reset and establish communication with a new ITLA laser
158    pub fn new(
159        n_reset: R,
160        n_disable: D,
161        n_ms: M,
162        srq: I,
163        serial: S,
164    ) -> Result<Self, Error<S, I, R, M>> {
165        Self::new_with_handler_between_retries(n_reset, n_disable, n_ms, srq, serial, None)
166    }
167}
168
169impl<R, D, M, I, S, F> Laser<R, D, M, I, S, F>
170where
171    R: OutputPin,
172    D: OutputPin,
173    M: OutputPin,
174    I: InputPin,
175    S: Read<u8> + Write<u8>,
176    F: FnMut(u32) -> AfterRequest,
177{
178    // Reset and establish communication with a new ITLA laser
179    // `after_request` is called after each verification of the status and can be used
180    //   for exponential backoff by adding delay between requests or for adding timeouts
181    //   by returning AfterRequest::Exit
182    pub fn new_with_handler_between_retries(
183        n_reset: R,
184        n_disable: D,
185        n_ms: M,
186        srq: I,
187        serial: S,
188        between_retries: Option<F>,
189    ) -> Result<Self, Error<S, I, R, M>> {
190        let mut this = Self {
191            n_reset,
192            n_disable,
193            n_ms,
194            srq,
195            serial,
196            tx_buf: Vec::new(),
197            rx_buf: Vec::new(),
198            state: LaserTransferState::Ready,
199            last_ok: true,
200            after_request: between_retries,
201            retries: 0,
202        };
203        this.n_ms.set_high().map_err(Error::CommResetPin)?;
204        this.reset().map_err(Error::ResetPin)?;
205        Ok(this)
206    }
207
208    // Release all the components of the laser control
209    pub fn release(self) -> (R, D, M, I, S) {
210        (
211            self.n_reset,
212            self.n_disable,
213            self.n_ms,
214            self.srq,
215            self.serial,
216        )
217    }
218
219    // Get access to the serial link. This can be used for example
220    //   to clear interrupts.
221    // WARNING: Do not read or write on the link. This can
222    //   interfere with the library and cause unexpected behaviour
223    pub fn serial(&mut self) -> &mut S {
224        &mut self.serial
225    }
226
227    // Prepare buffers for initiating communication
228    fn start_send(&mut self) {
229        // Sanity tests
230        logger::assert!(self.tx_buf.is_empty(), "Tx buffer is not empty");
231        logger::assert!(self.rx_buf.is_empty(), "Rx buffer is not empty");
232        let req = self
233            .current_command()
234            .expect("Need a command to start sending it to the laser");
235
236        self.tx_buf
237            .extend(req.encode(self.last_ok).iter().rev().copied());
238        #[cfg(test)]
239        debug!("Send command {:X?} => {:?}", &self.tx_buf[..], req);
240        #[cfg(not(test))]
241        debug!("Send command {:X} => {}", &self.tx_buf[..], req);
242    }
243
244    // Returns the command that is currently sent to the laser
245    pub fn current_command(&self) -> Option<Request> {
246        match self.state {
247            LaserTransferState::Ready => None,
248            LaserTransferState::Command(req) => Some(req),
249            LaserTransferState::ReadErrorStatus(_req) => Some(registers::Status::read()),
250            LaserTransferState::WaitForClear(_req) => Some(registers::Status::read()),
251        }
252    }
253
254    // Advance the communication process. This library is non-blocking and, as such,
255    //   this function will stop when the UART peripheral is no longer ready to communicate
256    //   with it. You can either call `nb::block!` on it to wait in a tight loop or wait
257    //   for the UART interrupt to further advance the task.
258    // When this function either returns `Ok(…)` or `Err(nb::Error::Other(…))`, the pending
259    //   command is respectively completed or aborted. You should not call `step` again after
260    //   this happens.
261    pub fn step(&mut self) -> nb::Result<Response, Error<S, I, R, M>> {
262        // Verify that there indeed is a command pending
263        logger::assert_ne!(
264            self.state,
265            LaserTransferState::Ready,
266            "Tried to poll after completion"
267        );
268        // Empty tx buffer
269        while let Some(byte) = self.tx_buf.last().copied() {
270            self.serial.write(byte).map_err(|e| e.map(Error::Tx))?;
271            trace!("Printed {:X}", byte);
272            // Do not pop before the peripheral has accepted the byte
273            self.tx_buf.pop();
274        }
275        // Fill rx buffer
276        while self.rx_buf.len() < 4 {
277            let byte = self.serial.read().map_err(|e| e.map(Error::Rx))?;
278            trace!("Received {:X}", byte);
279            self.rx_buf.push(byte).expect("Length is less than 4");
280        }
281        // Convert to fixed-length buffer
282        let buf: [u8; 4] = AsRef::<[u8]>::as_ref(&self.rx_buf)
283            .try_into()
284            .expect("Length is 4");
285        let resp = Response::decode(buf);
286        #[cfg(test)]
287        debug!("Received response {:X?} => {:?}", self.rx_buf, resp);
288        #[cfg(not(test))]
289        debug!("Received response {:X} => {}", self.rx_buf, resp);
290        self.rx_buf.clear();
291        self.last_ok = resp.is_some();
292        let resp = match resp {
293            Some(r) if !r.ce => r,
294            _ => {
295                warn!("Communication error");
296                self.start_send();
297                return self.step();
298            }
299        };
300        if resp.status == CommandStatus::ExecutionError {
301            // If an execution error occurs, read the status field to retrieve the
302            //  exact cause
303            self.state = LaserTransferState::ReadErrorStatus(
304                self.current_command().expect("Not in ready state"),
305            );
306            self.start_send();
307            return self.step();
308        }
309        if resp.status != CommandStatus::Ok {
310            #[cfg(test)]
311            debug!("Status: {:?}", resp.status);
312            #[cfg(not(test))]
313            debug!("Status: {}", resp.status);
314        }
315        // Sanity check
316        logger::assert_eq!(
317            resp.register,
318            self.current_command()
319                .expect("Tested at top of function")
320                .register(),
321            "Request register is not output register?"
322        );
323        if self.srq().map_err(Error::SrqPin)? {
324            return Err(Error::Srq(resp).into());
325        }
326        let out = match self.state {
327            LaserTransferState::Ready => logger::unreachable!("Checked at start of function"),
328            LaserTransferState::Command(_command) => {
329                if resp.status == CommandStatus::CommandPending {
330                    self.retries = 1;
331                    // Wait for the command to complete on the laser side
332                    self.state = LaserTransferState::WaitForClear(resp);
333                    self.start_send();
334                    self.step()
335                } else {
336                    self.state = LaserTransferState::Ready;
337                    Ok(resp)
338                }
339            }
340            LaserTransferState::ReadErrorStatus(command) => {
341                self.state = LaserTransferState::Ready;
342                error!("Error: {}", resp.val() & 0x00_0F);
343                Err(Error::CommandError(command, resp).into())
344            }
345            LaserTransferState::WaitForClear(prev_resp) => {
346                // See page 39 of RM for details of NOP command
347                const PENDING_FLAGS: u16 = 0xFF_00;
348                const MRDY_FLAG: u16 = 0x00_10;
349
350                // Wait for no pending operations and for the module to be ready
351                // TODO: Right now, the library requests from the laser in a busy loop. Provide
352                //   option for delays between requests
353                if resp.val() & PENDING_FLAGS == 0x00_00 && resp.val() & MRDY_FLAG == MRDY_FLAG {
354                    self.state = LaserTransferState::Ready;
355                    Ok(prev_resp)
356                } else {
357                    let retries = self.retries;
358                    if self
359                        .after_request
360                        .as_mut()
361                        .map_or(AfterRequest::Continue, |f| f(retries))
362                        == AfterRequest::Continue
363                    {
364                        self.retries += 1;
365                        self.start_send();
366                        self.step()
367                    } else {
368                        Err(nb::Error::Other(Error::TooManyRetries(self.retries)))
369                    }
370                }
371            }
372        }?;
373        Ok(out)
374    }
375
376    // Verify if the laser requested attention by the operator
377    pub fn srq(&self) -> Result<bool, <I as InputPin>::Error> {
378        self.srq.is_low()
379    }
380
381    // Disable the laser's output
382    pub fn disable(&mut self) -> Result<(), <D as OutputPin>::Error> {
383        self.n_disable.set_low()
384    }
385
386    // Enable the laser's output
387    pub fn enable(&mut self) -> Result<(), <D as OutputPin>::Error> {
388        self.n_disable.set_high()
389    }
390
391    // Reset the communication state to starting values
392    fn reset_state(&mut self) {
393        while self.serial.read().is_ok() {} // Clear input buffer
394        self.tx_buf.clear();
395        self.rx_buf.clear();
396        self.last_ok = true;
397        self.state = LaserTransferState::Ready;
398    }
399
400    // Reset the laser. This resets all unsaved state on the laser as
401    //   well as all pending commands
402    pub fn reset(&mut self) -> Result<(), <R as OutputPin>::Error> {
403        self.n_reset.set_low()?;
404        self.n_reset.set_high()?;
405        self.reset_state();
406        Ok(())
407    }
408
409    // Reset the communications with the laser. This resets all pending
410    //   commands
411    pub fn reset_comms(&mut self) -> Result<(), <M as OutputPin>::Error> {
412        self.n_ms.set_low()?;
413        self.n_ms.set_high()?;
414        self.reset_state();
415        Ok(())
416    }
417
418    // Start sending a command. To finish execution of the command, `laser.step()` must
419    //   be called until Ok(…) or Err(Error::Other(…)) is returned.
420    pub fn send(&mut self, command: Request) {
421        logger::assert_eq!(
422            self.state,
423            LaserTransferState::Ready,
424            "Can't send command while there is still one pending"
425        );
426        self.state = LaserTransferState::Command(command);
427        self.start_send();
428    }
429
430    // Wait for the laser to be clear. i.e. to be ready to enable its output and to have
431    //   finished pending commands.
432    // This command is blocking.
433    pub fn wait_for_clear(&mut self) -> nb::Result<(), Error<S, I, R, M>> {
434        trace!("Waiting for laser clear");
435        self.retries = 0;
436        loop {
437            self.send(registers::Status::read());
438            let resp = nb::block!(self.step())?;
439            let flags = resp.val();
440            trace!("Laser flags: {:X}", flags);
441            if flags & 0xFF_00 == 0 && flags & 0x00_10 == 0x00_10 {
442                trace!("Laser clear");
443                return Ok(());
444            }
445            let retries = self.retries;
446            let early_exit = self
447                .after_request
448                .as_mut()
449                .map_or(AfterRequest::Continue, |f| f(retries));
450            if early_exit == AfterRequest::Exit {
451                debug!("Requested early stop of wait_for_clear");
452                return Err(nb::Error::Other(Error::TooManyRetries(self.retries)));
453            }
454            self.retries = self.retries.saturating_add(1);
455        }
456    }
457}