use defmt::*;
#[derive(Copy, Clone, PartialEq, Eq, Debug, defmt::Format)]
enum Direction {
Read,
Write,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, defmt::Format)]
pub enum CommandStatus {
Ok = 0x00,
ExecutionError = 0x01,
ExtendedAddressing = 0x02,
CommandPending = 0x03,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
pub struct Request {
direction: Direction,
register: u8,
data: [u8; 2],
}
impl Request {
pub fn register(&self) -> u8 {
self.register
}
fn checksum(input: [u8; 4]) -> u8 {
let bip8 = (input[0] & 0x0F) ^ input[1] ^ input[2] ^ input[3];
((bip8 & 0xF0) >> 4) ^ (bip8 & 0x0F)
}
pub fn encode(self, last_ok: bool) -> [u8; 4] {
let mut out = [0u8; 4];
out[0] = (self.direction == Direction::Write) as u8;
out[1] = self.register;
out[2] = self.data[0];
out[3] = self.data[1];
out[0] |= ((!last_ok) as u8) << 3;
out[0] |= Self::checksum(out) << 4;
out
}
}
#[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 {
pub fn val(&self) -> u16 {
trace!("Value: {:X}", self.data);
u16::from_be_bytes(self.data)
}
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 {
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
}
}
}
pub trait Register {
fn register() -> u8;
}
pub trait ReadRegister: Register {
fn read() -> Request {
Request {
direction: Direction::Read,
register: Self::register(),
data: [0; 2],
}
}
}
pub trait WriteRegister: Register {
fn write(data: u16) -> Request {
Request {
direction: Direction::Write,
register: Self::register(),
data: data.to_be_bytes(),
}
}
}
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 {}
};
}
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::*;
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 }
}