Skip to content

Commit

Permalink
init cerb
Browse files Browse the repository at this point in the history
  • Loading branch information
jr1221 committed Sep 6, 2024
1 parent d160c52 commit a5bcccf
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["msb-fw-rs", "crates/*"]
members = ["msb-fw-rs", "cerberus", "crates/*"]
resolver = "2"

[workspace.dependencies]
Expand Down
20 changes: 20 additions & 0 deletions cerberus/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
edition = "2021"
name = "cerberus"
version = "0.1.0"

[dependencies]
cortex-m.workspace = true
cortex-m-rt.workspace = true
defmt.workspace = true
defmt-rtt.workspace = true
embassy-embedded-hal.workspace = true
embassy-executor.workspace = true
embassy-stm32.workspace = true
embassy-sync.workspace = true
embassy-time.workspace = true
embassy-futures.workspace = true
heapless.workspace = true
panic-probe.workspace = true
static_cell.workspace = true
pca9539-ner = { version = "0.1.0", path = "../crates/pca9539-ner" }
21 changes: 21 additions & 0 deletions cerberus/src/bms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use embassy_futures::select::select;
use embassy_stm32::can::Frame;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
use embassy_time::Timer;

use crate::FaultCode;

#[embassy_executor::task]
pub async fn bms_handler(
recved: &'static Signal<CriticalSectionRawMutex, Frame>,
fault: &'static Signal<CriticalSectionRawMutex, FaultCode>,
) {
loop {
match select(recved.wait(), Timer::after_secs(4)).await {
embassy_futures::select::Either::First(_) => continue,
embassy_futures::select::Either::Second(_) => {
fault.signal(FaultCode::BmsCanMonitorFault)
}
}
}
}
55 changes: 55 additions & 0 deletions cerberus/src/can_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use defmt::{trace, unwrap, warn};
use embassy_futures::select;
use embassy_futures::select::select;
use embassy_stm32::can::{
filter::{BankConfig, ListEntry16},
Can, Frame, StandardId,
};
use embassy_sync::{
blocking_mutex::raw::{CriticalSectionRawMutex, ThreadModeRawMutex},
channel::Receiver,
signal::Signal,
};

#[embassy_executor::task]
pub async fn can_handler(
mut can: Can<'static>,
bms_callback: &'static Signal<CriticalSectionRawMutex, Frame>,
dti_callback: &'static Signal<CriticalSectionRawMutex, Frame>,
recv: Receiver<'static, ThreadModeRawMutex, Frame, 25>,
) {
can.set_bitrate(500_000);
can.modify_filters().enable_bank(
0,
embassy_stm32::can::Fifo::Fifo0,
BankConfig::List16([
ListEntry16::data_frames_with_id(unwrap!(StandardId::new(0x156))),
ListEntry16::data_frames_with_id(unwrap!(StandardId::new(0x416))),
ListEntry16::data_frames_with_id(unwrap!(StandardId::new(0x1))), // TODO needed?
ListEntry16::data_frames_with_id(unwrap!(StandardId::new(0x2))),
]),
);
can.enable().await;

loop {
match select(recv.receive(), can.read()).await {
select::Either::First(frame) => {
trace!("Sending frame: {}", frame);
can.write(&frame).await;
}
select::Either::Second(res) => match res {
Ok(got) => match got.frame.header().id() {
embassy_stm32::can::Id::Standard(header) => match header.as_raw() {
0x416 => dti_callback.signal(got.frame),
0x156 => bms_callback.signal(got.frame),
_ => warn!("Ignored message of id {}", header.as_raw()),
},
embassy_stm32::can::Id::Extended(header) => {
warn!("Ignored message of ext. id {}", header.as_raw())
}
},
Err(err) => warn!("Bus error! {}", err),
},
}
}
}
46 changes: 46 additions & 0 deletions cerberus/src/fault.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use defmt::{debug, unwrap, warn};
use embassy_futures::select::select;
use embassy_stm32::can::{Frame, StandardId};
use embassy_sync::{
blocking_mutex::raw::{CriticalSectionRawMutex, ThreadModeRawMutex},
channel::Sender,
signal::Signal,
};
use embassy_time::Timer;

use crate::FaultCode;

