changes to support efuse config

This commit is contained in:
Ashcon Mohseninia 2022-11-30 20:26:48 +00:00
parent e82bdcca60
commit 30cb39fde5
5 changed files with 419 additions and 124 deletions

View File

@ -24,4 +24,5 @@ modular-bitfield = "0.11.2"
static_assertions = "1.1.0"
env_logger="0.9.0"
egui-toast="0.3.0"
chrono = "0.4.23"
nom="7.1.1"

View File

@ -34,6 +34,8 @@ fn main() {
height: icon_h,
});
native_options.initial_window_size = Some(Vec2::new(1280.0, 720.0));
//native_options.initial_window_size = Some(Vec2::new(1024.0, 768.0));
//native_options.fullscreen = true;
#[cfg(windows)]
{
native_options.renderer = Renderer::Wgpu;

View File

@ -1,3 +1,5 @@
use std::fmt::Display;
use modular_bitfield::{bitfield, BitfieldSpecifier};
#[bitfield]
@ -13,6 +15,61 @@ pub struct TcmCoreConfig {
pub red_line_dieselrpm: u16,
pub red_line_petrolrpm: u16,
pub engine_type: EngineType,
pub egs_can_type: EgsCanType,
// Only for V1,2 and newer PCBs
pub shifter_style: ShifterStyle,
// Only for V1.3 and newer PCBs
pub io_0_usage: IOPinConfig,
pub input_sensor_pulses_per_rev: u8,
pub output_pulse_width_per_kmh: u8,
pub mosfet_purpose: MosfetPurpose
}
#[derive(BitfieldSpecifier)]
#[bits = 8]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum EgsCanType {
UNKNOWN = 0,
EGS51 = 1,
EGS52 = 2,
EGS53 = 3,
}
#[derive(BitfieldSpecifier)]
#[bits = 8]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ShifterStyle {
EWM_CAN = 0,
TRRS = 1,
SLR_MCLAREN = 2, // NEEDS TESTING (Need to work out how this works)
}
#[derive(BitfieldSpecifier)]
#[bits = 8]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum IOPinConfig {
NotConnected = 0,
Input = 1,
Output = 2
}
#[derive(BitfieldSpecifier)]
#[bits = 8]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum MosfetPurpose {
NotConnected = 0,
TorqueCutTrigger = 1,
B3BrakeSolenoid = 2
}
#[bitfield]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct TcmEfuseConfig {
pub board_ver: BoardType,
pub manf_day: u8,
pub manf_week: u8,
pub manf_month: u8,
pub manf_year: u8
}
#[derive(BitfieldSpecifier)]
@ -33,3 +90,30 @@ pub enum EngineType {
Diesel,
Petrol,
}
#[derive(BitfieldSpecifier)]
#[bits = 8]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum BoardType {
Unknown = 0,
V11 = 1,
V12 = 2,
V13 = 3
}
impl Display for BoardType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BoardType::Unknown => write!(f, "Unknown"),
BoardType::V11 => write!(f, "V1.1 (12/12/21)"),
BoardType::V12 => write!(f, "V1.2 (07/07/22)"),
BoardType::V13 => write!(f, "V1.3 (UNDER DEVELOPMENT DO NOT SELECT)"),
}
}
}
impl Into<String> for BoardType {
fn into(self) -> String {
format!("{}", self)
}
}

View File

