itla 0.1.2

ITLA (micro laser) non-blocking control library using embedded-hal
Documentation
use defmt::*;

// Type of command to the laser
#[derive(Copy, Clone, PartialEq, Eq, Debug, defmt::Format)]
enum Direction {
    Read,
    Write,
}

// Status of the command executed. See page 31 of RM
#[derive(Copy, Clone, PartialEq, Eq, Debug, defmt::Format)]
pub enum CommandStatus {
    Ok = 0x00,
    ExecutionError = 0x01,
    ExtendedAddressing = 0x02,
    CommandPending = 0x03,
}

// Request to the laser
#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
pub struct Request {
    direction: Direction,
    register: u8,
    data: [u8; 2],
}

impl Request {
    // Get the register this request is targetting
    pub fn register(&self) -> u8 {
        self.register
    }

    // Compute the checksum for the command. See RM page 29 for definition
    fn checksum(input: [u8; 4]) -> u8 {
        let bip8 = (input[0] & 0x0F) ^ input[1] ^ input[2] ^ input[3];
        ((bip8 & 0xF0) >> 4) ^ (bip8 & 0x0F)
    }

    // Encode the request to a 4 bytes buffer. See pages 29 to 32 of RM
    pub fn encode(self, last_ok: bool) -> [u8; 4] {
        let mut out = [0u8; 4];

        // Application layer
        out[0] = (self.direction == Direction::Write) as u8;
        out[1] = self.register;
        out[2] = self.data[0];
        out[3] = self.data[1];

        // Transport layer
        out[0] |= ((!last_ok) as u8) << 3;
        out[0] |= Self::checksum(out) << 4;

        out
    }
}

// Response from the laser
#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
pub struct Response {
    pub ce: bool,
    pub status: CommandStatus,
    pub register: u8,
    pub data: [u8; 2],
}

impl Response {
    // Get response's value (the register value)
    pub fn val(&self) -> u16 {
        trace!("Value: {:X}", self.data);
        u16::from_be_bytes(self.data)
    }

    // Decode the packet format. See page 29 to 32 of RM
    pub fn decode(response: [u8; 4]) -> Option<Self> {
        let cksum = (response[0] >> 4) & 0x0F;
        let ce = (response[0] >> 3) & 0x01;
        let status = (response[0]) & 0x03;

        let bit3_on = (response[0] >> 2) & 0x01 == 0x01;

        let register = response[1];
        let data = [response[2], response[3]];
        let expected = Request::checksum(response);

        if cksum == expected && bit3_on {
            Some(Self {
                ce: ce != 0,
                status: match status {
                    // Convert 2 bits integer to enum
                    x if x == CommandStatus::Ok as u8 => CommandStatus::Ok,
                    x if x == CommandStatus::ExecutionError as u8 => CommandStatus::ExecutionError,
                    x if x == CommandStatus::ExtendedAddressing as u8 => {
                        CommandStatus::ExtendedAddressing
                    }
                    x if x == CommandStatus::CommandPending as u8 => CommandStatus::CommandPending,
                    _ => {
                        if cfg!(test) {
                            core::unreachable!("No other 2 bits value")
                        } else {
                            defmt::unreachable!("No other 2 bits value")
                        }
                    }
                },
                register,
                data,
            })
        } else {
            None
        }
    }
}

// General trait for registers of the laser
pub trait Register {
    fn register() -> u8;
}

// A register that can be read by the laser
pub trait ReadRegister: Register {
    fn read() -> Request {
        Request {
            direction: Direction::Read,
            register: Self::register(),
            data: [0; 2],
        }
    }
}

// A register that can be written to by the laser
pub trait WriteRegister: Register {
    fn write(data: u16) -> Request {
        Request {
            direction: Direction::Write,
            register: Self::register(),
            data: data.to_be_bytes(),
        }
    }
}