#[embassy_executor::task]
pub async fn fault_handler(
can_send: Sender<'static, ThreadModeRawMutex, Frame, 25>,
fault: &'static Signal<CriticalSectionRawMutex, FaultCode>,
) {
let mut last_fault = FaultCode::FaultsClear;

let status_id: StandardId = unwrap!(StandardId::new(0x502));

let mut fault_bits: [u8; 5] = [0u8; 5];

loop {
last_fault = match select(fault.wait(), Timer::after_millis(250)).await {
embassy_futures::select::Either::First(event) => {
match event.get_severity() {
crate::FaultSeverity::Defcon1
| crate::FaultSeverity::Defcon2
| crate::FaultSeverity::Defcon3 => todo!("Fault"),
crate::FaultSeverity::Defcon4 => warn!("Non critical fault!"),
crate::FaultSeverity::Defcon5 => debug!("Faults clear!"),
}
event
}
embassy_futures::select::Either::Second(_) => last_fault,
};

fault_bits[3..4].copy_from_slice(&(last_fault.get_severity() as u8).to_be_bytes());
fault_bits[0..3].copy_from_slice(&(last_fault as u32).to_be_bytes());

can_send
.send(unwrap!(Frame::new_data(status_id, &fault_bits)))
.await;
}
}
63 changes: 63 additions & 0 deletions cerberus/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#![no_std]

pub mod bms;
pub mod can_handler;
pub mod fault;

pub type SharedI2c = embassy_sync::mutex::Mutex<
embassy_sync::blocking_mutex::raw::NoopRawMutex,
embassy_stm32::i2c::I2c<'static, embassy_stm32::mode::Async>,
>;

#[derive(Copy, Clone)]
pub enum FunctionalType {
READY,
/* F means functional */
FPit,
FPerformance,
FEfficiency,
REVERSE,
FAULTED,
}
#[derive(Copy, Clone)]
pub enum NeroType {
OFF,
PIT, //SPEED_LIMITIED
PERFORMANCE, //AUTOCROSS
EFFICIENCY, //ENDURANCE
DEBUG,
CONFIGURATION,
FlappyBird,
EXIT,
}
#[derive(Copy, Clone)]
pub enum StateTransition {
Functional(FunctionalType),
Nero(NeroType),
}

// TODO: this is a breaking change and is also ugly and non-exhuastive in terms of IDs
// However it is centralized which is better than the C impl

pub enum FaultSeverity {
Defcon1 = 1,
Defcon2 = 2,
Defcon3 = 3,
Defcon4 = 4,
Defcon5 = 5,
}

#[derive(Copy, Clone)]
pub enum FaultCode {
FaultsClear = 0x0,
BmsCanMonitorFault = 0x800,
}

impl FaultCode {
fn get_severity(&self) -> FaultSeverity {
match self {
FaultCode::FaultsClear => FaultSeverity::Defcon5,
FaultCode::BmsCanMonitorFault => FaultSeverity::Defcon4,
}
}
}
162 changes: 162 additions & 0 deletions cerberus/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#![no_std]
#![no_main]

use core::fmt::Write;

use cerberus::{bms, can_handler, fault, FaultCode, SharedI2c, StateTransition};
use cortex_m::{peripheral::SCB, singleton};
use cortex_m_rt::{exception, ExceptionFrame};
use defmt::{info, unwrap, warn};
use embassy_executor::Spawner;
use embassy_stm32::{
adc::{Adc, SampleTime, Sequence},
bind_interrupts,
can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler},
i2c::{self, I2c},
peripherals::CAN1,
time::Hertz,
};
use embassy_stm32::{
can::Frame,
gpio::{Level, Output, Speed},
peripherals,
usart::{self, Uart},
wdg::IndependentWatchdog,
Config,
};
use embassy_sync::{
blocking_mutex::raw::{CriticalSectionRawMutex, ThreadModeRawMutex},
channel::Channel,
mutex::Mutex,
signal::Signal,
};
use embassy_time::Timer;
use heapless::String;
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};

bind_interrupts!(struct IrqsCAN {
CAN1_RX0 => Rx0InterruptHandler<CAN1>;
CAN1_RX1 => Rx1InterruptHandler<CAN1>;
CAN1_SCE => SceInterruptHandler<CAN1>;
CAN1_TX => TxInterruptHandler<CAN1>;
});

bind_interrupts!(struct IrqsUsart {
USART3 => usart::InterruptHandler<peripherals::USART3>;
});

bind_interrupts!(struct IrqsI2c1 {
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
});

