Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

trace MAX5970 status/faults/max current/max voltage on V12 rails #1560

Merged
merged 5 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 133 additions & 82 deletions drv/i2c-devices/src/max5970.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,58 @@ pub enum Register {
cubf_ba_chx_i = 0x47,
}

/// A newtype for the MON input range setting register
struct MonRange(u8);
bcantrill marked this conversation as resolved.
Show resolved Hide resolved

impl MonRange {
fn full_scale_voltage(&self, rail: u8) -> u8 {
let range = if rail == 0 {
self.0 & 0b11
} else {
(self.0 >> 2) & 0b11
};

match range {
0b00 => 16,
0b01 => 8,
0b10 => 4,
0b11 => 2,
_ => unreachable!(),
}
}
}

/// A newtype for the fast-trip threshold maximum range register
struct Status2(u8);

impl Status2 {
fn max_current_sense_range(&self, rail: u8) -> Option<u8> {
//
// The datasheet is enragingly inconsistent about how it refers to the
// channels. For most registers that have different settings for
// channels, it refers to them as Channel 1 and Channel 2 -- except
// for status2, which refers to Channel 0 and Channel 1.
//
let range = if rail == 0 {
self.0 & 0b11
} else {
(self.0 >> 2) & 0b11
};

//
// Our maximum current-sense range is 25mV, 50mV, or 100mV. (Contrary
// to the implication of the datasheet, there is no fourth maximum
// current-sense range.)
//
match range {
0b00 => Some(100),
0b01 => Some(50),
0b10 => Some(25),
_ => None,
}
}
}