// Helper macro that implements the corresponding traits for the registers
macro_rules! register {
    {$v:expr => $reg:ident} => {
        #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
        pub struct $reg;

        impl Register for $reg {
            fn register() -> u8 {
                $v
            }
        }
    };
    {$v:expr => $reg:ident : r} => {
        register!{$v => $reg}

        impl ReadRegister for $reg {}
    };
    {$v:expr => $reg:ident : w} => {
        register!{$v => $reg}

        impl WriteRegister for $reg {}
    };
    {$v:expr => $reg:ident : rw} => {
        register!{$v => $reg}

        impl ReadRegister for $reg {}
        impl WriteRegister for $reg {}
    };
}

// The registers implemented by the laser. See page 33 to 36 of RM
register! { 0x00 => Status : rw }
register! { 0x01 => DevTyp : r }
register! { 0x02 => Mfgr : r }
register! { 0x03 => Model : r }
register! { 0x04 => SerNo : r }
register! { 0x05 => MfgDate : r }
register! { 0x06 => Release : r }
register! { 0x07 => RelBack : r }
register! { 0x08 => GenCfg : rw }
register! { 0x09 => Aeaeac : r }
register! { 0x0A => Aeaea : r }
register! { 0x0B => Aeaear : rw }
register! { 0x0D => IOCap : rw }
register! { 0x0E => Eac : rw }
register! { 0x0F => Ea : rw }
register! { 0x10 => Ear : rw }
register! { 0x14 => DlConfig : rw }
register! { 0x15 => DlStatus : r }
register! { 0x20 => StatusF : rw }
register! { 0x21 => StatusW : rw }
register! { 0x22 => FPowTh : rw }
register! { 0x23 => WPowTh : rw }
register! { 0x24 => FFreqTh : r }
register! { 0x25 => WFreqTh : r }
register! { 0x26 => FTempTh : r }
register! { 0x27 => WTempTh : r }
register! { 0x28 => SrqT : rw }
register! { 0x29 => FatalT : rw }
register! { 0x2A => AlmT : rw }
register! { 0x30 => Channel : rw }
register! { 0x31 => Pwr : rw }
register! { 0x32 => Resena : rw }
register! { 0x33 => Mcb : rw }
register! { 0x34 => Grid : rw }
register! { 0x35 => Fcf1 : rw }
register! { 0x36 => Fcf2 : rw }
register! { 0x40 => Lf1 : r }
register! { 0x41 => Lf2 : r }
register! { 0x42 => Oop : r }
register! { 0x43 => Ctemp : r }
register! { 0x50 => Opsl : r }
register! { 0x51 => Opsh : r }
register! { 0x52 => Lfl1 : r }
register! { 0x53 => Lfl2 : r }
register! { 0x54 => Lfh1 : r }
register! { 0x55 => Lfh2 : r }
register! { 0x56 => Lgrid : r }
register! { 0x57 => Currents : r }
register! { 0x58 => Temp : r }
register! { 0x59 => DitherE : rw }
register! { 0x5A => DitherR : rw }
register! { 0x5B => DitherF : rw }
register! { 0x5C => DitherA : rw }
register! { 0x5D => Tbtfl : rw }
register! { 0x5E => Tbtfh : rw }
register! { 0x5F => FAgeTh : rw }
register! { 0x60 => WAgeTh : rw }
register! { 0x61 => Age : r }
register! { 0x62 => Ftf : rw }

#[cfg(any(feature = "ppcl200", test))]
pub mod ppcl200 {
    use super::*;

    // Commands specific to the PPCL200 laser
    register! { 0x90 => CleanMode : rw }
    register! { 0xE0 => Password : rw }
    register! { 0xE4 => CleanSweepAmplitude : rw }
    register! { 0xE5 => CleanSweepEnable : rw }
    register! { 0xE6 => CleanSweepOffset : rw }
    register! { 0xE8 => TemperatureSlope : r }
    register! { 0xE9 => CleanJumpCurrent : rw }
    register! { 0xEA => CleanJumpThz : rw }
    register! { 0xEB => CleanJumpGhz : rw }
    register! { 0xEC => CleanJumpSled : rw }
    register! { 0xED => CleanJumpEnable : rw }
}