Implemented config persitence

+ added ConfigStorage struct
+ added CRC computation for data integrity
* fixed config error message not showing
This commit is contained in:
Steins7 2022-01-29 15:41:05 +01:00
parent 07245df732
commit dedb811aab
5 changed files with 133 additions and 27 deletions

View File

@ -14,6 +14,7 @@ cortex-m-semihosting = "0.3.7"
panic-halt = "0.2.0" panic-halt = "0.2.0"
hd44780-driver = "0.3.0" hd44780-driver = "0.3.0"
libm = "0.2.1" libm = "0.2.1"
half = "1.8.2"
[dependencies.stm32f1xx-hal] [dependencies.stm32f1xx-hal]
version = "0.8.0" version = "0.8.0"

View File

@ -3,9 +3,6 @@ use core::fmt;
use crate::lcd_gui::LCDScreen; use crate::lcd_gui::LCDScreen;
//---Config struct---------------------------------------------------------------------------------- //---Config struct----------------------------------------------------------------------------------
pub enum ConfigError {
LoadError,
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum FanOutput { pub enum FanOutput {
@ -13,6 +10,21 @@ pub enum FanOutput {
P2, P2,
} }
impl FanOutput {
pub fn from_bits(bits: u16) -> FanOutput {
if bits == 0 { return Self::P1 }
return Self::P2;
}
pub fn to_bits(&self) -> u16 {
match self {
Self::P1 => 0,
Self::P2 => 1,
}
}
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct CalData { pub struct CalData {
pub offset: f32, pub offset: f32,
@ -20,9 +32,9 @@ pub struct CalData {
} }
impl CalData { impl CalData {
fn new() -> CalData { pub fn new(offset: f32) -> CalData {
CalData { CalData {
offset: 0.0, offset,
raw_value: None, raw_value: None,
} }
} }
@ -44,22 +56,12 @@ impl SystemConfig {
SystemConfig { SystemConfig {
max_temp_diff: 8.0, max_temp_diff: 8.0,
min_temp_diff: 4.0, min_temp_diff: 4.0,
ext_offset: CalData::new(), ext_offset: CalData::new(0.0),
p1_offset: CalData::new(), p1_offset: CalData::new(0.0),
p2_offset: CalData::new(), p2_offset: CalData::new(0.0),
fan_output: FanOutput::P1, fan_output: FanOutput::P1,
} }
} }
#[allow(dead_code)]
pub fn load() -> Result<SystemConfig, ConfigError> {
Err(ConfigError::LoadError)
}
#[allow(dead_code)]
pub fn store() -> SystemConfig {
unimplemented!();
}
} }
//---Menu struct------------------------------------------------------------------------------------ //---Menu struct------------------------------------------------------------------------------------
@ -86,9 +88,9 @@ impl Menu {
Entry::new("fan out", EntryValue::FanOut (FanOutput::P1), config), Entry::new("fan out", EntryValue::FanOut (FanOutput::P1), config),
Entry::new("max diff", EntryValue::MaxTempDiff (0.0), config), Entry::new("max diff", EntryValue::MaxTempDiff (0.0), config),
Entry::new("min diff", EntryValue::MinTempDiff (0.0), config), Entry::new("min diff", EntryValue::MinTempDiff (0.0), config),
Entry::new("ext cal", EntryValue::ExtOffset (CalData::new()), config), Entry::new("ext cal", EntryValue::ExtOffset (CalData::new(0.0)), config),
Entry::new("p1 cal", EntryValue::P1Offset (CalData::new()), config), Entry::new("p1 cal", EntryValue::P1Offset (CalData::new(0.0)), config),
Entry::new("p2 cal", EntryValue::P2Offset (CalData::new()), config), Entry::new("p2 cal", EntryValue::P2Offset (CalData::new(0.0)), config),
], ],
state: MenuState::Scroll, state: MenuState::Scroll,
should_refresh: true, should_refresh: true,

View File

@ -32,6 +32,7 @@ use lcd_gui::{LCDGui, GUI_TICK_SEC};
mod utils; mod utils;
use utils::{ use utils::{
TemperatureProbe, TemperatureProbe,
ConfigStorage,
}; };
mod config; mod config;
@ -58,7 +59,7 @@ static TICK_TIMER: Mutex<RefCell<Option<CountDownTimer<pac::TIM2>>>>
= Mutex::new(RefCell::new(None)); = Mutex::new(RefCell::new(None));
// button interrupt // button interrupt
static BUTTON_PIN: Mutex<RefCell<Option<gpiob::PB8<Input<PullUp>>>>> static BUTTON_PIN: Mutex<RefCell<Option<gpiob::PB8<Input<Floating>>>>>
= Mutex::new(RefCell::new(None)); = Mutex::new(RefCell::new(None));
static BUTTON_FLAG: AtomicBool = AtomicBool::new(false); static BUTTON_FLAG: AtomicBool = AtomicBool::new(false);
@ -185,14 +186,18 @@ fn main() -> ! {
let mut afio = dp.AFIO.constrain(); let mut afio = dp.AFIO.constrain();
// initialize system state // initialize system state
let mut config_storage = ConfigStorage::new(backup_domain, dp.CRC.new());
let mut error_state = ErrorState::NoError; let mut error_state = ErrorState::NoError;
let config = match SystemConfig::load() { let config = match config_storage.load_config() {
Ok(conf) => conf, Ok(conf) => conf,
Err(_) => { Err(_) => {
error_state = ErrorState::NewError("config invalide"); error_state = ErrorState::NewError("config invalide");
SystemConfig::new() let conf = SystemConfig::new();
config_storage.save_config(&conf);
conf
}}; }};
let mut state = SystemState::new(config, gpiob.pb12.into_push_pull_output(&mut gpiob.crh)); let mut state = SystemState::new(config, config_storage,
gpiob.pb12.into_push_pull_output(&mut gpiob.crh));
// Setup display // Setup display
let mut gui = { let mut gui = {
@ -234,7 +239,7 @@ fn main() -> ! {
); );
// setup button (interrupt) // setup button (interrupt)
let mut but_pin = gpiob.pb8.into_pull_up_input(&mut gpiob.crh); let mut but_pin = gpiob.pb8.into_floating_input(&mut gpiob.crh);
but_pin.make_interrupt_source(&mut afio); but_pin.make_interrupt_source(&mut afio);
but_pin.trigger_on_edge(&dp.EXTI, Edge::Rising); but_pin.trigger_on_edge(&dp.EXTI, Edge::Rising);
but_pin.enable_interrupt(&dp.EXTI); but_pin.enable_interrupt(&dp.EXTI);
@ -300,6 +305,12 @@ fn main() -> ! {
/* run */ /* run */
let _ = TEMP_FLAG.swap(true, Ordering::Release); let _ = TEMP_FLAG.swap(true, Ordering::Release);
// fix some weird bug caused by the debouncing capacitor of the button
for i in 0..10 {
cortex_m::asm::wfi();
}
BUTTON_FLAG.store(false, Ordering::Release);
loop { loop {
match error_state { match error_state {

View File

@ -6,6 +6,8 @@ use crate::config::{
FanOutput, FanOutput,
}; };
use crate::utils::ConfigStorage;
pub enum FanState { pub enum FanState {
On, On,
Off, Off,
@ -43,6 +45,7 @@ impl<O: OutputPin> Fan<O> {
pub struct SystemState<O: OutputPin> { pub struct SystemState<O: OutputPin> {
config: SystemConfig, config: SystemConfig,
config_storage: ConfigStorage,
ext_temp: Option<f32>, ext_temp: Option<f32>,
p1_temp: Option<f32>, p1_temp: Option<f32>,
p2_temp: Option<f32>, p2_temp: Option<f32>,
@ -51,10 +54,12 @@ pub struct SystemState<O: OutputPin> {
impl<O: OutputPin> SystemState<O> { impl<O: OutputPin> SystemState<O> {
pub fn new(config: SystemConfig, fan_control_pin: O) -> SystemState<O> { pub fn new(config: SystemConfig, config_storage: ConfigStorage, fan_control_pin: O)
-> SystemState<O> {
SystemState { SystemState {
config, config,
config_storage,
ext_temp: None, ext_temp: None,
p1_temp: None, p1_temp: None,
p2_temp: None, p2_temp: None,
@ -76,6 +81,7 @@ impl<O: OutputPin> SystemState<O> {
// replace config // replace config
self.config = config; self.config = config;
self.config_storage.save_config(&self.config);
// reset fan state before recomputing fan state // reset fan state before recomputing fan state
self.fan.off(); self.fan.off();

View File

@ -6,8 +6,17 @@ use core::{
use embedded_hal::adc::*; use embedded_hal::adc::*;
use stm32f1xx_hal::{
backup_domain::BackupDomain,
crc::Crc,
};
use libm::*; use libm::*;
use half::f16;
use crate::config::SystemConfig;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/* 2nd order linearisation factor for the temperature probe */ /* 2nd order linearisation factor for the temperature probe */
const A: f32 = -0.000574; const A: f32 = -0.000574;
@ -138,3 +147,80 @@ where
} }
} }
//--------------------------------------------------------------------------------------------------
/* ConfigStorage */
pub struct ConfigStorage {
backup_domain: BackupDomain,
crc: Crc,
}
impl ConfigStorage {
pub fn new(backup_domain: BackupDomain, crc: Crc) -> ConfigStorage {
ConfigStorage {
backup_domain,
crc,
}
}
pub fn load_config(&mut self) -> Result<SystemConfig, ()> {
use crate::config::{CalData, FanOutput};
// read from persistent memory
let mut values: [u16; 10] = [0; 10];
for (i, val) in values.iter_mut().enumerate() {
*val = self.backup_domain.read_data_register_low(i);
}
// compute CRC
self.crc.reset();
for i in (2..8).step_by(2) {
self.crc.write((values[i] as u32) << 16 | values[i+1] as u32);
}
let crc = (values[0] as u32) << 16 | values[1] as u32;
// parse config if CRC is valid
match crc != 0 && crc == self.crc.read() {
true => {
Ok(SystemConfig {
max_temp_diff: f16::from_bits(values[2]).to_f32(),
min_temp_diff: f16::from_bits(values[3]).to_f32(),
ext_offset: CalData::new(f16::from_bits(values[4]).to_f32()),
p1_offset: CalData::new(f16::from_bits(values[5]).to_f32()),
p2_offset: CalData::new(f16::from_bits(values[6]).to_f32()),
fan_output: FanOutput::from_bits(values[7]),
})
},
false => Err(()),
}
}
pub fn save_config(&mut self, config: &SystemConfig) {
// prepare buffer for later storage
let mut values: [u16; 10] = [0; 10];
values[2] = f16::from_f32(config.max_temp_diff).to_bits();
values[3] = f16::from_f32(config.min_temp_diff).to_bits();
values[4] = f16::from_f32(config.ext_offset.offset).to_bits();
values[5] = f16::from_f32(config.p1_offset.offset).to_bits();
values[6] = f16::from_f32(config.p2_offset.offset).to_bits();
values[7] = config.fan_output.to_bits();
// compute CRC
self.crc.reset();
for i in (2..8).step_by(2) {
self.crc.write((values[i] as u32) << 16 | values[i+1] as u32);
}
let crc = self.crc.read();
values[0] = ((crc >> 16) & 0xFFFF) as u16;
values[1] = (crc & 0xFFFF) as u16;
// write to persistent memory
for (i, val) in values.iter().enumerate() {
self.backup_domain.write_data_register_low(i, *val);
}
}
}