Skip to content

Commit 1b7cce5

Browse files
committed
Persist the calibration parameter
1 parent 92b21a0 commit 1b7cce5

File tree

5 files changed

+133
-4
lines changed

5 files changed

+133
-4
lines changed

escale_fw_rs/.vscode/settings.json

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"libm",
1010
"pico",
1111
"plls",
12+
"repr",
13+
"rodata",
1214
"Uninit",
1315
"XOSC",
1416
"xtal"

escale_fw_rs/app/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ rp-pico = "0.4.1"
2424
# rp2040-hal = { version="0.4.0", features=["rt"] }
2525
# rp2040-boot2 = "0.2.0"
2626

27+
rp2040-flash = "0.1.0"
2728
ssd1306 = "0.7.0"
2829
nau7802 = { git = "https://github.com/werediver/nau7802-rs.git", branch = "next" }
2930

escale_fw_rs/app/src/flash.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use core::mem::{size_of, MaybeUninit};
2+
3+
use rp2040_flash::flash;
4+
5+
const FLASH_ORIGIN: usize = 0x10000000;
6+
/// The erasable sector size.
7+
const FLASH_SECTOR_SIZE: usize = 4096;
8+
9+
/// The payload type `T` must fit into a single flash sector
10+
/// and be `repr(C)` to have a stable layout.
11+
///
12+
/// This data type itself should be flash sector aligned.
13+
#[repr(C, align(4096))]
14+
pub union FlashSector<T>
15+
where
16+
T: Copy,
17+
// `FLASH_SECTOR_SIZE` must be a power of two
18+
[(); 0 - !is_pwr_of_two(FLASH_SECTOR_SIZE) as usize]:,
19+
// `T` must fit into a single sector size
20+
[(); FLASH_SECTOR_SIZE - size_of::<T>()]:,
21+
{
22+
data: [u8; FLASH_SECTOR_SIZE],
23+
value: MaybeUninit<T>,
24+
}
25+
26+
impl<T> FlashSector<T>
27+
where
28+
T: Copy,
29+
[(); FLASH_SECTOR_SIZE - size_of::<T>()]:,
30+
{
31+
pub const fn uninit() -> Self {
32+
Self {
33+
value: MaybeUninit::uninit(),
34+
}
35+
}
36+
37+
const fn new(value: T) -> Self {
38+
Self {
39+
value: MaybeUninit::new(value),
40+
}
41+
}
42+
43+
fn mem_addr(&self) -> usize {
44+
unsafe { &self.data as *const _ as usize }
45+
}
46+
47+
pub fn read(&self) -> MaybeUninit<T> {
48+
unsafe { self.value }
49+
}
50+
51+
pub unsafe fn write(&mut self, value: T) {
52+
let tmp_flash_block = FlashSector::new(value);
53+
self.write_flash(&tmp_flash_block.data)
54+
}
55+
56+
unsafe fn write_flash(&self, data: &[u8; FLASH_SECTOR_SIZE]) {
57+
let flash_addr = self.mem_addr() - FLASH_ORIGIN;
58+
assert!(is_aligned(flash_addr, FLASH_SECTOR_SIZE));
59+
60+
cortex_m::interrupt::free(|_cs| {
61+
flash::flash_range_erase_and_program(flash_addr as u32, data, true);
62+
});
63+
}
64+
}
65+
66+
const fn is_pwr_of_two(n: usize) -> bool {
67+
n != 0 && (n & (n - 1) == 0)
68+
}
69+
70+
const fn is_aligned(addr: usize, alignment: usize) -> bool {
71+
assert!(is_pwr_of_two(alignment));
72+
addr & (alignment - 1) == 0
73+
}

escale_fw_rs/app/src/main.rs

+46-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
#![no_main]
33
#![feature(alloc_error_handler)]
44
#![feature(trait_alias)]
5+
#![allow(incomplete_features)]
6+
#![feature(generic_const_exprs)]
57

8+
mod flash;
69
mod ssd1306_terminal;
710
mod uptime;
811
mod uptime_delay;
@@ -15,6 +18,7 @@ use core::{alloc::Layout, cell::RefCell};
1518
use cortex_m_rt::entry;
1619
use embedded_hal::digital::v2::InputPin;
1720
use embedded_time::rate::Extensions;
21+
use flash::FlashSector;
1822
use panic_probe as _;
1923

