diff --git a/firmware/rust1/Cargo.toml b/firmware/rust1/Cargo.toml index fefd5c0e667cb2a15986204f40697505e9f5a802..9936fa4a09a1f17f89799cdf2b1a15e7dc03010b 100644 --- a/firmware/rust1/Cargo.toml +++ b/firmware/rust1/Cargo.toml @@ -3,6 +3,8 @@ edition = "2021" name = "heizung" version = "0.1.0" license = "MIT OR Apache-2.0" +homepage = "https://git.c3pb.de/c3pb/heizung/-/tree/main/firmware/rust1" +description = "Heating control for Subraum" [features] diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs index 371d6c9936fadde785e0d56f338c359541bd6558..0d15bdfe0caa22a9ac2f25448ed19b68304327c5 100644 --- a/firmware/rust1/src/bin/heizung.rs +++ b/firmware/rust1/src/bin/heizung.rs @@ -699,6 +699,8 @@ fn init_early() { // Add program info. +// Show with: picotool info heizung-release.uf2 -a +// or `picotool info` if actual hardware is connected in bootloader mode #[used] #[link_section = ".program_info"] pub static PROGRAM_INFO: [u8; 4096] = { @@ -708,7 +710,23 @@ pub static PROGRAM_INFO: [u8; 4096] = { const PROGRAM_INFO_TABLE_SIZE: u32 = 128; // one pointer (4 bytes) for each piece of data, all must be valid pointers const PROGRAM_INFO_COPY_TABLE_SIZE: u32 = 128; // 3*u32 for each entry, picotool stops after 10 entries + #[cfg(debug_assertions)] + let build_type = "Debug"; + #[cfg(not(debug_assertions))] + let build_type = "Release"; + + // We can fill in some values from info that is provided by Cargo. + // see https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates + ProgramInfoBuilder::new(PROGRAM_INFO_FLASH_OFFSET, PROGRAM_INFO_TABLE_SIZE, PROGRAM_INFO_COPY_TABLE_SIZE) - .program_name("Test 123") + .program_name("subraum-heizung") + .program_description(env!("CARGO_PKG_DESCRIPTION")) + .program_version_string(env!("CARGO_PKG_VERSION")) + .program_url(env!("CARGO_PKG_HOMEPAGE")) + .board("subraum-heizung base v1.0") + .sdk_version("Embassy") + .program_build_attribute(build_type) + //FIXME should be based on what is selected for embassy-rp crate + .boot2_name("boot2_w25q080") .build() }; diff --git a/firmware/rust1/src/program_info.rs b/firmware/rust1/src/program_info.rs index de20db1427d422dc6d590747eb6e2847b1a0e6a2..503c5fe014398e20ba227a4cfd0f0d8ca649dd52 100644 --- a/firmware/rust1/src/program_info.rs +++ b/firmware/rust1/src/program_info.rs @@ -1,4 +1,3 @@ - pub struct ProgramInfoBuilder { addr: u32, buf: [u8; 4096], @@ -8,6 +7,16 @@ pub struct ProgramInfoBuilder { data_end_offset: usize, } +#[repr(u16)] +#[allow(non_camel_case_types)] +pub enum GroupFlags { + NONE = 0x0000, + SHOW_IF_EMPTY = 0x0001, // default is to hide + SEPARATE_COMMAS = 0x0002, // default is newlines + SORT_ALPHA = 0x0004, // default is no sort + ADVANCED = 0x0008, // if set, then only shown in say info -a +} + const fn copy(mut buf: [u8; 4096], offset: usize, source: &[u8], source_offset: usize) -> [u8; 4096] { if source_offset >= source.len() { buf @@ -129,9 +138,9 @@ impl ProgramInfoBuilder { .push(((value >> 24) & 0xff) as u8) } - const fn push_raspberry_tag(self: Self) -> Self { - self.push('R' as u8) - .push('P' as u8) + const fn push_tag(self: Self, tag: [char; 2]) -> Self { + self.push(tag[0] as u8) + .push(tag[1] as u8) } const fn push_extra_data(mut self: Self, value: &[u8]) -> (Self, u32) { @@ -149,7 +158,7 @@ impl ProgramInfoBuilder { //NOTE This will push the address and then add the string so this must be the last item of the data record. const fn push_string(mut self: Self, value: &str) -> Self { - if self.data_pos - self.info_pos < value.len() { + if self.data_pos - self.info_pos < value.len() + 1 { core::panic!("ProgramInfoBuilder: too much data") } @@ -170,34 +179,179 @@ impl ProgramInfoBuilder { self } - const BINARY_INFO_TYPE_ID_AND_INT: u16 = 5; - const BINARY_INFO_TYPE_ID_AND_STRING: u16 = 6; + const fn append_to_last_string(mut self: Self, value: &str) -> Self { + if self.data_pos - self.info_pos < value.len() { + core::panic!("ProgramInfoBuilder: too much data") + } + + self.data_pos -= 1; + (self, _) = self.push_extra_data(value.as_bytes()); + (self, _) = self.push_extra_data(&[0]); + + self + } - pub const BINARY_INFO_ID_RP_PROGRAM_NAME: u32 = 0x02031c86; - pub const BINARY_INFO_ID_RP_PROGRAM_VERSION_STRING: u32 = 0x11a9bc3a; - pub const BINARY_INFO_ID_RP_PROGRAM_BUILD_DATE_STRING: u32 = 0x9da22254; - pub const BINARY_INFO_ID_RP_BINARY_END: u32 = 0x68f465de; - pub const BINARY_INFO_ID_RP_PROGRAM_URL: u32 = 0x1856239a; - pub const BINARY_INFO_ID_RP_PROGRAM_DESCRIPTION: u32 = 0xb6a07c19; - pub const BINARY_INFO_ID_RP_PROGRAM_FEATURE: u32 = 0xa1f4b453; - pub const BINARY_INFO_ID_RP_PROGRAM_BUILD_ATTRIBUTE: u32 = 0x4275f0d3; - pub const BINARY_INFO_ID_RP_SDK_VERSION: u32 = 0x5360b3ab; - pub const BINARY_INFO_ID_RP_PICO_BOARD: u32 = 0xb63cffbb; - pub const BINARY_INFO_ID_RP_BOOT2_NAME: u32 = 0x7f8882e1; + const fn bi_int(self: Self, tag: [char; 2], id: u32, value: u32) -> Self { + self.push_current_info_ptr() + .push_u16(Self::BINARY_INFO_TYPE_ID_AND_INT) + .push_tag(tag) + .push_u32(id) + .push_u32(value) + } - pub const fn program_name(self: Self, value: &str) -> Self { + const fn bi_string(self: Self, tag: [char; 2], id: u32, value: &str) -> Self { self.push_current_info_ptr() .push_u16(Self::BINARY_INFO_TYPE_ID_AND_STRING) - .push_raspberry_tag() - .push_u32(Self::BINARY_INFO_ID_RP_PROGRAM_NAME) + .push_tag(tag) + .push_u32(id) .push_string(value) } - pub const fn binary_end_address(self: Self, value: u32) -> Self { + const fn bi_named_group(self: Self, tag: [char; 2], id: u32, flags: u16, group_tag: u16, group_id: u32, label: &str) -> Self { self.push_current_info_ptr() - .push_u16(Self::BINARY_INFO_TYPE_ID_AND_INT) - .push_raspberry_tag() - .push_u32(Self::BINARY_INFO_ID_RP_BINARY_END) + .push_u16(Self::BINARY_INFO_TYPE_NAMED_GROUP) + .push_tag(tag) + .push_u32(id) + .push_u16(flags) + .push_u16(group_tag) + .push_u32(group_id) + .push_string(label) + } + + const fn bi_encoded_pins_with_func(self: Self, value: u32) -> Self { + self.push_current_info_ptr() + .push_u16(Self::BINARY_INFO_TYPE_PINS_WITH_FUNC) + .push_tag(Self::BINARY_INFO_TAG_RASPBERRY_PI) .push_u32(value) } + + const fn bi_pins_with_names(self: Self, pin_mask: u32, label: &str) -> Self { + self.push_current_info_ptr() + .push_u16(Self::BINARY_INFO_TYPE_PINS_WITH_NAME) + .push_tag(Self::BINARY_INFO_TAG_RASPBERRY_PI) + .push_u32(pin_mask) + .push_string(label) + } + + const BINARY_INFO_TAG_RASPBERRY_PI: [char; 2] = ['R', 'P']; + + #[allow(unused)] const BINARY_INFO_TYPE_RAW_DATA: u16 = 1; + #[allow(unused)] const BINARY_INFO_TYPE_SIZED_DATA: u16 = 2; + #[allow(unused)] const BINARY_INFO_TYPE_BINARY_INFO_LIST_ZERO_TERMINATED: u16 = 3; + #[allow(unused)] const BINARY_INFO_TYPE_BSON: u16 = 4; + #[allow(unused)] const BINARY_INFO_TYPE_ID_AND_INT: u16 = 5; + #[allow(unused)] const BINARY_INFO_TYPE_ID_AND_STRING: u16 = 6; + #[allow(unused)] const BINARY_INFO_TYPE_BLOCK_DEVICE: u16 = 7; + #[allow(unused)] const BINARY_INFO_TYPE_PINS_WITH_FUNC: u16 = 8; + #[allow(unused)] const BINARY_INFO_TYPE_PINS_WITH_NAME: u16 = 9; + #[allow(unused)] const BINARY_INFO_TYPE_PINS_WITH_NAMES: u16 = 9; + #[allow(unused)] const BINARY_INFO_TYPE_NAMED_GROUP: u16 = 10; + + #[allow(unused)] const BINARY_INFO_ID_RP_PROGRAM_NAME: u32 = 0x02031c86; + #[allow(unused)] const BINARY_INFO_ID_RP_PROGRAM_VERSION_STRING: u32 = 0x11a9bc3a; + #[allow(unused)] const BINARY_INFO_ID_RP_PROGRAM_BUILD_DATE_STRING: u32 = 0x9da22254; + #[allow(unused)] const BINARY_INFO_ID_RP_BINARY_END: u32 = 0x68f465de; + #[allow(unused)] const BINARY_INFO_ID_RP_PROGRAM_URL: u32 = 0x1856239a; + #[allow(unused)] const BINARY_INFO_ID_RP_PROGRAM_DESCRIPTION: u32 = 0xb6a07c19; + #[allow(unused)] const BINARY_INFO_ID_RP_PROGRAM_FEATURE: u32 = 0xa1f4b453; + #[allow(unused)] const BINARY_INFO_ID_RP_PROGRAM_BUILD_ATTRIBUTE: u32 = 0x4275f0d3; + #[allow(unused)] const BINARY_INFO_ID_RP_SDK_VERSION: u32 = 0x5360b3ab; + #[allow(unused)] const BINARY_INFO_ID_RP_PICO_BOARD: u32 = 0xb63cffbb; + #[allow(unused)] const BINARY_INFO_ID_RP_BOOT2_NAME: u32 = 0x7f8882e1; + + pub const fn binary_end_address(self: Self, value: u32) -> Self { + self.bi_int(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_BINARY_END, value) + } + + pub const fn program_name(self: Self, name: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PROGRAM_NAME, name) + } + pub const fn program_description(self: Self, description: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PROGRAM_DESCRIPTION, description) + } + pub const fn program_version_string(self: Self, version_string: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PROGRAM_VERSION_STRING, version_string) + } + pub const fn program_build_date_string(self: Self, date_string: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PROGRAM_BUILD_DATE_STRING, date_string) + } + pub const fn program_url(self: Self, url: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PROGRAM_URL, url) + } + // multiple of these may be added + pub const fn program_feature(self: Self, feature: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PROGRAM_FEATURE, feature) + } + pub const fn program_build_attribute(self: Self, attr: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PROGRAM_BUILD_ATTRIBUTE, attr) + } + pub const fn sdk_version(self: Self, attr: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_SDK_VERSION, attr) + } + pub const fn board(self: Self, attr: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PICO_BOARD, attr) + } + pub const fn boot2_name(self: Self, attr: &str) -> Self { + self.bi_string(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_BOOT2_NAME, attr) + } + pub const fn program_feature_group(self: Self, tag: u16, id: u32, label: &str) -> Self { + self.program_feature_group_with_flags(tag, id, label, GroupFlags::NONE) + } + pub const fn program_feature_group_with_flags(self: Self, tag: u16, id: u32, label: &str, flags: GroupFlags) -> Self { + self.bi_named_group(Self::BINARY_INFO_TAG_RASPBERRY_PI, Self::BINARY_INFO_ID_RP_PROGRAM_FEATURE, + flags as u16, tag, id, label) + } + + pub const fn pins_with_func(self: Self, func: u8, pins: &[u8]) -> Self { + assert!(func < 16); + assert!(pins.len() > 0 && pins.len() < 6); + + const BI_PINS_ENCODING_MULTI: u32 = 2; + let encoded = if pins.len() == 1 { + BI_PINS_ENCODING_MULTI | ((func as u32) << 3) | ((pins[0] as u32) << 7) | ((pins[0] as u32) << 12) + } else if pins.len() == 2 { + BI_PINS_ENCODING_MULTI | ((func as u32) << 3) | ((pins[0] as u32) << 7) | ((pins[1] as u32) << 12) | ((pins[1] as u32) << 17) + } else if pins.len() == 3 { + BI_PINS_ENCODING_MULTI | ((func as u32) << 3) | ((pins[0] as u32) << 7) | ((pins[1] as u32) << 12) | ((pins[2] as u32) << 17) | ((pins[2] as u32) << 22) + } else if pins.len() == 4 { + BI_PINS_ENCODING_MULTI | ((func as u32) << 3) | ((pins[0] as u32) << 7) | ((pins[1] as u32) << 12) | ((pins[2] as u32) << 17) | ((pins[3] as u32) << 22) | ((pins[3] as u32) << 27) + } else if pins.len() == 5 { + BI_PINS_ENCODING_MULTI | ((func as u32) << 3) | ((pins[0] as u32) << 7) | ((pins[1] as u32) << 12) | ((pins[2] as u32) << 17) | ((pins[3] as u32) << 22) | ((pins[4] as u32) << 27) + } else { + assert!(false); + 0 + }; + + self.bi_encoded_pins_with_func(encoded) + } + + pub const fn pin_range_with_func(self: Self, func: u8, pin_range: (u8, u8)) -> Self { + assert!(func < 16); + let (plo, phi) = pin_range; + + const BI_PINS_ENCODING_RANGE: u32 = 1; + let encoded = BI_PINS_ENCODING_RANGE | ((func as u32) << 3) | ((plo as u32) << 7) | ((phi as u32) << 12); + + self.bi_encoded_pins_with_func(encoded) + } + + pub const fn pins_with_names(mut self: Self, pins: &[(u8, &str)]) -> Self { + const fn collect_mask(mask: u32, pins: &[(u8, &str)], offset: usize) -> u32 { + if offset >= pins.len() { + mask + } else { + collect_mask(mask | (1 << pins[offset].0), pins, offset+1) + } + } + self = self.bi_pins_with_names(collect_mask(0, pins, 0), pins[0].1); + + const fn append_names(acc: ProgramInfoBuilder, pins: &[(u8, &str)], offset: usize) -> ProgramInfoBuilder { + if offset >= pins.len() { + acc + } else { + append_names(acc.append_to_last_string("|").append_to_last_string(pins[offset].1), pins, offset+1) + } + } + append_names(self, pins, 1) + } }