#![cfg_attr(not(test), no_std)]
#![deny(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
unstable_features,
unused_import_braces,
unused_qualifications,
warnings
)]
#![allow(dead_code)]
mod config;
mod error;
mod i2c_interface;
mod register;
pub use config::*;
use embedded_hal::blocking::i2c::{Read, Write, WriteRead};
use error::Error;
use register::*;
#[derive(Debug, Clone, Copy)]
pub struct MAX17320<I2C: Write + WriteRead> {
com: I2C,
address: u8,
address_nvm: u8,
r_sense: f32,
}
impl<I2C, E> MAX17320<I2C>
where
I2C: WriteRead<Error = E> + Write<Error = E> + Read<Error = E>,
{
pub fn new(i2c: I2C, r_sense_mohm: f32) -> Result<Self, Error<E>> {
MAX17320::with_addresses(i2c, 0x36, 0x0B, r_sense_mohm)
}
pub fn with_addresses(
i2c: I2C,
address: u8,
address_nvm: u8,
r_sense_mohm: f32,
) -> Result<Self, Error<E>> {
let chip = Self {
com: i2c,
address,
address_nvm,
r_sense: r_sense_mohm,
};
Ok(chip)
}
pub fn read_device_name(&mut self) -> Result<u16, Error<E>> {
let name = self.read_named_register(Register::DevName)?;
Ok(name)
}
pub fn read_status(&mut self) -> Result<u16, Error<E>> {
let val = self.read_named_register(Register::Status)?;
Ok(val)
}
pub fn read_capacity(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::RepCap)?;
Ok(convert_to_capacity(raw, self.r_sense))
}
pub fn read_state_of_charge(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::RepSoc)?;
Ok(convert_to_percentage(raw))
}
pub fn read_vcell(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::VCell)?;
Ok(convert_to_voltage(raw))
}
pub fn read_temperature(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::Temp)? as i16;
Ok(convert_to_temperature(raw))
}
pub fn read_die_temperature(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::DieTemp)? as i16;
Ok(convert_to_temperature(raw))
}
pub fn read_current(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::Current)? as i16;
Ok(convert_to_current(raw, self.r_sense))
}
pub fn read_time_to_empty(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::TimeToEmpty)?;
Ok(convert_to_time(raw))
}
pub fn read_time_to_full(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::TimeToFull)?;
Ok(convert_to_time(raw))
}
pub fn read_protection_status(&mut self) -> Result<u16, Error<E>> {
let val = self.read_named_register(Register::ProtStatus)?;
Ok(val)
}
pub fn read_protection_alert(&mut self) -> Result<u16, Error<E>> {
let val = self.read_named_register(Register::ProtAlrt)?;
Ok(val)
}
pub fn clear_protection_alert(&mut self) -> Result<(), Error<E>> {
self.write_named_register(Register::ProtAlrt, 0x0000)?;
Ok(())
}
pub fn read_cell1(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::Cell1)?;
Ok(convert_to_voltage(raw))
}
pub fn read_cell2(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::Cell2)?;
Ok(convert_to_voltage(raw))
}
pub fn read_cell3(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::Cell3)?;
Ok(convert_to_voltage(raw))
}
pub fn read_cell4(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::Cell4)?;
Ok(convert_to_voltage(raw))
}
pub fn read_batt(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::Batt)?;
Ok(convert_to_voltage(raw))
}
pub fn read_pckp(&mut self) -> Result<f32, Error<E>> {
let raw = self.read_named_register(Register::Pckp)?;
Ok(convert_to_voltage(raw))
}
pub fn read_battery_status(&mut self) -> Result<u16, Error<E>> {
let val = self.read_named_register_nvm(RegisterNvm::NBattStatus)?;
Ok(val)
}
fn unlock_write_protection(&mut self) -> Result<(), Error<E>> {
self.write_named_register(Register::CommStat, 0x0000)?;
self.write_named_register(Register::CommStat, 0x0000)?;
Ok(())
}
fn lock_write_protection(&mut self) -> Result<(), Error<E>> {
self.write_named_register(Register::CommStat, 0x00F9)?;
self.write_named_register(Register::CommStat, 0x00F9)?;
Ok(())
}
pub fn read_pack_config(&mut self) -> Result<u16, Error<E>> {
let val = self.read_named_register_nvm(RegisterNvm::NPackCfg)?;
Ok(val)
}
pub fn set_pack_config(
&mut self,
n_cells: u8,
n_therms: u8,
therm_type: ThermistorType,
charge_pump_voltage_config: ChargePumpVoltageConfiguration,
always_on_regulator_config: AlwaysOnRegulatorConfiguration,
battery_pack_update: BatteryPackUpdate,
) -> Result<(), Error<E>> {
if n_cells < 2 || n_cells > 4 {
return Err(Error::InvalidConfigurationValue(n_cells as u16));
}
let n_cells = n_cells - 2;
if n_therms > 4 {
return Err(Error::InvalidConfigurationValue(n_therms as u16));
}
let n_therms = n_therms << 2;
let code = n_cells as u16
| n_therms as u16
| therm_type as u16
| charge_pump_voltage_config as u16
| always_on_regulator_config as u16
| battery_pack_update as u16;
self.unlock_write_protection()?;
self.write_named_register_nvm(RegisterNvm::NPackCfg, code)?;
self.lock_write_protection()?;
Ok(())
}
pub fn set_alert_output_enable(&mut self, enable: bool) -> Result<(), Error<E>> {
let current_config = self.read_named_register(Register::Config)?;
let new_config: u16;
if enable {
new_config = set_bit(current_config, 2);
self.set_alert_shutdown_enable(false)?;
} else {
new_config = clear_bit(current_config, 2);
}
self.write_named_register(Register::Config, new_config)?;
Ok(())
}
pub fn set_alert_shutdown_enable(&mut self, enable: bool) -> Result<(), Error<E>> {
let current_nconfig = self.read_named_register_nvm(RegisterNvm::NConfig)?;
let new_nconfig = if enable {
set_bit(current_nconfig, 5)
} else {
clear_bit(current_nconfig, 5)
};
self.write_named_register_nvm(RegisterNvm::NConfig, new_nconfig)
}
pub fn set_voltage_alert_threshold(&mut self, min_v: f32, max_v: f32) -> Result<(), Error<E>> {
if !is_valid_voltage_threshold(max_v) {
return Err(Error::InvalidConfigurationValue(max_v as u16));
}
if !is_valid_voltage_threshold(min_v) {
return Err(Error::InvalidConfigurationValue(min_v as u16));
}
let threshold_array = [
(max_v / VALRTTH_LSB_RESOLUTION) as u8,
(min_v / VALRTTH_LSB_RESOLUTION) as u8,
];
let threshold_code = u16::from_be_bytes(threshold_array);
self.write_named_register(Register::VAlrtTh, threshold_code)?;
Ok(())
}
pub fn read_volatage_alert_threshold(&mut self) -> Result<(f32, f32), Error<E>> {
let code = self.read_named_register(Register::VAlrtTh)?;
let raw = code.to_be_bytes();
Ok((
raw[0] as f32 * VALRTTH_LSB_RESOLUTION, raw[1] as f32 * VALRTTH_LSB_RESOLUTION, ))
}
pub fn set_temperature_alert_threshold(
&mut self,
min_t: i8,
max_t: i8,
) -> Result<(), Error<E>> {
let threshold_array = [max_t as u8, min_t as u8];
let threshold_code = u16::from_be_bytes(threshold_array);
self.write_named_register(Register::TAlrtTh, threshold_code)?;
Ok(())
}
pub fn read_temperature_alert_threshold(&mut self) -> Result<(i8, i8), Error<E>> {
let code = self.read_named_register(Register::TAlrtTh)?;
let raw = code.to_be_bytes();
Ok((
raw[0] as i8, raw[1] as i8, ))
}
pub fn set_state_of_charge_alert_threshold(
&mut self,
min_soc: u8,
max_soc: u8,
) -> Result<(), Error<E>> {
let threshold_array = [max_soc, min_soc];
let threshold_code = u16::from_be_bytes(threshold_array);
self.write_named_register(Register::SAlrtTh, threshold_code)?;
Ok(())
}
pub fn read_state_of_charge_alert_threshold(&mut self) -> Result<(u8, u8), Error<E>> {
let code = self.read_named_register(Register::SAlrtTh)?;
let raw = code.to_be_bytes();
Ok((
raw[0] as u8, raw[1] as u8, ))
}
pub fn set_current_alert_threshold(&mut self, min_i: i8, max_i: i8) -> Result<(), Error<E>> {
let threshold_array = [max_i as u8, min_i as u8];
let threshold_code = u16::from_be_bytes(threshold_array);
self.write_named_register(Register::IAlrtTh, threshold_code)?;
Ok(())
}
pub fn read_current_alert_threshold(&mut self) -> Result<(i8, i8), Error<E>> {
let code = self.read_named_register(Register::IAlrtTh)?;
let raw = code.to_be_bytes();
Ok((
raw[0] as i8, raw[1] as i8, ))
}
}
const VALRTTH_LSB_RESOLUTION: f32 = 0.02;
fn is_valid_voltage_threshold(raw: f32) -> bool {
raw % VALRTTH_LSB_RESOLUTION < 0.0001 && raw >= 0.0 && raw <= (255.0 * VALRTTH_LSB_RESOLUTION)
}
fn convert_to_time(raw: u16) -> f32 {
raw as f32 * 5.625
}
fn convert_to_voltage(raw: u16) -> f32 {
raw as f32 * 0.078125 / 1000.0
}
fn convert_to_percentage(raw: u16) -> f32 {
raw as f32 / 256.0
}
fn convert_to_temperature(raw: i16) -> f32 {
raw as f32 / 256.0
}
fn convert_to_capacity(raw: u16, r_sense: f32) -> f32 {
raw as f32 * 5.0 / r_sense
}
fn convert_to_current(raw: i16, r_sense: f32) -> f32 {
raw as f32 * 1.5625 / (r_sense / 1000.0)
}
#[cfg(test)]
mod tests {
use super::*;
use std::println;
#[test]
fn max_temp_conversion() {
let max_temp_raw: u16 = 0b01111111_11111111;
let temp = convert_to_temperature(max_temp_raw as i16);
println!("temp {}", temp);
assert_eq!(temp, 127.99609)
}
#[test]
fn min_temp_conversion() {
let min_temp_raw: u16 = 0b10000000_00000000;
let temp = convert_to_temperature(min_temp_raw as i16);
println!("temp {}", temp);
assert_eq!(temp, -128.0)
}
#[test]
fn valid_voltage_threshold() {
assert!(is_valid_voltage_threshold(5.1))
}
}