pub struct Max5970 {
device: I2cDevice,
rail: u8,
Expand All @@ -240,6 +292,72 @@ impl Max5970 {
pub fn i2c_device(&self) -> &I2cDevice {
&self.device
}

fn convert_volts(&self, mon_range: MonRange, msb: u8, lsb: u8) -> Volts {
//
// The 10-bit value from the ADC is a fraction of the full-scale
// voltage setting.
//
let divisor = 1024.0 / mon_range.full_scale_voltage(self.rail) as f32;

Volts(((((msb as u16) << 2) | (lsb as u16)) as f32) / divisor)
}

fn convert_current(
&self,
status2: Status2,
msb: u8,
lsb: u8,
) -> Result<Amperes, ResponseCode> {
let millivolts = status2
.max_current_sense_range(self.rail)
.ok_or(ResponseCode::BadDeviceState)?;

//
// The 10-bit value from the ADC is a fraction of the maximum
// current-sense range.
//
let divisor = 1024.0 / millivolts as f32;
let delta = ((((msb as u16) << 2) | (lsb as u16)) as f32) / divisor;

//
// We have the voltage drop across the current sense resistor; to
// determine current, we divide voltage by resistance (I = V / R).
//
Ok(Amperes(delta / self.rsense as f32))
}

pub fn max_vout(&self) -> Result<Volts, ResponseCode> {
let (msb_reg, lsb_reg) = if self.rail == 0 {
(Register::max_chx_mon_msb_ch1, Register::max_chx_mon_lsb_ch1)
} else {
(Register::max_chx_mon_msb_ch2, Register::max_chx_mon_lsb_ch2)
};

Ok(self.convert_volts(
MonRange(self.read_reg(Register::mon_range)?),
self.read_reg(msb_reg)?,
self.read_reg(lsb_reg)?,
))
}

pub fn max_iout(&self) -> Result<Amperes, ResponseCode> {
let (msb_reg, lsb_reg) = if self.rail == 0 {
(Register::max_chx_cs_msb_ch1, Register::max_chx_cs_lsb_ch1)
} else {
(Register::max_chx_cs_msb_ch2, Register::max_chx_cs_lsb_ch2)
};

self.convert_current(
Status2(self.read_reg(Register::status2)?),
self.read_reg(msb_reg)?,
self.read_reg(lsb_reg)?,
)
}

pub fn status0(&self) -> Result<u8, ResponseCode> {
self.read_reg(Register::status0)
}
}

impl Validate<ResponseCode> for Max5970 {
Expand All @@ -252,99 +370,32 @@ impl Validate<ResponseCode> for Max5970 {

impl VoltageSensor<ResponseCode> for Max5970 {
fn read_vout(&self) -> Result<Volts, ResponseCode> {
let (msb, lsb) = if self.rail == 0 {
(
self.read_reg(Register::adc_chx_mon_msb_ch1)?,
self.read_reg(Register::adc_chx_mon_lsb_ch1)?,
)
} else {
(
self.read_reg(Register::adc_chx_mon_msb_ch2)?,
self.read_reg(Register::adc_chx_mon_lsb_ch2)?,
)
};

let mon_range = self.read_reg(Register::mon_range)?;

let range = if self.rail == 0 {
mon_range & 0b11
let (msb_reg, lsb_reg) = if self.rail == 0 {
(Register::adc_chx_mon_msb_ch1, Register::adc_chx_mon_lsb_ch1)
} else {
(mon_range >> 2) & 0b11
};

let volts = match range {
0b00 => 16,
0b01 => 8,
0b10 => 4,
0b11 => 2,
_ => unreachable!(),
(Register::adc_chx_mon_msb_ch2, Register::adc_chx_mon_lsb_ch2)
};

//
// The 10-bit value from the ADC is a fraction of the full-scale
// voltage setting.
//
let divisor = 1024.0 / volts as f32;

Ok(Volts(
((((msb as u16) << 2) | (lsb as u16)) as f32) / divisor,
Ok(self.convert_volts(
MonRange(self.read_reg(Register::mon_range)?),
self.read_reg(msb_reg)?,
self.read_reg(lsb_reg)?,
))
}
}

impl CurrentSensor<ResponseCode> for Max5970 {
fn read_iout(&self) -> Result<Amperes, ResponseCode> {
let (msb, lsb) = if self.rail == 0 {
(
self.read_reg(Register::adc_chx_cs_msb_ch1)?,
self.read_reg(Register::adc_chx_cs_lsb_ch1)?,
)
} else {
(
self.read_reg(Register::adc_chx_cs_msb_ch2)?,
self.read_reg(Register::adc_chx_cs_lsb_ch2)?,
)
};

let status2 = self.read_reg(Register::status2)?;

//
// The datasheet is enragingly inconsistent about how it refers to the
// channels. For most registers that have different settings for
// channels, it refers to them as Channel 1 and Channel 2 -- except
// for status2, which refers to Channel 0 and Channel 1.
//
let range = if self.rail == 0 {
status2 & 0b11
let (msb_reg, lsb_reg) = if self.rail == 0 {
(Register::adc_chx_cs_msb_ch1, Register::adc_chx_cs_lsb_ch1)
} else {
(status2 >> 2) & 0b11
};

//
// Our maximum current-sense range is 25mV, 50mV, or 100mV. (Contrary
// to the implication of the datasheet, there is no fourth maximum
// current-sense range.)
//
let millivolts = match range {
0b00 => 100,
0b01 => 50,
0b10 => 25,
_ => {
return Err(ResponseCode::BadDeviceState);
}
(Register::adc_chx_cs_msb_ch2, Register::adc_chx_cs_lsb_ch2)
};

//
// The 10-bit value from the ADC is a fraction of the maximum
// current-sense range.
//
let divisor = 1024.0 / millivolts as f32;
let delta = ((((msb as u16) << 2) | (lsb as u16)) as f32) / divisor;

//
// We have the voltage drop across the current sense resistor; to
// determine current, we divide voltage by resistance (I = V / R).
//
Ok(Amperes(delta / self.rsense as f32))
self.convert_current(
Status2(self.read_reg(Register::status2)?),
self.read_reg(msb_reg)?,
self.read_reg(lsb_reg)?,
)
}
}
72 changes: 72 additions & 0 deletions task/power/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ use drv_i2c_devices::{
enum Trace {
GotVersion(u32),
GotAddr(u32),
Max5970 {
sensor: SensorId,
status0: u8,
status1: u8,
status3: u8,
fault0: u8,
fault1: u8,
fault2: u8,
max_current: f32,
max_voltage: f32,
},
None,
}

Expand Down Expand Up @@ -349,6 +360,54 @@ macro_rules! max5970_controller {
};
}

fn trace_max5970(dev: &Max5970, sensor: SensorId) {
if let Ok(Volts(volts)) = dev.max_vout() {
use drv_i2c_devices::max5970::Register;

//
// We want to *not* trace the 3.3V rails on the MAX5970 on the
// Sharkfin (U8), so anything that has either never powered on or
// never seen a voltage greater than ~4V we will not record.
//
if volts < 4.0 {
return;
}

ringbuf_entry!(Trace::Max5970 {
bcantrill marked this conversation as resolved.
Show resolved Hide resolved
sensor,
status0: match dev.read_reg(Register::status0) {
Ok(reg) => reg,
_ => return,
},
status1: match dev.read_reg(Register::status1) {
Ok(reg) => reg,
_ => return,
},
status3: match dev.read_reg(Register::status3) {
Ok(reg) => reg,
_ => return,
},
fault0: match dev.read_reg(Register::fault0) {
Ok(reg) => reg,
_ => return,
},
fault1: match dev.read_reg(Register::fault1) {
Ok(reg) => reg,
_ => return,
},
fault2: match dev.read_reg(Register::fault2) {
Ok(reg) => reg,
_ => return,
},
max_current: match dev.max_iout() {
Ok(Amperes(amps)) => amps,
_ => return,
},
max_voltage: volts,
});
}
}

#[allow(unused_macros)]
macro_rules! mwocp68_controller {
($which:ident, $rail:ident, $state:ident) => {
Expand Down Expand Up @@ -412,6 +471,7 @@ fn main() -> ! {
i2c_task,
sensor: sensor_api::Sensor::from(SENSOR.get_task_id()),
devices: claim_devices(i2c_task),
fired: 0,
};
let mut buffer = [0; idl::INCOMING_SIZE];

Expand All @@ -428,6 +488,7 @@ struct ServerImpl {
i2c_task: TaskId,
sensor: sensor_api::Sensor,
devices: &'static mut [Device; bsp::CONTROLLER_CONFIG_LEN],
fired: u32,
}

impl ServerImpl {
Expand Down Expand Up @@ -500,7 +561,18 @@ impl ServerImpl {
}
}
}

//
// Every 10 seconds, gather max5970 data
//
if self.fired % 10 == 0 {
if let Device::Max5970(dev) = dev {
trace_max5970(dev, c.current);
}
}
}

self.fired += 1;
}

/// Find the BMR491 and return an `I2cDevice` handle
Expand Down