@ -4,14 +4,17 @@ use std::{
};
use crate::window::PageAction;
use chrono::{Datelike, Weekday};
use ecu_diagnostics::{
kwp2000::{Kwp2000DiagnosticServer, ResetMode, SessionType},
DiagnosticServer,
};
use eframe::egui::Ui;
use eframe::egui::{self, *};
use egui_extras::RetainedImage;
use image::{ImageFormat, DynamicImage};
use self::cfg_structs::{DefaultProfile, EngineType, TcmCoreConfig};
use self::cfg_structs::{DefaultProfile, EngineType, TcmCoreConfig, TcmEfuseConfig, BoardType, EgsCanType, ShifterStyle, IOPinConfig, MosfetPurpose};
use super::{status_bar::MainStatusBar, StatusText};
@ -22,15 +25,41 @@ pub struct ConfigPage {
bar: MainStatusBar,
status: StatusText,
scn: Option<TcmCoreConfig>,
efuse: Option<TcmEfuseConfig>,
show_efuse: bool,
show_final_warning: bool,
pcb_11_img: RetainedImage,
pcb_12_img: RetainedImage,
pcb_13_img: RetainedImage
}
fn load_image(image: DynamicImage, name: &str) -> RetainedImage {
let size = [image.width() as usize, image.height() as usize];
let buffer = image.to_rgba8();
let pixels = buffer.as_flat_samples();
RetainedImage::from_color_image(name, ColorImage::from_rgba_unmultiplied(size, pixels.as_slice()))
}
impl ConfigPage {
pub fn new(server: Arc<Mutex<Kwp2000DiagnosticServer>>, bar: MainStatusBar) -> Self {
let red_img = image::load_from_memory_with_format(include_bytes!("../../../res/pcb_11.jpg"), ImageFormat::Jpeg).unwrap();
let blk_img = image::load_from_memory_with_format(include_bytes!("../../../res/pcb_12.jpg"), ImageFormat::Jpeg).unwrap();
let bet_img = image::load_from_memory_with_format(include_bytes!("../../../res/pcb_13.jpg"), ImageFormat::Jpeg).unwrap();
let pcb_11_img = load_image(red_img, "V11-PCB");
let pcb_12_img = load_image(blk_img, "V12-PCB");
let pcb_13_img = load_image(bet_img, "V13-PCB");
Self {
server,
bar,
status: StatusText::Ok("".into()),
scn: None,
efuse: None,
show_efuse: false,
show_final_warning: false,
pcb_11_img,
pcb_12_img,
pcb_13_img
}
}
}
@ -51,11 +80,30 @@ impl crate::window::InterfacePage for ConfigPage {
self.status = StatusText::Ok(format!("Read OK!"));
}
Err(e) => {
self.status = StatusText::Err(format!("Error reading configuration: {}", e))
self.status = StatusText::Err(format!("Error reading TCM configuration: {}", e))
}
}
match self
.server
.lock()
.unwrap()
.read_custom_local_identifier(0xFD)
{
Ok(res) => {
let tmp = TcmEfuseConfig::from_bytes(res.try_into().unwrap());
if tmp.board_ver() == BoardType::Unknown {
self.show_efuse = true;
}
self.efuse = Some(tmp);
self.status = StatusText::Ok(format!("Read OK!"));
}
Err(e) => {
self.status = StatusText::Err(format!("Error reading TCM EFUSE configuration: {}", e))
}
}
}
let board_ver = self.efuse.clone().map(|x| x.board_ver()).unwrap_or(BoardType::Unknown);
if let Some(scn) = self.scn.borrow_mut() {
egui::Grid::new("DGS").striped(true).show(ui, |ui| {
let mut x = scn.is_large_nag() == 1;
@ -157,9 +205,89 @@ impl crate::window::InterfacePage for ConfigPage {
}
ui.end_row();
}
ui.label("EGS CAN Layer: ");
let mut can = scn.egs_can_type();
egui::ComboBox::from_id_source("can_layer")
.width(100.0)
.selected_text(format!("{:?}", can))
.show_ui(ui, |cb_ui| {
let layers = match board_ver {
BoardType::Unknown | BoardType::V11 => vec![EgsCanType::UNKNOWN, EgsCanType::EGS52, EgsCanType::EGS53],
_ => vec![EgsCanType::UNKNOWN, EgsCanType::EGS51, EgsCanType::EGS52, EgsCanType::EGS53]
};
for layer in layers {
cb_ui.selectable_value(&mut can, layer.clone(), format!("{:?}", layer));
}
scn.set_egs_can_type(can)
});
ui.end_row();
if board_ver == BoardType::V12 || board_ver == BoardType::V13 { // 1.2 or 1.3 config
ui.label("Shifter style: ");
let mut ss = scn.shifter_style();
egui::ComboBox::from_id_source("shifter_style")
.width(200.0)
.selected_text(format!("{:?}", ss))
.show_ui(ui, |cb_ui| {
let options = vec![ShifterStyle::EWM_CAN, ShifterStyle::TRRS, ShifterStyle::SLR_MCLAREN];
for o in options {
cb_ui.selectable_value(&mut ss, o.clone(), format!("{:?}", o));
}
scn.set_shifter_style(ss)
});
ui.end_row();
}
if board_ver == BoardType::V13 { // Only v1.3 config
ui.label("GPIO usage: ");
let mut ss = scn.io_0_usage();
egui::ComboBox::from_id_source("gpio_usage")
.width(200.0)
.selected_text(format!("{:?}", ss))
.show_ui(ui, |cb_ui| {
let options = vec![IOPinConfig::NotConnected, IOPinConfig::Input, IOPinConfig::Output];
for o in options {
cb_ui.selectable_value(&mut ss, o.clone(), format!("{:?}", o));
}
scn.set_io_0_usage(ss)
});
ui.end_row();
if scn.io_0_usage() == IOPinConfig::Input {
let mut t = format!("{}", scn.input_sensor_pulses_per_rev());
ui.label("Input sensor pulses/rev");
ui.text_edit_singleline(&mut t);
if let Ok(prev) = buffer.parse::<u8>() {
scn.set_input_sensor_pulses_per_rev(prev);
}
ui.end_row();
} else if scn.io_0_usage() == IOPinConfig::Output {
let mut t = format!("{}", scn.output_pulse_width_per_kmh());
ui.label("Pulse width (us) per kmh");
ui.text_edit_singleline(&mut t);
if let Ok(prev) = buffer.parse::<u8>() {
scn.set_output_pulse_width_per_kmh(prev);
}
ui.end_row();
}
ui.label("General MOSFET usage: ");
let mut ss = scn.mosfet_purpose();
egui::ComboBox::from_id_source("mosfet_purpose")
.width(200.0)
.selected_text(format!("{:?}", ss))
.show_ui(ui, |cb_ui| {
let options = vec![MosfetPurpose::NotConnected, MosfetPurpose::TorqueCutTrigger, MosfetPurpose::B3BrakeSolenoid];
for o in options {
cb_ui.selectable_value(&mut ss, o.clone(), format!("{:?}", o));
}
scn.set_mosfet_purpose(ss)
});
ui.end_row();
}
});
if ui.button("Write configuration").clicked() {
if ui.button("Write SCN configuration").clicked() {
let res = {
let mut x: Vec<u8> = vec![0x3B, 0xFE];
x.extend_from_slice(&scn.clone().into_bytes());
@ -179,6 +307,98 @@ impl crate::window::InterfacePage for ConfigPage {
}
}
if let Some(efuse) = self.efuse.borrow_mut() {
if self.show_efuse {
ui.heading("EFUSE CONFIG");
ui.label("IMPORTANT! This can only be set once! Be careful!");
ui.spacing();
ui.horizontal(|row| {
row.vertical(|col| {
col.label("V1.1 - Red PCB (12/12/21)");
col.image(self.pcb_11_img.texture_id(col.ctx()), Vec2::from((200.0, 150.0)));
});
row.separator();
row.vertical(|col| {
col.label("V1.2 - Black PCB (07/07/22) with TRRS support");
col.image(self.pcb_12_img.texture_id(col.ctx()), Vec2::from((200.0, 150.0)));
});
row.separator();
row.vertical(|col| {
col.label("V1.3 - UNDER DEVELOPMENT - DO NOT SELECT!!!!");
col.image(self.pcb_13_img.texture_id(col.ctx()), Vec2::from((230.0, 150.0)));
});
});
let mut ver = efuse.board_ver();
ui.label("Choose board variant: ");
egui::ComboBox::from_id_source("board_ver")
.width(100.0)
.selected_text(format!("{:?}", efuse.board_ver()))
.show_ui(ui, |cb_ui| {
let profiles = vec![BoardType::V11, BoardType::V12, BoardType::V13];
for (pos, dev) in profiles.iter().enumerate() {
cb_ui.selectable_value(&mut ver, dev.clone(), dev.to_string());
}
efuse.set_board_ver(ver)
});
}
if self.show_efuse && efuse.board_ver() != BoardType::Unknown {
if ui.button("Write EFUSE configuration").clicked() {
self.show_final_warning = true;
}
}
}
let mut tmp = self.show_final_warning;
let ss = ui.ctx().input().screen_rect();
let mut reload = false;
egui::Window::new("ARE YOU SURE?")
.open(&mut self.show_final_warning)
.fixed_pos(Pos2::new(ss.size().x / 2.0,ss.size().y / 2.0))
.show(ui.ctx(), |win| {
win.label("EFUSE CONFIGURATION CANNOT BE UN-DONE");
win.label("Please double check and ensure you have selected the right board variant!");
win.horizontal(|row| {
if row.button("Take me back").clicked() {
tmp = false;
}
if row.button("Yes, I am sure!").clicked() {
let mut efuse = self.efuse.clone().unwrap();
let date = chrono::Utc::now().date_naive();
efuse.set_manf_day(date.day() as u8);
efuse.set_manf_week(date.iso_week().week() as u8);
efuse.set_manf_month(date.month() as u8);
efuse.set_manf_year((date.year() - 2000) as u8);
println!("EFUSE: {:?}", efuse);
let mut x = vec![0x3Bu8, 0xFD];
x.extend_from_slice(&efuse.into_bytes());
self.server
.lock()
.unwrap()
.set_diagnostic_session_mode(SessionType::ExtendedDiagnostics);
self.server
.lock()
.unwrap()
.send_byte_array_with_response(&x);
self.server
.lock()
.unwrap()
.reset_ecu(ResetMode::PowerOnReset);
tmp = false;
}
}
)
});
if reload {
*self = Self::new(self.server.clone(), self.bar.clone());
}
self.show_final_warning = tmp;
ui.add(self.status.clone());
PageAction::None
}

View File

@ -4,11 +4,11 @@ use std::{
Arc, Mutex, RwLock,
},
thread,
time::{Duration, Instant},
time::{Duration, Instant}, ops::RangeInclusive,
};
use ecu_diagnostics::kwp2000::{Kwp2000DiagnosticServer, SessionType};
use eframe::egui::plot::{Legend, Line, Plot, PlotPoints};
use eframe::egui::plot::{Legend, Line, Plot, PlotPoints, Bar, BarChart};
use crate::{ui::status_bar::MainStatusBar, window::PageAction};
@ -23,6 +23,13 @@ pub struct SolenoidPage {
curr_values: Arc<RwLock<Option<DataSolenoids>>>,
prev_values: Arc<RwLock<Option<DataSolenoids>>>,
time_since_launch: Instant,
view_type: ViewType
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ViewType {
Pwm,
Current
}
impl SolenoidPage {
@ -74,6 +81,7 @@ impl SolenoidPage {
last_update_time: last_update,
prev_values: store_old,
time_since_launch: launch_time,
view_type: ViewType::Pwm
}
}
}
@ -84,40 +92,12 @@ const MAX_DUTY: u16 = 0xFFF; // 12bit pwm (4096)
const VOLTAGE_HIGH: f64 = 12.0;
const VOLTAGE_LOW: f64 = 0.0;
fn make_line_duty_pwm(duty: f32, freq: u16, x_off: f64, y_off: f64) -> PlotPoints {
let num_pulses = freq / GRAPH_TIME_MS as u16;
let pulse_width = GRAPH_TIME_MS as f64 / num_pulses as f64;
let pulse_on_width = (duty as f64 / 4096.0) * pulse_width;
let pulse_off_width = pulse_width - pulse_on_width;
fn make_pwm_bar(idx: usize, duty: f32) -> Bar {
return Bar::new(idx as f64, (duty as f64 / 4096.0) * 100.0);
}
let mut points: Vec<[f64; 2]> = Vec::new();
let mut curr_x_pos = 0f64;
// Shortcut
if duty as u16 == MAX_DUTY {
points.push([0.0, VOLTAGE_LOW]);
points.push([GRAPH_TIME_MS, VOLTAGE_LOW]);
} else if duty as u16 == 0 {
points.push([0.0, VOLTAGE_HIGH]);
points.push([GRAPH_TIME_MS, VOLTAGE_HIGH]);
} else {
for i in 0..num_pulses {
// Start at 12V (High - Solenoid off)
points.push([curr_x_pos, VOLTAGE_HIGH]); // High, left
curr_x_pos += pulse_off_width;
points.push([curr_x_pos, VOLTAGE_HIGH]); // High, right
// Now vertical line
points.push([curr_x_pos, VOLTAGE_LOW]);
curr_x_pos += pulse_on_width;
points.push([curr_x_pos, VOLTAGE_LOW]);
// Now draw at 0V (Low - Solenoid on)
}
}
for p in points.iter_mut() {
p[0] += x_off;
p[1] += y_off;
}
points.into()
fn make_current_bar(idx: usize, c: f32) -> Bar {
return Bar::new(idx as f64, c as f64);
}
impl crate::window::InterfacePage for SolenoidPage {
@ -127,9 +107,14 @@ impl crate::window::InterfacePage for SolenoidPage {
frame: &eframe::Frame,
) -> crate::window::PageAction {
ui.heading("Solenoid live view");
ui.horizontal(|row| {
row.label("Showing: ");
row.selectable_value(&mut self.view_type, ViewType::Pwm, "PWM");
row.selectable_value(&mut self.view_type, ViewType::Current, "Current");
});
let mut curr = self.curr_values.read().unwrap().clone().unwrap_or_default();
let mut prev = self.prev_values.read().unwrap().clone().unwrap_or_default();
let curr = self.curr_values.read().unwrap().clone().unwrap_or_default();
let prev = self.prev_values.read().unwrap().clone().unwrap_or_default();
let ms_since_update = std::cmp::min(
UPDATE_DELAY_MS,
@ -139,7 +124,6 @@ impl crate::window::InterfacePage for SolenoidPage {
let mut proportion_curr: f32 = (ms_since_update as f32) / UPDATE_DELAY_MS as f32; // Percentage of old value to use
let mut proportion_prev: f32 = 1.0 - proportion_curr; // Percentage of curr value to use
if ms_since_update == 0 {
proportion_prev = 0.5;
proportion_curr = 0.5;
@ -147,93 +131,97 @@ impl crate::window::InterfacePage for SolenoidPage {
proportion_prev = 0.5;
proportion_curr = 0.5;
}
let mut lines = Vec::new();
let mut bars = Vec::new();
let mut legend = Legend::default();
let c_height = (ui.available_height() - 50.0) / 6.0;
let x_fmt = |y, _range: &RangeInclusive<f64>| {
match y as usize {
1 => "MPC",
2 => "SPC",
3 => "TCC",
4 => "Y3",
5 => "Y4",
6 => "Y5",
_ => ""
}.to_string()
};
lines.push((
"MPC",
Line::new(make_line_duty_pwm(
(curr.mpc_pwm() as f32 * proportion_curr)
+ (prev.mpc_pwm() as f32 * proportion_prev),
1000,
0.0,
0.0,
))
.name("MPC")
.width(2.0),
));
lines.push((
"SPC",
Line::new(make_line_duty_pwm(
(curr.spc_pwm() as f32 * proportion_curr)
+ (prev.spc_pwm() as f32 * proportion_prev),
1000,
0.0,
0.0,
))
.name("SPC")
.width(2.0),
));
lines.push((
"TCC",
Line::new(make_line_duty_pwm(
(curr.tcc_pwm() as f32 * proportion_curr)
+ (prev.tcc_pwm() as f32 * proportion_prev),
1000,
0.0,
0.0,
))
.name("TCC")
.width(2.0),
));
let mut plot = Plot::new("Solenoid data")
.allow_drag(false)
.include_y(0)
.legend(legend.clone())
.x_axis_formatter(x_fmt)
.allow_zoom(false)
.allow_boxed_zoom(false)
.allow_scroll(false)
.allow_drag(false);
lines.push((
"Y3",
Line::new(make_line_duty_pwm(
(curr.y3_pwm() as f32 * proportion_curr) + (prev.y3_pwm() as f32 * proportion_prev),
1000,
0.0,
0.0,
))
.name("Y3")
.width(2.0),
));
lines.push((
"Y4",
Line::new(make_line_duty_pwm(
(curr.y4_pwm() as f32 * proportion_curr) + (prev.y4_pwm() as f32 * proportion_prev),
1000,
0.0,
0.0,
))
.name("Y4")
.width(2.0),
));
lines.push((
"Y5",
Line::new(make_line_duty_pwm(
(curr.y5_pwm() as f32 * proportion_curr) + (prev.y5_pwm() as f32 * proportion_prev),
1000,
0.0,
0.0,
))
.name("Y5")
.width(2.0),
));
for line in lines {
let mut plot = Plot::new(format!("Solenoid {}", line.0))
.allow_drag(false)
.height(c_height)
.legend(legend.clone());
plot = plot.include_y(13);
plot = plot.include_y(-1);
plot = plot.include_x(0);
plot = plot.include_x(100);
plot.show(ui, |plot_ui| plot_ui.line(line.1));
if self.view_type == ViewType::Pwm {
bars.push(
make_pwm_bar(1, (curr.mpc_pwm() as f32 * proportion_curr)
+ (prev.mpc_pwm() as f32 * proportion_prev)).name("MPC")
);
bars.push(
make_pwm_bar(2, (curr.spc_pwm() as f32 * proportion_curr)
+ (prev.spc_pwm() as f32 * proportion_prev)).name("SPC")
);
bars.push(
make_pwm_bar(3, (curr.tcc_pwm() as f32 * proportion_curr)
+ (prev.tcc_pwm() as f32 * proportion_prev)).name("TCC")
);
bars.push(
make_pwm_bar(4, (curr.y3_pwm() as f32 * proportion_curr)
+ (prev.y3_pwm() as f32 * proportion_prev)).name("Y3")
);
bars.push(
make_pwm_bar(5, (curr.y4_pwm() as f32 * proportion_curr)
+ (prev.y4_pwm() as f32 * proportion_prev)).name("Y4")
);
bars.push(
make_pwm_bar(6, (curr.y5_pwm() as f32 * proportion_curr)
+ (prev.y5_pwm() as f32 * proportion_prev)).name("Y5")
);
let mut y_fmt_pwm = |x, _range: &RangeInclusive<f64>| {
format!("{} %", x)
};
plot = plot.y_axis_formatter( y_fmt_pwm);
plot = plot.include_y(100);
} else {
bars.push(
make_current_bar(1, (curr.mpc_current() as f32 * proportion_curr)
+ (prev.mpc_current() as f32 * proportion_prev)).name("MPC")
);
bars.push(
make_current_bar(2, (curr.spc_current() as f32 * proportion_curr)
+ (prev.spc_current() as f32 * proportion_prev)).name("SPC")
);
bars.push(
make_current_bar(3, (curr.tcc_current() as f32 * proportion_curr)
+ (prev.tcc_current() as f32 * proportion_prev)).name("TCC")
);
bars.push(
make_current_bar(4, (curr.y3_current() as f32 * proportion_curr)
+ (prev.y3_current() as f32 * proportion_prev)).name("Y3")
);
bars.push(
make_current_bar(5, (curr.y4_current() as f32 * proportion_curr)
+ (prev.y4_current() as f32 * proportion_prev)).name("Y4")
);
bars.push(
make_current_bar(6, (curr.y5_current() as f32 * proportion_curr)
+ (prev.y5_current() as f32 * proportion_prev)).name("Y5")
);
let mut y_fmt_current = |x, _range: &RangeInclusive<f64>| {
format!("{} mA", x)
};
plot = plot.y_axis_formatter( y_fmt_current);
plot = plot.include_y(2000);
}
plot.show(ui, |plot_ui| {
for bar in bars {
plot_ui.bar_chart(BarChart::new(vec![bar]))
}
});
ui.ctx().request_repaint();
PageAction::None
}