use crate::{command::WriteCommand, instr::Instr};
macro_rules! millivolt_value {
(
$(#[$meta:meta])*
$name:ident: $min:literal ..= $max:literal,
zero = $zero:literal,
step = $step:literal
) => {
$(#[$meta])*
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct $name(u8);
impl $name {
#[doc = concat!(
"Voltage in millivolts, ", stringify!($min), " to ", stringify!($max),
" in steps of ", stringify!($step), " mV.\n\n# Panics\n\n",
"If `mv` is out of range or not on a step (at compile time in a `const`)."
)]
pub const fn from_mv(mv: i16) -> Self {
assert!(
mv >= $min && mv <= $max && (mv - $zero) % $step == 0,
concat!(stringify!($name), " out of range")
);
Self(((mv - $zero).unsigned_abs() / $step) as u8)
}
}
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct GateVoltageControl {
vgha: Vgh,
vgla: Vgl,
}
impl GateVoltageControl {
pub const DEFAULT: Self = Self {
vgha: Vgh::from_mv(15_000),
vgla: Vgl::from_mv(-10_000),
};
#[must_use]
pub const fn with_vgh(mut self, v: Vgh) -> Self {
self.vgha = v;
self
}
#[must_use]
pub const fn with_vgl(mut self, v: Vgl) -> Self {
self.vgla = v;
self
}
const fn bytes(&self) -> [u8; 2] {
[self.vgha.0, self.vgla.0]
}
}
impl Default for GateVoltageControl {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for GateVoltageControl {
const OPCODE: Instr = Instr::GCTRL;
type Params = [u8; 2];
fn params(&self) -> Self::Params {
self.bytes()
}
}
millivolt_value!(
Vgh: 8_000..=17_000, zero = 8_000, step = 500
);
millivolt_value!(
Vgl: -15_000..=-6_000, zero = -5_000, step = 500
);
const _: () = assert!(matches!(GateVoltageControl::DEFAULT.bytes(), [0x0E, 0x0A]));
const _: () = assert!(matches!(
GateVoltageControl::DEFAULT
.with_vgh(Vgh::from_mv(8_000))
.with_vgl(Vgl::from_mv(-6_000))
.bytes(),
[0x00, 0x02]
));
const _: () = assert!(matches!(
GateVoltageControl::DEFAULT
.with_vgh(Vgh::from_mv(17_000))
.with_vgl(Vgl::from_mv(-15_000))
.bytes(),
[0x12, 0x14]
));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SourceHighPositiveVoltageControl {
vshpa: [SourceHighPositiveVoltage; 4],
}
impl SourceHighPositiveVoltageControl {
pub const DEFAULT: Self = Self {
vshpa: [
SourceHighPositiveVoltage::from_mv(5_000),
SourceHighPositiveVoltage::from_mv(5_000),
SourceHighPositiveVoltage::from_mv(5_000),
SourceHighPositiveVoltage::from_mv(5_000),
],
};
#[must_use]
pub const fn with_vshpa(mut self, v: [SourceHighPositiveVoltage; 4]) -> Self {
self.vshpa = v;
self
}
const fn bytes(&self) -> [u8; 4] {
[
self.vshpa[0].0,
self.vshpa[1].0,
self.vshpa[2].0,
self.vshpa[3].0,
]
}
}
impl Default for SourceHighPositiveVoltageControl {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for SourceHighPositiveVoltageControl {
const OPCODE: Instr = Instr::VSHPCTRL;
type Params = [u8; 4];
fn params(&self) -> Self::Params {
self.bytes()
}
}
millivolt_value!(
SourceHighPositiveVoltage: 3_700..=6_200, zero = 3_700, step = 20
);
const _: () = assert!(matches!(
SourceHighPositiveVoltageControl::DEFAULT.bytes(),
[0x41, 0x41, 0x41, 0x41]
));
const _: () = assert!(matches!(
SourceHighPositiveVoltageControl {
vshpa: [
SourceHighPositiveVoltage::from_mv(3_700),
SourceHighPositiveVoltage::from_mv(6_200),
SourceHighPositiveVoltage::from_mv(5_800),
SourceHighPositiveVoltage::from_mv(5_820)
]
}
.bytes(),
[0x00, 0x7D, 0x69, 0x6A]
));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SourceLowPositiveVoltageControl {
vslpa: [SourceLowPositiveVoltage; 4],
}
impl SourceLowPositiveVoltageControl {
pub const DEFAULT: Self = Self {
vslpa: [
SourceLowPositiveVoltage::from_mv(1_000),
SourceLowPositiveVoltage::from_mv(1_000),
SourceLowPositiveVoltage::from_mv(1_000),
SourceLowPositiveVoltage::from_mv(1_000),
],
};
#[must_use]
pub const fn with_vslpa(mut self, v: [SourceLowPositiveVoltage; 4]) -> Self {
self.vslpa = v;
self
}
const fn bytes(&self) -> [u8; 4] {
[
self.vslpa[0].0,
self.vslpa[1].0,
self.vslpa[2].0,
self.vslpa[3].0,
]
}
}
impl Default for SourceLowPositiveVoltageControl {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for SourceLowPositiveVoltageControl {
const OPCODE: Instr = Instr::VSLPCTRL;
type Params = [u8; 4];
fn params(&self) -> Self::Params {
self.bytes()
}
}
millivolt_value!(
SourceLowPositiveVoltage: 0..=2_000, zero = 0, step = 20
);
const _: () = assert!(matches!(
SourceLowPositiveVoltageControl::DEFAULT.bytes(),
[0x32, 0x32, 0x32, 0x32]
));
const _: () = assert!(matches!(
SourceLowPositiveVoltageControl {
vslpa: [
SourceLowPositiveVoltage::from_mv(0),
SourceLowPositiveVoltage::from_mv(2_000),
SourceLowPositiveVoltage::from_mv(500),
SourceLowPositiveVoltage::from_mv(520)
]
}
.bytes(),
[0x00, 0x64, 0x19, 0x1A]
));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SourceHighNegativeVoltageControl {
vshna: [SourceHighNegativeVoltage; 4],
}
impl SourceHighNegativeVoltageControl {
pub const DEFAULT: Self = Self {
vshna: [
SourceHighNegativeVoltage::from_mv(-3_900),
SourceHighNegativeVoltage::from_mv(-3_900),
SourceHighNegativeVoltage::from_mv(-3_900),
SourceHighNegativeVoltage::from_mv(-3_900),
],
};
#[must_use]
pub const fn with_vshna(mut self, v: [SourceHighNegativeVoltage; 4]) -> Self {
self.vshna = v;
self
}
const fn bytes(&self) -> [u8; 4] {
[
self.vshna[0].0,
self.vshna[1].0,
self.vshna[2].0,
self.vshna[3].0,
]
}
}
impl Default for SourceHighNegativeVoltageControl {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for SourceHighNegativeVoltageControl {
const OPCODE: Instr = Instr::VSHNCTRL;
type Params = [u8; 4];
fn params(&self) -> Self::Params {
self.bytes()
}
}
millivolt_value!(
SourceHighNegativeVoltage: -5_200..=-2_500, zero = -2_500, step = 20
);
const _: () = assert!(matches!(
SourceHighNegativeVoltageControl::DEFAULT.bytes(),
[0x46, 0x46, 0x46, 0x46]
));
const _: () = assert!(matches!(
SourceHighNegativeVoltageControl {
vshna: [
SourceHighNegativeVoltage::from_mv(-2_500),
SourceHighNegativeVoltage::from_mv(-5_200),
SourceHighNegativeVoltage::from_mv(-4_000),
SourceHighNegativeVoltage::from_mv(-4_020),
]
}
.bytes(),
[0x00, 0x87, 0x4B, 0x4C]
));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SourceLowNegativeVoltageControl {
vslna: [SourceLowNegativeVoltage; 4],
}
impl SourceLowNegativeVoltageControl {
pub const DEFAULT: Self = Self {
vslna: [
SourceLowNegativeVoltage::from_mv(-400),
SourceLowNegativeVoltage::from_mv(-400),
SourceLowNegativeVoltage::from_mv(-400),
SourceLowNegativeVoltage::from_mv(-400),
],
};
#[must_use]
pub const fn with_vslna(mut self, v: [SourceLowNegativeVoltage; 4]) -> Self {
self.vslna = v;
self
}
const fn bytes(&self) -> [u8; 4] {
[
self.vslna[0].0,
self.vslna[1].0,
self.vslna[2].0,
self.vslna[3].0,
]
}
}
impl Default for SourceLowNegativeVoltageControl {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for SourceLowNegativeVoltageControl {
const OPCODE: Instr = Instr::VSLNCTRL;
type Params = [u8; 4];
fn params(&self) -> Self::Params {
self.bytes()
}
}
millivolt_value!(
SourceLowNegativeVoltage: -1_800..=1_000, zero = 1_000, step = 20
);
const _: () = assert!(matches!(
SourceLowNegativeVoltageControl::DEFAULT.bytes(),
[0x46, 0x46, 0x46, 0x46]
));
const _: () = assert!(matches!(
SourceLowNegativeVoltageControl {
vslna: [
SourceLowNegativeVoltage::from_mv(1_000),
SourceLowNegativeVoltage::from_mv(-1_800),
SourceLowNegativeVoltage::from_mv(500),
SourceLowNegativeVoltage::from_mv(480),
]
}
.bytes(),
[0x00, 0x8C, 0x19, 0x1A]
));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SourceGammaVoltageControl {
vsip: VsipVoltage,
vskp: VskpVoltage,
vsin: VsinVoltage,
vskn: VsknVoltage,
}
impl SourceGammaVoltageControl {
pub const DEFAULT: Self = Self {
vsip: VsipVoltage::from_mv(3_600),
vskp: VskpVoltage::from_mv(2_600),
vsin: VsinVoltage::from_mv(-2_400),
vskn: VsknVoltage::from_mv(-1_400),
};
#[must_use]
pub const fn with_vsip(mut self, v: VsipVoltage) -> Self {
self.vsip = v;
self
}
#[must_use]
pub const fn with_vskp(mut self, v: VskpVoltage) -> Self {
self.vskp = v;
self
}
#[must_use]
pub const fn with_vsin(mut self, v: VsinVoltage) -> Self {
self.vsin = v;
self
}
#[must_use]
pub const fn with_vskn(mut self, v: VsknVoltage) -> Self {
self.vskn = v;
self
}
const fn bytes(&self) -> [u8; 4] {
[self.vsip.0, self.vskp.0, self.vsin.0, self.vskn.0]
}
}
impl Default for SourceGammaVoltageControl {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for SourceGammaVoltageControl {
const OPCODE: Instr = Instr::VSIKCTRL;
type Params = [u8; 4];
fn params(&self) -> Self::Params {
self.bytes()
}
}
millivolt_value!(
VsipVoltage: 2_200..=5_000, zero = 2_200, step = 20
);
millivolt_value!(
VskpVoltage: 1_200..=4_000, zero = 1_200, step = 20
);
millivolt_value!(
VsinVoltage: -3_800..=-1_000, zero = -1_000, step = 20
);
millivolt_value!(
VsknVoltage: -2_800..=0, zero = 0, step = 20
);
const _: () = assert!(matches!(
SourceGammaVoltageControl::DEFAULT.bytes(),
[0x46, 0x46, 0x46, 0x46]
));
const _: () = assert!(matches!(
SourceGammaVoltageControl {
vsip: VsipVoltage::from_mv(2_200),
vskp: VskpVoltage::from_mv(1_200),
vsin: VsinVoltage::from_mv(-1_000),
vskn: VsknVoltage::from_mv(0),
}
.bytes(),
[0x00, 0x00, 0x00, 0x00]
));
const _: () = assert!(matches!(
SourceGammaVoltageControl::DEFAULT
.with_vsip(VsipVoltage::from_mv(5_000))
.with_vskp(VskpVoltage::from_mv(4_000))
.with_vsin(VsinVoltage::from_mv(-3_800))
.with_vskn(VsknVoltage::from_mv(-2_800))
.bytes(),
[0x8C, 0x8C, 0x8C, 0x8C]
));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SourceVoltageSelect {
sdhlsel: SourceVoltage,
}
impl SourceVoltageSelect {
pub const DEFAULT: Self = Self {
sdhlsel: SourceVoltage::Source1,
};
pub const fn new(sdhlsel: SourceVoltage) -> Self {
Self { sdhlsel }
}
const fn bytes(&self) -> [u8; 1] {
[self.sdhlsel as u8]
}
}
impl Default for SourceVoltageSelect {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for SourceVoltageSelect {
const OPCODE: Instr = Instr::VSHLSEL;
type Params = [u8; 1];
fn params(&self) -> Self::Params {
self.bytes()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SourceVoltage {
Source1 = 0,
Source2 = 1,
Source3 = 2,
Source4 = 3,
}
const _: () = assert!(matches!(SourceVoltageSelect::DEFAULT.bytes(), [0x00]));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AutoPowerDownControl {
autopwr: AutoPowerDown,
}
impl AutoPowerDownControl {
pub const DEFAULT: Self = Self {
autopwr: AutoPowerDown::Enable,
};
pub const fn new(autopwr: AutoPowerDown) -> Self {
Self { autopwr }
}
#[allow(clippy::identity_op, reason = "readability")]
const fn bytes(&self) -> [u8; 1] {
[0xFF & !(1 << 7) | (self.autopwr as u8) << 7]
}
}
impl Default for AutoPowerDownControl {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for AutoPowerDownControl {
const OPCODE: Instr = Instr::AUTOPWRCTRL;
type Params = [u8; 1];
fn params(&self) -> Self::Params {
self.bytes()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AutoPowerDown {
Disable = 0,
Enable = 1,
}
const _: () = assert!(matches!(AutoPowerDownControl::DEFAULT.bytes(), [0xFF]));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BoosterControl {
bsten: Booster,
}
impl BoosterControl {
pub const DEFAULT: Self = Self {
bsten: Booster::Disable,
};
pub const fn new(bsten: Booster) -> Self {
Self { bsten }
}
const fn bytes(&self) -> [u8; 1] {
[self.bsten as u8]
}
}
impl Default for BoosterControl {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for BoosterControl {
const OPCODE: Instr = Instr::BSTEN;
type Params = [u8; 1];
fn params(&self) -> Self::Params {
self.bytes()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Booster {
Disable = 0,
Enable = 1,
}
const _: () = assert!(matches!(BoosterControl::DEFAULT.bytes(), [0x00]));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OscSetting {
oscen: OscEnable,
oscsw: OscSwitch,
}
impl OscSetting {
pub const DEFAULT: Self = Self {
oscen: OscEnable::Disable,
oscsw: OscSwitch::HpmMax32Hz,
};
#[must_use]
pub const fn with_osc_en(mut self, v: OscEnable) -> Self {
self.oscen = v;
self
}
#[must_use]
pub const fn with_osc_switch(mut self, v: OscSwitch) -> Self {
self.oscsw = v;
self
}
#[allow(clippy::identity_op, reason = "readability")]
const fn bytes(&self) -> [u8; 2] {
let sw = match self.oscsw {
OscSwitch::HpmMax32Hz => 1 << 5 | 1 << 2 | 1 << 1,
OscSwitch::HpmMax51Hz => 0,
};
[
0x26 & !(1 << 7 | 1 << 5 | 0b11 << 1) | (self.oscen as u8) << 7 | sw,
0xE9,
]
}
}
impl Default for OscSetting {
fn default() -> Self {
Self::DEFAULT
}
}
impl WriteCommand for OscSetting {
const OPCODE: Instr = Instr::OSCSET;
type Params = [u8; 2];
fn params(&self) -> Self::Params {
self.bytes()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OscEnable {
Disable = 0,
Enable = 1,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OscSwitch {
HpmMax51Hz = 0b000,
HpmMax32Hz = 0b111,
}
const _: () = assert!(matches!(OscSetting::DEFAULT.bytes(), [0x26, 0xE9]));