| Crates.io | st-mem-bank-macro |
| lib.rs | st-mem-bank-macro |
| version | 1.0.0 |
| created_at | 2025-07-31 09:37:24.874256+00 |
| updated_at | 2025-07-31 09:37:24.874256+00 |
| description | Simplifies memory state management for sensors with multiple memory banks by providing useful macros. |
| homepage | |
| repository | https://github.com/STMicroelectronics/st-mems-rust-drivers/tree/main/util/st-mem-bank-macro |
| max_upload_size | |
| id | 1774761 |
| size | 54,678 |
This crate simplifies implementing memory state management for sensors. Some sensors have a register (called MemBank) that can change the address space, enabling access on special registers while restricting access to others during that state. Additionally, it streamlines register access over communication buses such as I2C or SPI.
mem_bank MacroThe mem_bank macro is applied to an enum representing the sensor's memory states. It uses annotations to provide metadata about each state.
#[main]: Marks the default (main) state.#[state(StructName, fn_name = "...")]: For other states, specifies:
StructName: A struct representing the state (used internally).fn_name: A function is generate with that name, it handles the state transition via a closure. The closure moves into the specified state and then returns to the main state.Annotate the entire enum as:
#[mem_bank(SensorName, generics = N)]
SensorName: This is a link to the struct that represent the main sensor lib.
'generics': Number of generic parameters (1 or 2). The sensor must be compatible, typically including a BusOperation trait (the bus crate) and optionally a DelayNs trait from embedded_hal.
Note: The fn_name closure wraps all errors. If an error occurs during the clousure execution a return to the main MemBank is tried, if fails the error returned in the MemBank, it takes precedence over others.
The register macro works alongside the bitfield-struct crate. The sensor must implement these two functions:
pub fn write_to_register(&mut self, reg: u8, buf: &[u8]) -> Result<(), Error<B::Error>> {
self.bus.write_to_register(reg, buf).map_err(Error::Bus)
}
pub fn read_from_register(&mut self, reg: u8, buf: &mut [u8]) -> Result<(), Error<B::Error>> {
self.bus.read_from_register(reg, buf).map_err(Error::Bus)
}
Suggested implementation considers an attribute (like the bus in the example) that is generic over the BusOperation trait, as it already provides default implementations for write_to_register and read_from_register.
Annotate a struct representing a register with: #[register(address = ..., access_type = ..., generics = ...)]
All three attributes are mandatory:
mem_bank state.mem_bank macro.Example:
#[register(address = Reg::Ctrl, access_type = Lsm6dsv16x, generics = 2)]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct Ctrl {
#[bits(1)]
pub feature1: bool,
...
}
Using the register in sensor code:
impl<B, T> Lsm6dsv16x<B, T> where B: BusOperation, T: DelayNs {
pub fn activate_feature1(&mut self) -> Result<(), Error> {
let mut reg = Ctrl::read(self)?;
reg.set_feature1(true); // Generated by bitfield-struct
Ctrl::write(self)
}
}
Some edge uses cases are covered, for example:
#[register(address = Reg::OutxLG, access_type = Lsm6dsv16x, generics = 2)]
pub struct OutXYZG(pub [i16; 3]);
#[named_register(address = Reg::OutxLG, access_type = Lsm6dsv16x, generics = 2)]
pub struct OutXYZ {
pub x: i16,
pub y: i16,
pub z: i16
}
#[register(address = EmbReg::FsmOuts1, access_type = EmbedFuncState, init_fn = FsmOutsElement::new, generics = 2)]
pub struct FsmOut(pub [FsmOutsElement; 8]);
#[register(address = EmbReg::FsmOuts1, access_type = EmbedFuncState, generics = 2)]
#[cfg_attr(feature = "bit_order_msb", bitfield(u8, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u8, order = Lsb))]
pub struct FsmOutsElement {
#[bits(1)]
pub fsm_n_v: u8,
...
}
impl FsmOutsElement {
pub fn from_le_bytes(val: [u8; 1]) -> Self {
FsmOutsElement(val[0])
}
pub fn to_le_bytes(&self) -> [u8; 1] {
[self.0]
}
}
order = Reverse parameter could be supplied to reverse how registers are read or write.#[register(address = Reg::OutXL, access_type = Lsm6dsv16x, generics = 2)]
#[cfg_attr(feature = "bit_order_msb", bitfield(u16, order = Msb))]
#[cfg_attr(not(feature = "bit_order_msb"), bitfield(u16, order = Lsb))]
pub struct OutX {
#[bits(16, access = RO)]
pub outx: i16,
}
This macro is equivalent to register and supports the same attributes, with the addition of a required base_address attribute that specifies the base page of embedded advanced functions. In this case, reads and writes are performed using ln_pg_write and ln_pg_read methods. It is recommended to implement the EmbAdvFunctions trait from the bus crate to support this functionality.
An example is:
#[adv_register(base_address = AdvPage::_0, address = EmbAdv0Reg::SflpGameGbiasxL, access_type = Lsm6dsv16x, generics = 2)]
pub struct SflpGameGbiasXYZ(pub [u16; 3]);