bind_interrupts!(struct IrqsI2c2 {
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
});

static CAN_CHANNEL: Channel<ThreadModeRawMutex, Frame, 25> = Channel::new();

static CURRENT_STATE: Signal<CriticalSectionRawMutex, StateTransition> = Signal::new();
static FAULT: Signal<CriticalSectionRawMutex, FaultCode> = Signal::new();

static BMS_CALLBACK: Signal<CriticalSectionRawMutex, Frame> = Signal::new();
static DTI_CALLBACK: Signal<CriticalSectionRawMutex, Frame> = Signal::new();

#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
info!("Initializing Cerberus...");

let mut p = embassy_stm32::init(Config::default());

let can = Can::new(p.CAN1, p.PA11, p.PA12, IrqsCAN);
if let Err(err) = spawner.spawn(can_handler::can_handler(
can,
&BMS_CALLBACK,
&DTI_CALLBACK,
CAN_CHANNEL.receiver(),
)) {
warn!("Could not spawn CAN task: {}", err);
}

if let Err(err) = spawner.spawn(bms::bms_handler(&BMS_CALLBACK, &FAULT)) {
warn!("Could not spawn BMS task: {}", err);
}

if let Err(err) = spawner.spawn(fault::fault_handler(CAN_CHANNEL.sender(), &FAULT)) {
warn!("Could not spawn fault task: {}", err);
}

static I2C_BUS_1: StaticCell<SharedI2c> = StaticCell::new();
let i2c_1 = I2c::new(
p.I2C1,
p.PB6,
p.PB7,
IrqsI2c1,
p.DMA1_CH6,
p.DMA1_CH0,
Hertz(100_000),
i2c::Config::default(),
);
let i2c_bus_1 = I2C_BUS_1.init(Mutex::new(i2c_1));

static I2C_BUS_2: StaticCell<SharedI2c> = StaticCell::new();
let i2c_2 = I2c::new(
p.I2C2,
p.PB10,
p.PB11,
IrqsI2c2,
p.DMA1_CH7,
p.DMA1_CH2,
Hertz(100_000),
i2c::Config::default(),
);
let i2c_bus_2 = I2C_BUS_2.init(Mutex::new(i2c_2));

const ADC_BUF_SIZE: usize = 1024;

let adc1 = Adc::new(p.ADC1);
let adc_data_1 = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE])
.expect("Could not init adc buffer");
let mut adc1 = adc1.into_ring_buffered(p.DMA2_CH4, adc_data_1);
adc1.set_sample_sequence(Sequence::One, &mut p.PB0, SampleTime::CYCLES112); //

let adc3 = Adc::new(p.ADC3);
let adc_data_3 = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE])
.expect("Could not init adc buffer");
let mut adc3 = adc3.into_ring_buffered(p.DMA2_CH0, adc_data_3);
adc3.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); //
adc3.set_sample_sequence(Sequence::One, &mut p.PA1, SampleTime::CYCLES112); //
adc3.set_sample_sequence(Sequence::One, &mut p.PA2, SampleTime::CYCLES112); //
adc3.set_sample_sequence(Sequence::One, &mut p.PA3, SampleTime::CYCLES112); //

let mut usart = Uart::new(
p.USART3,
p.PC11,
p.PC10,
IrqsUsart,
p.DMA1_CH3,
p.DMA1_CH1,
usart::Config::default(),
)
.unwrap();
let mut s: String<128> = String::new();
core::write!(&mut s, "Hello DMA World!\r\n",).unwrap();
unwrap!(usart.write(s.as_bytes()).await);

let mut watchdog = IndependentWatchdog::new(p.IWDG, 4000000);
watchdog.unleash();
let mut led_pin = Output::new(p.PC8, Level::Low, Speed::Low);
loop {
info!("Status: Alive");
led_pin.toggle();
Timer::after_secs(3).await;
watchdog.pet();
}
}

#[exception]
unsafe fn HardFault(_frame: &ExceptionFrame) -> ! {
SCB::sys_reset() // <- you could do something other than reset
}
2 changes: 1 addition & 1 deletion crates/pca9539-ner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub struct Pca9539<I2C> {
address: u8,
}

///
/// Pca9539 GPIO expander (or TCA9539)
impl<I2C: I2c, E> Pca9539<I2C>
where
I2C: I2c<Error = E>,
Expand Down
Loading

0 comments on commit a5bcccf

Please sign in to comment.