#![no_std]
#[cfg(test)]
extern crate std;
use embedded_hal::delay::DelayNs;
use embedded_hal::i2c::{I2c, SevenBitAddress};
#[cfg(feature = "async")]
use embedded_hal_async::delay::DelayNs as AsyncDelayNs;
#[cfg(feature = "async")]
use embedded_hal_async::i2c::I2c as AsyncI2c;
const CONTROL_WRITE_MASK: u8 = 0x9f;
const CONFIGURATION_WRITE_MASK: u8 = 0x1f;
const BOOST_CONTROL_WRITE_MASK: u8 = 0x7f;
const CURRENT_A_WRITE_MASK: u8 = 0xdf;
const CURRENT_B_WRITE_MASK: u8 = 0x1f;
const RAMP_WRITE_MASK: u8 = 0x3f;
const INTERRUPT_ENABLE_WRITE_MASK: u8 = 0x87;
const FAULT_STATUS_WRITE_MASK: u8 = 0x3f;
const SOFTWARE_RESET_WRITE_MASK: u8 = 0x01;
const FILTER_STRENGTH_WRITE_MASK: u8 = 0x03;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Address {
SelLow,
SelHigh,
}
impl Address {
pub const fn as_u8(self) -> SevenBitAddress {
match self {
Self::SelLow => 0x36,
Self::SelHigh => 0x38,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Register {
Control = 0x00,
Configuration = 0x01,
BoostControl = 0x02,
BrightnessA = 0x03,
BrightnessB = 0x04,
CurrentA = 0x05,
CurrentB = 0x06,
OnOffRamp = 0x07,
RunRamp = 0x08,
InterruptStatus = 0x09,
InterruptEnable = 0x0a,
FaultStatus = 0x0b,
SoftwareReset = 0x0f,
PwmOutLow = 0x12,
PwmOutHigh = 0x13,
Revision = 0x1f,
FilterStrength = 0x50,
}
impl Register {
pub const fn address(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Bank {
A,
B,
}
impl Bank {
const fn brightness_register(self) -> Register {
match self {
Self::A => Register::BrightnessA,
Self::B => Register::BrightnessB,
}
}
const fn current_register(self) -> Register {
match self {
Self::A => Register::CurrentA,
Self::B => Register::CurrentB,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Led2Bank {
A,
#[default]
B,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Mapping {
#[default]
Exponential,
Linear,
}
impl Mapping {
const fn from_bit(bit: bool) -> Self {
if bit { Self::Linear } else { Self::Exponential }
}
const fn bit(self) -> bool {
matches!(self, Self::Linear)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum PwmPolarity {
#[default]
ActiveHigh,
ActiveLow,
}
impl PwmPolarity {
const fn from_bit(bit: bool) -> Self {
if bit {
Self::ActiveLow
} else {
Self::ActiveHigh
}
}
const fn bit(self) -> bool {
matches!(self, Self::ActiveLow)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Brightness(u8);
impl Brightness {
pub const OFF: Self = Self(0x00);
pub const MIN_ON: Self = Self(0x04);
pub const MAX: Self = Self(0xff);
pub const fn from_raw(raw: u8) -> Self {
Self(raw)
}
pub const fn raw(self) -> u8 {
self.0
}
}
impl From<u8> for Brightness {
fn from(value: u8) -> Self {
Self::from_raw(value)
}
}
impl From<Brightness> for u8 {
fn from(value: Brightness) -> Self {
value.raw()
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct FullScaleCurrent {
code: u8,
}
impl FullScaleCurrent {
pub const MIN: Self = Self { code: 0 };
pub const MAX: Self = Self { code: 31 };
pub const fn from_code(code: u8) -> Option<Self> {
if code <= 31 {
Some(Self { code })
} else {
None
}
}
const fn from_register(value: u8) -> Self {
Self { code: value & 0x1f }
}
pub const fn code(self) -> u8 {
self.code
}
pub const fn microamps(self) -> u32 {
5_000 + (self.code as u32 * 750)
}
}
impl Default for FullScaleCurrent {
fn default() -> Self {
Self::MAX
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ControlConfig {
pub sleep: bool,
pub bank_a_mapping: Mapping,
pub bank_b_mapping: Mapping,
pub bank_a_enabled: bool,
pub bank_b_enabled: bool,
pub led2_bank: Led2Bank,
}
impl ControlConfig {
pub const fn bits(self) -> u8 {
let mut bits = 0;
if self.sleep {
bits |= 0x80;
}
if self.bank_a_mapping.bit() {
bits |= 0x10;
}
if self.bank_b_mapping.bit() {
bits |= 0x08;
}
if self.bank_a_enabled {
bits |= 0x04;
}
if self.bank_b_enabled {
bits |= 0x02;
}
if matches!(self.led2_bank, Led2Bank::A) {
bits |= 0x01;
}
bits
}
pub const fn from_bits(bits: u8) -> Self {
Self {
sleep: bits & 0x80 != 0,
bank_a_mapping: Mapping::from_bit(bits & 0x10 != 0),
bank_b_mapping: Mapping::from_bit(bits & 0x08 != 0),
bank_a_enabled: bits & 0x04 != 0,
bank_b_enabled: bits & 0x02 != 0,
led2_bank: if bits & 0x01 != 0 {
Led2Bank::A
} else {
Led2Bank::B
},
}
}
}
impl Default for ControlConfig {
fn default() -> Self {
Self {
sleep: true,
bank_a_mapping: Mapping::Exponential,
bank_b_mapping: Mapping::Exponential,
bank_a_enabled: false,
bank_b_enabled: false,
led2_bank: Led2Bank::B,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ControlState {
pub sleep_command: bool,
pub sleeping: bool,
pub bank_a_mapping: Mapping,
pub bank_b_mapping: Mapping,
pub bank_a_enabled: bool,
pub bank_b_enabled: bool,
pub led2_bank: Led2Bank,
}
impl ControlState {
pub const fn from_bits(bits: u8) -> Self {
Self {
sleep_command: bits & 0x80 != 0,
sleeping: bits & 0x40 != 0,
bank_a_mapping: Mapping::from_bit(bits & 0x10 != 0),
bank_b_mapping: Mapping::from_bit(bits & 0x08 != 0),
bank_a_enabled: bits & 0x04 != 0,
bank_b_enabled: bits & 0x02 != 0,
led2_bank: if bits & 0x01 != 0 {
Led2Bank::A
} else {
Led2Bank::B
},
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Configuration {
pub feedback_bank_a: bool,
pub feedback_bank_b: bool,
pub pwm_polarity: PwmPolarity,
pub pwm_bank_a: bool,
pub pwm_bank_b: bool,
}
impl Configuration {
pub const fn bits(self) -> u8 {
let mut bits = 0;
if self.feedback_bank_b {
bits |= 0x10;
}
if self.feedback_bank_a {
bits |= 0x08;
}
if self.pwm_polarity.bit() {
bits |= 0x04;
}
if self.pwm_bank_b {
bits |= 0x02;
}
if self.pwm_bank_a {
bits |= 0x01;
}
bits
}
pub const fn from_bits(bits: u8) -> Self {
Self {
feedback_bank_a: bits & 0x08 != 0,
feedback_bank_b: bits & 0x10 != 0,
pwm_polarity: PwmPolarity::from_bit(bits & 0x04 != 0),
pwm_bank_a: bits & 0x01 != 0,
pwm_bank_b: bits & 0x02 != 0,
}
}
}
impl Default for Configuration {
fn default() -> Self {
Self {
feedback_bank_a: true,
feedback_bank_b: true,
pwm_polarity: PwmPolarity::ActiveHigh,
pwm_bank_a: false,
pwm_bank_b: false,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum OverVoltageProtection {
V16,
#[default]
V24,
V32,
V40,
}
impl OverVoltageProtection {
const fn bits(self) -> u8 {
match self {
Self::V16 => 0,
Self::V24 => 1,
Self::V32 => 2,
Self::V40 => 3,
}
}
const fn from_bits(bits: u8) -> Self {
match bits & 0x03 {
0 => Self::V16,
1 => Self::V24,
2 => Self::V32,
_ => Self::V40,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum OverCurrentProtection {
Ma600,
Ma800,
Ma1000,
#[default]
Ma1200,
}
impl OverCurrentProtection {
const fn bits(self) -> u8 {
match self {
Self::Ma600 => 0,
Self::Ma800 => 1,
Self::Ma1000 => 2,
Self::Ma1200 => 3,
}
}
const fn from_bits(bits: u8) -> Self {
match bits & 0x03 {
0 => Self::Ma600,
1 => Self::Ma800,
2 => Self::Ma1000,
_ => Self::Ma1200,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum BoostFrequency {
#[default]
Khz500,
Mhz1,
}
impl BoostFrequency {
const fn bit(self) -> bool {
matches!(self, Self::Mhz1)
}
const fn from_bit(bit: bool) -> Self {
if bit { Self::Mhz1 } else { Self::Khz500 }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BoostConfig {
pub over_voltage: OverVoltageProtection,
pub over_current: OverCurrentProtection,
pub slow_start: bool,
pub frequency_shift: bool,
pub frequency: BoostFrequency,
}
impl BoostConfig {
pub const fn bits(self) -> u8 {
let mut bits = (self.over_voltage.bits() << 5) | (self.over_current.bits() << 3);
if self.slow_start {
bits |= 0x04;
}
if self.frequency_shift {
bits |= 0x02;
}
if self.frequency.bit() {
bits |= 0x01;
}
bits
}
pub const fn from_bits(bits: u8) -> Self {
Self {
over_voltage: OverVoltageProtection::from_bits(bits >> 5),
over_current: OverCurrentProtection::from_bits(bits >> 3),
slow_start: bits & 0x04 != 0,
frequency_shift: bits & 0x02 != 0,
frequency: BoostFrequency::from_bit(bits & 0x01 != 0),
}
}
}
impl Default for BoostConfig {
fn default() -> Self {
Self {
over_voltage: OverVoltageProtection::V24,
over_current: OverCurrentProtection::Ma1200,
slow_start: false,
frequency_shift: false,
frequency: BoostFrequency::Khz500,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum RampTime {
#[default]
Code0 = 0,
Ms261 = 1,
Ms522 = 2,
Ms1045 = 3,
Ms2091 = 4,
Ms4182 = 5,
Ms8364 = 6,
Ms16730 = 7,
}
impl RampTime {
pub const fn code(self) -> u8 {
self as u8
}
pub const fn from_code(code: u8) -> Option<Self> {
match code {
0 => Some(Self::Code0),
1 => Some(Self::Ms261),
2 => Some(Self::Ms522),
3 => Some(Self::Ms1045),
4 => Some(Self::Ms2091),
5 => Some(Self::Ms4182),
6 => Some(Self::Ms8364),
7 => Some(Self::Ms16730),
_ => None,
}
}
const fn from_register(value: u8) -> Self {
match value & 0x07 {
0 => Self::Code0,
1 => Self::Ms261,
2 => Self::Ms522,
3 => Self::Ms1045,
4 => Self::Ms2091,
5 => Self::Ms4182,
6 => Self::Ms8364,
_ => Self::Ms16730,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct OnOffRamp {
pub startup: RampTime,
pub shutdown: RampTime,
}
impl OnOffRamp {
pub const fn bits(self) -> u8 {
(self.startup.code() << 3) | self.shutdown.code()
}
pub const fn from_bits(bits: u8) -> Self {
Self {
startup: RampTime::from_register(bits >> 3),
shutdown: RampTime::from_register(bits),
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct RunRamp {
pub up: RampTime,
pub down: RampTime,
}
impl RunRamp {
pub const fn bits(self) -> u8 {
(self.up.code() << 3) | self.down.code()
}
pub const fn from_bits(bits: u8) -> Self {
Self {
up: RampTime::from_register(bits >> 3),
down: RampTime::from_register(bits),
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PwmSamplerConfig {
pub one_bit_hysteresis: bool,
pub allow_below_code_6: bool,
}
impl PwmSamplerConfig {
pub const fn bits(self) -> u8 {
let mut bits = 0;
if self.one_bit_hysteresis {
bits |= 0x80;
}
if self.allow_below_code_6 {
bits |= 0x40;
}
bits
}
pub const fn from_bits(bits: u8) -> Self {
Self {
one_bit_hysteresis: bits & 0x80 != 0,
allow_below_code_6: bits & 0x40 != 0,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum FilterStrength {
#[default]
EverySample = 0,
Every2Samples = 1,
Every4Samples = 2,
Every8Samples = 3,
}
impl FilterStrength {
pub const fn bits(self) -> u8 {
self as u8
}
pub const fn from_bits(bits: u8) -> Self {
match bits & 0x03 {
0 => Self::EverySample,
1 => Self::Every2Samples,
2 => Self::Every4Samples,
_ => Self::Every8Samples,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct InterruptStatus {
pub over_current: bool,
pub over_voltage: bool,
pub thermal_shutdown: bool,
}
impl InterruptStatus {
pub const fn from_bits(bits: u8) -> Self {
Self {
over_current: bits & 0x04 != 0,
over_voltage: bits & 0x02 != 0,
thermal_shutdown: bits & 0x01 != 0,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct InterruptEnable {
pub global: bool,
pub over_current: bool,
pub over_voltage: bool,
pub thermal_shutdown: bool,
}
impl InterruptEnable {
pub const fn bits(self) -> u8 {
let mut bits = 0;
if self.global {
bits |= 0x80;
}
if self.over_current {
bits |= 0x04;
}
if self.over_voltage {
bits |= 0x02;
}
if self.thermal_shutdown {
bits |= 0x01;
}
bits
}
pub const fn from_bits(bits: u8) -> Self {
Self {
global: bits & 0x80 != 0,
over_current: bits & 0x04 != 0,
over_voltage: bits & 0x02 != 0,
thermal_shutdown: bits & 0x01 != 0,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct FaultStatus {
pub open: bool,
pub led2_short: bool,
pub led1_short: bool,
pub short_test_enabled: bool,
pub manufacturing_ovp_fault: bool,
pub manufacturing_ovp_enabled: bool,
}
impl FaultStatus {
pub const fn bits(self) -> u8 {
let mut bits = 0;
if self.open {
bits |= 0x20;
}
if self.led2_short {
bits |= 0x10;
}
if self.led1_short {
bits |= 0x08;
}
if self.short_test_enabled {
bits |= 0x04;
}
if self.manufacturing_ovp_fault {
bits |= 0x02;
}
if self.manufacturing_ovp_enabled {
bits |= 0x01;
}
bits
}
pub const fn from_bits(bits: u8) -> Self {
Self {
open: bits & 0x20 != 0,
led2_short: bits & 0x10 != 0,
led1_short: bits & 0x08 != 0,
short_test_enabled: bits & 0x04 != 0,
manufacturing_ovp_fault: bits & 0x02 != 0,
manufacturing_ovp_enabled: bits & 0x01 != 0,
}
}
}
pub struct Lm3630a<I2C> {
i2c: I2C,
address: SevenBitAddress,
}
impl<I2C> Lm3630a<I2C> {
pub fn new(i2c: I2C, address: Address) -> Self {
Self {
i2c,
address: address.as_u8(),
}
}
pub fn release(self) -> I2C {
self.i2c
}
pub const fn address(&self) -> SevenBitAddress {
self.address
}
pub fn wait_until_ready<D>(&mut self, delay: &mut D)
where
D: DelayNs,
{
delay.delay_ms(1);
}
#[cfg(feature = "async")]
pub async fn wait_until_ready_async<D>(&mut self, delay: &mut D)
where
D: AsyncDelayNs,
{
delay.delay_ms(1).await;
}
}
impl<I2C> Lm3630a<I2C>
where
I2C: I2c<SevenBitAddress>,
{
pub fn read_register(&mut self, register: Register) -> Result<u8, I2C::Error> {
let mut value = [0];
self.i2c
.write_read(self.address, &[register.address()], &mut value)?;
Ok(value[0])
}
pub fn write_register(&mut self, register: Register, value: u8) -> Result<(), I2C::Error> {
self.i2c.write(self.address, &[register.address(), value])
}
pub fn software_reset<D>(&mut self, delay: &mut D) -> Result<(), I2C::Error>
where
D: DelayNs,
{
self.write_register(Register::SoftwareReset, SOFTWARE_RESET_WRITE_MASK)?;
delay.delay_ms(1);
Ok(())
}
pub fn revision(&mut self) -> Result<u8, I2C::Error> {
self.read_register(Register::Revision)
}
pub fn set_control(&mut self, config: ControlConfig) -> Result<(), I2C::Error> {
self.write_register(Register::Control, config.bits() & CONTROL_WRITE_MASK)
}
pub fn read_control(&mut self) -> Result<ControlState, I2C::Error> {
self.read_register(Register::Control)
.map(ControlState::from_bits)
}
pub fn set_sleep(&mut self, sleep: bool) -> Result<(), I2C::Error> {
self.update_register(Register::Control, CONTROL_WRITE_MASK, |bits| {
if sleep { bits | 0x80 } else { bits & !0x80 }
})
}
pub fn set_bank_mapping(&mut self, bank: Bank, mapping: Mapping) -> Result<(), I2C::Error> {
let mask = match bank {
Bank::A => 0x10,
Bank::B => 0x08,
};
self.update_register(Register::Control, CONTROL_WRITE_MASK, |bits| {
if mapping.bit() {
bits | mask
} else {
bits & !mask
}
})
}
pub fn set_bank_enabled(&mut self, bank: Bank, enabled: bool) -> Result<(), I2C::Error> {
let mask = match bank {
Bank::A => 0x04,
Bank::B => 0x02,
};
self.update_register(Register::Control, CONTROL_WRITE_MASK, |bits| {
if enabled { bits | mask } else { bits & !mask }
})
}
pub fn set_led2_bank(&mut self, bank: Led2Bank) -> Result<(), I2C::Error> {
self.update_register(Register::Control, CONTROL_WRITE_MASK, |bits| {
if matches!(bank, Led2Bank::A) {
bits | 0x01
} else {
bits & !0x01
}
})
}
pub fn set_configuration(&mut self, config: Configuration) -> Result<(), I2C::Error> {
self.write_register(
Register::Configuration,
config.bits() & CONFIGURATION_WRITE_MASK,
)
}
pub fn read_configuration(&mut self) -> Result<Configuration, I2C::Error> {
self.read_register(Register::Configuration)
.map(Configuration::from_bits)
}
pub fn set_feedback_enabled(&mut self, bank: Bank, enabled: bool) -> Result<(), I2C::Error> {
let mask = match bank {
Bank::A => 0x08,
Bank::B => 0x10,
};
self.update_register(Register::Configuration, CONFIGURATION_WRITE_MASK, |bits| {
if enabled { bits | mask } else { bits & !mask }
})
}
pub fn set_pwm_enabled(&mut self, bank: Bank, enabled: bool) -> Result<(), I2C::Error> {
let mask = match bank {
Bank::A => 0x01,
Bank::B => 0x02,
};
self.update_register(Register::Configuration, CONFIGURATION_WRITE_MASK, |bits| {
if enabled { bits | mask } else { bits & !mask }
})
}
pub fn set_pwm_polarity(&mut self, polarity: PwmPolarity) -> Result<(), I2C::Error> {
self.update_register(Register::Configuration, CONFIGURATION_WRITE_MASK, |bits| {
if polarity.bit() {
bits | 0x04
} else {
bits & !0x04
}
})
}
pub fn set_boost_config(&mut self, config: BoostConfig) -> Result<(), I2C::Error> {
self.write_register(
Register::BoostControl,
config.bits() & BOOST_CONTROL_WRITE_MASK,
)
}
pub fn read_boost_config(&mut self) -> Result<BoostConfig, I2C::Error> {
self.read_register(Register::BoostControl)
.map(BoostConfig::from_bits)
}
pub fn set_brightness(&mut self, bank: Bank, brightness: Brightness) -> Result<(), I2C::Error> {
self.write_register(bank.brightness_register(), brightness.raw())
}
pub fn read_brightness(&mut self, bank: Bank) -> Result<Brightness, I2C::Error> {
self.read_register(bank.brightness_register())
.map(Brightness::from_raw)
}
pub fn set_full_scale_current(
&mut self,
bank: Bank,
current: FullScaleCurrent,
) -> Result<(), I2C::Error> {
match bank {
Bank::A => self.update_register(Register::CurrentA, CURRENT_A_WRITE_MASK, |bits| {
(bits & 0xc0) | current.code()
}),
Bank::B => {
self.write_register(Register::CurrentB, current.code() & CURRENT_B_WRITE_MASK)
}
}
}
pub fn read_full_scale_current(&mut self, bank: Bank) -> Result<FullScaleCurrent, I2C::Error> {
self.read_register(bank.current_register())
.map(FullScaleCurrent::from_register)
}
pub fn set_pwm_sampler_config(&mut self, config: PwmSamplerConfig) -> Result<(), I2C::Error> {
self.update_register(Register::CurrentA, CURRENT_A_WRITE_MASK, |bits| {
config.bits() | (bits & 0x1f)
})
}
pub fn read_pwm_sampler_config(&mut self) -> Result<PwmSamplerConfig, I2C::Error> {
self.read_register(Register::CurrentA)
.map(PwmSamplerConfig::from_bits)
}
pub fn set_on_off_ramp(&mut self, ramp: OnOffRamp) -> Result<(), I2C::Error> {
self.write_register(Register::OnOffRamp, ramp.bits() & RAMP_WRITE_MASK)
}
pub fn read_on_off_ramp(&mut self) -> Result<OnOffRamp, I2C::Error> {
self.read_register(Register::OnOffRamp)
.map(OnOffRamp::from_bits)
}
pub fn set_run_ramp(&mut self, ramp: RunRamp) -> Result<(), I2C::Error> {
self.write_register(Register::RunRamp, ramp.bits() & RAMP_WRITE_MASK)
}
pub fn read_run_ramp(&mut self) -> Result<RunRamp, I2C::Error> {
self.read_register(Register::RunRamp)
.map(RunRamp::from_bits)
}
pub fn read_interrupt_status(&mut self) -> Result<InterruptStatus, I2C::Error> {
self.read_register(Register::InterruptStatus)
.map(InterruptStatus::from_bits)
}
pub fn set_interrupt_enable(&mut self, enable: InterruptEnable) -> Result<(), I2C::Error> {
self.write_register(
Register::InterruptEnable,
enable.bits() & INTERRUPT_ENABLE_WRITE_MASK,
)
}
pub fn read_interrupt_enable(&mut self) -> Result<InterruptEnable, I2C::Error> {
self.read_register(Register::InterruptEnable)
.map(InterruptEnable::from_bits)
}
pub fn read_fault_status(&mut self) -> Result<FaultStatus, I2C::Error> {
self.read_register(Register::FaultStatus)
.map(FaultStatus::from_bits)
}
pub fn set_fault_status(&mut self, status: FaultStatus) -> Result<(), I2C::Error> {
self.write_register(
Register::FaultStatus,
status.bits() & FAULT_STATUS_WRITE_MASK,
)
}
pub fn read_pwm_output(&mut self) -> Result<u16, I2C::Error> {
let low = self.read_register(Register::PwmOutLow)? as u16;
let high = self.read_register(Register::PwmOutHigh)? as u16;
Ok(((high & 0x01) << 8) | low)
}
pub fn set_filter_strength(&mut self, strength: FilterStrength) -> Result<(), I2C::Error> {
self.write_register(
Register::FilterStrength,
strength.bits() & FILTER_STRENGTH_WRITE_MASK,
)
}
pub fn read_filter_strength(&mut self) -> Result<FilterStrength, I2C::Error> {
self.read_register(Register::FilterStrength)
.map(FilterStrength::from_bits)
}
fn update_register<F>(
&mut self,
register: Register,
writable_mask: u8,
update: F,
) -> Result<(), I2C::Error>
where
F: FnOnce(u8) -> u8,
{
let current = self.read_register(register)?;
self.write_register(register, update(current) & writable_mask)
}
}
#[cfg(feature = "async")]
impl<I2C> Lm3630a<I2C>
where
I2C: AsyncI2c<SevenBitAddress>,
{
pub async fn read_register_async(&mut self, register: Register) -> Result<u8, I2C::Error> {
let mut value = [0];
self.i2c
.write_read(self.address, &[register.address()], &mut value)
.await?;
Ok(value[0])
}
pub async fn write_register_async(
&mut self,
register: Register,
value: u8,
) -> Result<(), I2C::Error> {
self.i2c
.write(self.address, &[register.address(), value])
.await
}
pub async fn software_reset_async<D>(&mut self, delay: &mut D) -> Result<(), I2C::Error>
where
D: AsyncDelayNs,
{
self.write_register_async(Register::SoftwareReset, SOFTWARE_RESET_WRITE_MASK)
.await?;
delay.delay_ms(1).await;
Ok(())
}
pub async fn revision_async(&mut self) -> Result<u8, I2C::Error> {
self.read_register_async(Register::Revision).await
}
pub async fn set_control_async(&mut self, config: ControlConfig) -> Result<(), I2C::Error> {
self.write_register_async(Register::Control, config.bits() & CONTROL_WRITE_MASK)
.await
}
pub async fn read_control_async(&mut self) -> Result<ControlState, I2C::Error> {
self.read_register_async(Register::Control)
.await
.map(ControlState::from_bits)
}
pub async fn set_sleep_async(&mut self, sleep: bool) -> Result<(), I2C::Error> {
self.update_register_async(Register::Control, CONTROL_WRITE_MASK, |bits| {
if sleep { bits | 0x80 } else { bits & !0x80 }
})
.await
}
pub async fn set_bank_mapping_async(
&mut self,
bank: Bank,
mapping: Mapping,
) -> Result<(), I2C::Error> {
let mask = match bank {
Bank::A => 0x10,
Bank::B => 0x08,
};
self.update_register_async(Register::Control, CONTROL_WRITE_MASK, |bits| {
if mapping.bit() {
bits | mask
} else {
bits & !mask
}
})
.await
}
pub async fn set_bank_enabled_async(
&mut self,
bank: Bank,
enabled: bool,
) -> Result<(), I2C::Error> {
let mask = match bank {
Bank::A => 0x04,
Bank::B => 0x02,
};
self.update_register_async(Register::Control, CONTROL_WRITE_MASK, |bits| {
if enabled { bits | mask } else { bits & !mask }
})
.await
}
pub async fn set_led2_bank_async(&mut self, bank: Led2Bank) -> Result<(), I2C::Error> {
self.update_register_async(Register::Control, CONTROL_WRITE_MASK, |bits| {
if matches!(bank, Led2Bank::A) {
bits | 0x01
} else {
bits & !0x01
}
})
.await
}
pub async fn set_configuration_async(
&mut self,
config: Configuration,
) -> Result<(), I2C::Error> {
self.write_register_async(
Register::Configuration,
config.bits() & CONFIGURATION_WRITE_MASK,
)
.await
}
pub async fn read_configuration_async(&mut self) -> Result<Configuration, I2C::Error> {
self.read_register_async(Register::Configuration)
.await
.map(Configuration::from_bits)
}
pub async fn set_feedback_enabled_async(
&mut self,
bank: Bank,
enabled: bool,
) -> Result<(), I2C::Error> {
let mask = match bank {
Bank::A => 0x08,
Bank::B => 0x10,
};
self.update_register_async(Register::Configuration, CONFIGURATION_WRITE_MASK, |bits| {
if enabled { bits | mask } else { bits & !mask }
})
.await
}
pub async fn set_pwm_enabled_async(
&mut self,
bank: Bank,
enabled: bool,
) -> Result<(), I2C::Error> {
let mask = match bank {
Bank::A => 0x01,
Bank::B => 0x02,
};
self.update_register_async(Register::Configuration, CONFIGURATION_WRITE_MASK, |bits| {
if enabled { bits | mask } else { bits & !mask }
})
.await
}
pub async fn set_pwm_polarity_async(
&mut self,
polarity: PwmPolarity,
) -> Result<(), I2C::Error> {
self.update_register_async(Register::Configuration, CONFIGURATION_WRITE_MASK, |bits| {
if polarity.bit() {
bits | 0x04
} else {
bits & !0x04
}
})
.await
}
pub async fn set_boost_config_async(&mut self, config: BoostConfig) -> Result<(), I2C::Error> {
self.write_register_async(
Register::BoostControl,
config.bits() & BOOST_CONTROL_WRITE_MASK,
)
.await
}
pub async fn read_boost_config_async(&mut self) -> Result<BoostConfig, I2C::Error> {
self.read_register_async(Register::BoostControl)
.await
.map(BoostConfig::from_bits)
}
pub async fn set_brightness_async(
&mut self,
bank: Bank,
brightness: Brightness,
) -> Result<(), I2C::Error> {
self.write_register_async(bank.brightness_register(), brightness.raw())
.await
}
pub async fn read_brightness_async(&mut self, bank: Bank) -> Result<Brightness, I2C::Error> {
self.read_register_async(bank.brightness_register())
.await
.map(Brightness::from_raw)
}
pub async fn set_full_scale_current_async(
&mut self,
bank: Bank,
current: FullScaleCurrent,
) -> Result<(), I2C::Error> {
match bank {
Bank::A => {
self.update_register_async(Register::CurrentA, CURRENT_A_WRITE_MASK, |bits| {
(bits & 0xc0) | current.code()
})
.await
}
Bank::B => {
self.write_register_async(Register::CurrentB, current.code() & CURRENT_B_WRITE_MASK)
.await
}
}
}
pub async fn read_full_scale_current_async(
&mut self,
bank: Bank,
) -> Result<FullScaleCurrent, I2C::Error> {
self.read_register_async(bank.current_register())
.await
.map(FullScaleCurrent::from_register)
}
pub async fn set_pwm_sampler_config_async(
&mut self,
config: PwmSamplerConfig,
) -> Result<(), I2C::Error> {
self.update_register_async(Register::CurrentA, CURRENT_A_WRITE_MASK, |bits| {
config.bits() | (bits & 0x1f)
})
.await
}
pub async fn read_pwm_sampler_config_async(&mut self) -> Result<PwmSamplerConfig, I2C::Error> {
self.read_register_async(Register::CurrentA)
.await
.map(PwmSamplerConfig::from_bits)
}
pub async fn set_on_off_ramp_async(&mut self, ramp: OnOffRamp) -> Result<(), I2C::Error> {
self.write_register_async(Register::OnOffRamp, ramp.bits() & RAMP_WRITE_MASK)
.await
}
pub async fn read_on_off_ramp_async(&mut self) -> Result<OnOffRamp, I2C::Error> {
self.read_register_async(Register::OnOffRamp)
.await
.map(OnOffRamp::from_bits)
}
pub async fn set_run_ramp_async(&mut self, ramp: RunRamp) -> Result<(), I2C::Error> {
self.write_register_async(Register::RunRamp, ramp.bits() & RAMP_WRITE_MASK)
.await
}
pub async fn read_run_ramp_async(&mut self) -> Result<RunRamp, I2C::Error> {
self.read_register_async(Register::RunRamp)
.await
.map(RunRamp::from_bits)
}
pub async fn read_interrupt_status_async(&mut self) -> Result<InterruptStatus, I2C::Error> {
self.read_register_async(Register::InterruptStatus)
.await
.map(InterruptStatus::from_bits)
}
pub async fn set_interrupt_enable_async(
&mut self,
enable: InterruptEnable,
) -> Result<(), I2C::Error> {
self.write_register_async(
Register::InterruptEnable,
enable.bits() & INTERRUPT_ENABLE_WRITE_MASK,
)
.await
}
pub async fn read_interrupt_enable_async(&mut self) -> Result<InterruptEnable, I2C::Error> {
self.read_register_async(Register::InterruptEnable)
.await
.map(InterruptEnable::from_bits)
}
pub async fn read_fault_status_async(&mut self) -> Result<FaultStatus, I2C::Error> {
self.read_register_async(Register::FaultStatus)
.await
.map(FaultStatus::from_bits)
}
pub async fn set_fault_status_async(&mut self, status: FaultStatus) -> Result<(), I2C::Error> {
self.write_register_async(
Register::FaultStatus,
status.bits() & FAULT_STATUS_WRITE_MASK,
)
.await
}
pub async fn read_pwm_output_async(&mut self) -> Result<u16, I2C::Error> {
let low = self.read_register_async(Register::PwmOutLow).await? as u16;
let high = self.read_register_async(Register::PwmOutHigh).await? as u16;
Ok(((high & 0x01) << 8) | low)
}
pub async fn set_filter_strength_async(
&mut self,
strength: FilterStrength,
) -> Result<(), I2C::Error> {
self.write_register_async(
Register::FilterStrength,
strength.bits() & FILTER_STRENGTH_WRITE_MASK,
)
.await
}
pub async fn read_filter_strength_async(&mut self) -> Result<FilterStrength, I2C::Error> {
self.read_register_async(Register::FilterStrength)
.await
.map(FilterStrength::from_bits)
}
async fn update_register_async<F>(
&mut self,
register: Register,
writable_mask: u8,
update: F,
) -> Result<(), I2C::Error>
where
F: FnOnce(u8) -> u8,
{
let current = self.read_register_async(register).await?;
self.write_register_async(register, update(current) & writable_mask)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_hal::i2c::{ErrorKind, ErrorType, Operation};
use std::vec::Vec;
#[derive(Debug)]
struct MockError;
impl embedded_hal::i2c::Error for MockError {
fn kind(&self) -> ErrorKind {
ErrorKind::Other
}
}
struct MockI2c {
registers: [u8; 256],
writes: Vec<(SevenBitAddress, u8, u8)>,
}
impl Default for MockI2c {
fn default() -> Self {
Self {
registers: [0; 256],
writes: Vec::new(),
}
}
}
impl ErrorType for MockI2c {
type Error = MockError;
}
impl I2c<SevenBitAddress> for MockI2c {
fn read(&mut self, _address: SevenBitAddress, read: &mut [u8]) -> Result<(), Self::Error> {
read.fill(0);
Ok(())
}
fn write(&mut self, address: SevenBitAddress, write: &[u8]) -> Result<(), Self::Error> {
if write.len() == 2 {
let register = write[0];
let value = write[1];
self.registers[register as usize] = value;
self.writes.push((address, register, value));
}
Ok(())
}
fn write_read(
&mut self,
_address: SevenBitAddress,
write: &[u8],
read: &mut [u8],
) -> Result<(), Self::Error> {
if write.len() == 1 && read.len() == 1 {
read[0] = self.registers[write[0] as usize];
}
Ok(())
}
fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
let mut selected = 0;
for operation in operations {
match operation {
Operation::Write(bytes) => {
if bytes.len() == 1 {
selected = bytes[0];
} else {
self.write(address, bytes)?;
}
}
Operation::Read(bytes) => {
if bytes.len() == 1 {
bytes[0] = self.registers[selected as usize];
} else {
bytes.fill(0);
}
}
}
}
Ok(())
}
}
#[derive(Default)]
struct MockDelay {
milliseconds: u32,
}
impl DelayNs for MockDelay {
fn delay_ns(&mut self, ns: u32) {
self.milliseconds += ns / 1_000_000;
}
fn delay_ms(&mut self, ms: u32) {
self.milliseconds += ms;
}
}
#[test]
fn addresses_match_sel_pin() {
assert_eq!(Address::SelLow.as_u8(), 0x36);
assert_eq!(Address::SelHigh.as_u8(), 0x38);
}
#[test]
fn datasheet_dual_string_pwm_example_encodes() {
let control = ControlConfig {
sleep: false,
bank_a_mapping: Mapping::Linear,
bank_b_mapping: Mapping::Linear,
bank_a_enabled: true,
bank_b_enabled: true,
led2_bank: Led2Bank::B,
};
let configuration = Configuration {
feedback_bank_a: true,
feedback_bank_b: true,
pwm_polarity: PwmPolarity::ActiveHigh,
pwm_bank_a: true,
pwm_bank_b: true,
};
assert_eq!(control.bits(), 0x1e);
assert_eq!(configuration.bits(), 0x1b);
}
#[test]
fn writes_brightness_to_selected_bank() {
let i2c = MockI2c::default();
let mut driver = Lm3630a::new(i2c, Address::SelLow);
driver
.set_brightness(Bank::B, Brightness::from_raw(0x80))
.unwrap();
let i2c = driver.release();
assert_eq!(i2c.writes, [(0x36, Register::BrightnessB.address(), 0x80)]);
}
#[test]
fn current_a_write_preserves_pwm_sampler_bits_and_clears_reserved_bit() {
let mut i2c = MockI2c::default();
i2c.registers[Register::CurrentA.address() as usize] = 0xe0;
let mut driver = Lm3630a::new(i2c, Address::SelLow);
driver
.set_full_scale_current(Bank::A, FullScaleCurrent::from_code(0x14).unwrap())
.unwrap();
let i2c = driver.release();
assert_eq!(i2c.writes, [(0x36, Register::CurrentA.address(), 0xd4)]);
}
#[test]
fn software_reset_waits_twait() {
let i2c = MockI2c::default();
let mut delay = MockDelay::default();
let mut driver = Lm3630a::new(i2c, Address::SelLow);
driver.software_reset(&mut delay).unwrap();
let i2c = driver.release();
assert_eq!(delay.milliseconds, 1);
assert_eq!(
i2c.writes,
[(0x36, Register::SoftwareReset.address(), 0x01)]
);
}
}