2024
use rp_pico as bsp;
@@ -55,6 +59,31 @@ fn oom(_: Layout) -> ! {
5559
#[global_allocator]
5660
static ALLOCATOR: CortexMHeap = CortexMHeap::empty();
5761

62+
#[repr(C)]
63+
#[derive(Copy, Clone)]
64+
struct Conf {
65+
format: u16,
66+
scale_unit: f32,
67+
}
68+
69+
impl Conf {
70+
const fn initial() -> Self {
71+
Self {
72+
format: 1,
73+
scale_unit: 1.0,
74+
}
75+
}
76+
}
77+
78+
#[link_section = ".rodata"]
79+
static mut CONF_FLASH_SECTOR: FlashSector<Conf> = FlashSector::uninit();
80+
81+
unsafe fn write_conf(conf: Conf) {
82+
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
83+
CONF_FLASH_SECTOR.write(conf);
84+
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
85+
}
86+
5887
fn init_heap() {
5988
use core::mem::MaybeUninit;
6089
const HEAP_SIZE: usize = 128 * 1024;
@@ -130,6 +159,17 @@ fn _main() -> ! {
130159
Uptime::get_instant,
131160
)));
132161

162+
let conf = {
163+
let conf = unsafe { CONF_FLASH_SECTOR.read().assume_init() };
164+
165+
let init_conf = Conf::initial();
166+
if conf.format == init_conf.format {
167+
conf
168+
} else {
169+
init_conf
170+
}
171+
};
172+
133173
let i2c1 = I2C::i2c1(
134174
pac.I2C1,
135175
pins.gpio2.into_mode(),
@@ -147,6 +187,7 @@ fn _main() -> ! {
147187
)
148188
.unwrap();
149189
let mut scale = Scale::<i32, f32, 20>::default();
190+
scale.set_unit(conf.scale_unit);
150191

151192
schedule.push(AppTask::Fn(FnTask::new(move |cx: &mut AppContext| {
152193
if nau7802.data_available().unwrap() {
@@ -155,11 +196,14 @@ fn _main() -> ! {
155196
if scale.is_filled() {
156197
cx.mq.process(|m, _push| match m {
157198
AppMessage::Tare => {
158-
scale.set_tare().unwrap();
199+
scale.capture_tare().unwrap();
159200
MessageProcessingStatus::Processed
160201
}
161202
AppMessage::Calibrate => {
162-
scale.set_unit(100.0).unwrap();
203+
scale.capture_unit(100.0).unwrap();
204+
let mut conf = Conf::initial();
205+
conf.scale_unit = scale.get_unit();
206+
unsafe { write_conf(conf) }
163207
MessageProcessingStatus::Processed
164208
}
165209
_ => MessageProcessingStatus::Ignored,

escale_fw_rs/lib/app-core/src/scale.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ where
4343
/// Set the zero offset (tare) based on the current buffer.
4444
///
4545
/// The buffer must be filled.
46-
pub fn set_tare(&mut self) -> Result<(), Error> {
46+
pub fn capture_tare(&mut self) -> Result<(), Error> {
4747
if self.is_filled() {
4848
self.tare = self.read_raw()?;
4949
Ok(())
@@ -58,7 +58,7 @@ where
5858
///
5959
/// If the desired unit is 1 g and a 100 g weight is used for calibration,
6060
/// set `value` to 100.
61-
pub fn set_unit(&mut self, value: U) -> Result<(), Error> {
61+
pub fn capture_unit(&mut self, value: U) -> Result<(), Error> {
6262
assert!(value != U::zero());
6363
if self.is_filled() {
6464
let unit = (self.read_raw()? - self.tare) / value;
@@ -70,6 +70,15 @@ where
7070
}
7171
}
7272

73+
pub fn set_unit(&mut self, unit: U) {
74+
assert!(unit != U::zero());
75+
self.unit = unit;
76+
}
77+
78+
pub fn get_unit(&self) -> U {
79+
self.unit
80+
}
81+
7382
fn read_raw(&self) -> Result<U, Error> {
7483
if self.is_filled() {
7584
Ok(mean(self.ring.iter().copied()).unwrap())

0 commit comments

Comments
 (0)