#![no_std]
#![warn(missing_docs)]
use embedded_hal_async::delay::DelayNs;
use embedded_hal_async::i2c::I2c;
use modular_bitfield::{bitfield, specifiers::B1, BitfieldSpecifier};
const ADDR: u8 = 0x2c;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
struct Reg(pub u8);
#[allow(dead_code)]
impl Reg {
pub const DEVICE_ID: Reg = Reg(0x00);
pub const CONTROL_1: Reg = Reg(0x01);
pub const INTERRUPT: Reg = Reg(0x02);
pub const CONTROL_2: Reg = Reg(0x03);
pub const CONTROL_3: Reg = Reg(0x04);
pub const fn new(val: u8) -> Self {
Reg(val)
}
pub const fn to_u8(self) -> u8 {
self.0
}
}
pub struct ChargerDetector<D> {
i2c_dev: D,
}
impl<D: I2c> ChargerDetector<D> {
pub fn new(i2c_dev: D) -> Self {
ChargerDetector { i2c_dev }
}
pub async fn device_id(&mut self) -> Result<u8, D::Error> {
self.read_reg(Reg::DEVICE_ID).await
}
pub async fn enable_irq(&mut self, enable: bool) -> Result<(), D::Error> {
const INTEN: u8 = 0x40;
self.modify_reg(Reg::CONTROL_1, |x| {
x & !INTEN | if enable { INTEN } else { 0 }
})
.await
}
pub async fn irq_status(&mut self) -> Result<InterruptStatus, D::Error> {
self.read_reg(Reg::INTERRUPT)
.await
.map(|x| InterruptStatus::from_bytes([x]))
}
pub async fn set_usb_switch<T: DelayNs>(
&mut self,
closed: bool,
mut delay: T,
) -> Result<(), D::Error> {
const CP_ENA: u8 = 0x10;
const USBSWC: u8 = 0x20;
if closed {
self.modify_reg(Reg::CONTROL_1, |x| x | CP_ENA).await?;
delay.delay_ms(1).await;
self.modify_reg(Reg::CONTROL_1, |x| x | USBSWC).await
} else {
self.modify_reg(Reg::CONTROL_1, |x| x & !USBSWC).await?;
delay.delay_ms(1).await;
self.modify_reg(Reg::CONTROL_1, |x| x & !CP_ENA).await
}
}
async fn read_reg(&mut self, reg: Reg) -> Result<u8, D::Error> {
let mut val = 0u8;
self.i2c_dev
.write_read(
ADDR,
core::slice::from_ref(®.to_u8()),
core::slice::from_mut(&mut val),
)
.await?;
Ok(val)
}
async fn write_reg(&mut self, reg: Reg, val: u8) -> Result<(), D::Error> {
let buf = [reg.to_u8(), val];
self.i2c_dev.write(ADDR, &buf).await
}
async fn modify_reg<F: FnOnce(u8) -> u8>(&mut self, reg: Reg, func: F) -> Result<(), D::Error> {
let val = self.read_reg(reg).await?;
let val = func(val);
self.write_reg(reg, val).await
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, BitfieldSpecifier)]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
#[bits = 3]
pub enum ChargerType {
#[default]
None,
Usb,
ChargingDownstreamPort,
DedicatedChargingPort,
Special500mA,
Special1000mA,
}
impl ChargerType {
pub fn is_usb(self) -> bool {
matches!(self, ChargerType::Usb | ChargerType::ChargingDownstreamPort)
}
pub fn can_charge(self) -> bool {
!matches!(self, ChargerType::None)
}
pub fn current_limit(self) -> u16 {
match self {
ChargerType::None => 0,
ChargerType::Usb => 100, ChargerType::ChargingDownstreamPort => 1500, ChargerType::DedicatedChargingPort => 1500, ChargerType::Special500mA => 500,
ChargerType::Special1000mA => 1000,
}
}
}
#[bitfield(bits = 8)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, BitfieldSpecifier)]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
pub struct InterruptStatus {
#[skip]
__: B1,
pub is_charger_detection_running: bool,
pub is_data_contact_detection_running: bool,
#[skip]
__: B1,
pub is_vbus_present: bool,
#[bits = 3]
pub charger_type: ChargerType,
}