cangen/
lib.rs

1#![no_std]
2
3use bitfield_struct::bitfield;
4use cangen_macro::generate_all_messages;
5use embedded_can::{ExtendedId, Frame, Id, StandardId};
6
7mod sealed {
8    pub trait Sealed {}
9    impl Sealed for u8 {}
10    impl Sealed for u16 {}
11    impl Sealed for u32 {}
12    impl Sealed for u64 {}
13}
14
15pub trait CanRepr: sealed::Sealed + Copy {
16    type Bytes: AsRef<[u8]>;
17    fn to_le_bytes(self) -> Self::Bytes;
18}
19
20impl CanRepr for u8 {
21    type Bytes = [u8; 1];
22    fn to_le_bytes(self) -> Self::Bytes {
23        u8::to_le_bytes(self)
24    }
25}
26impl CanRepr for u16 {
27    type Bytes = [u8; 2];
28    fn to_le_bytes(self) -> Self::Bytes {
29        u16::to_le_bytes(self)
30    }
31}
32impl CanRepr for u32 {
33    type Bytes = [u8; 4];
34    fn to_le_bytes(self) -> Self::Bytes {
35        u32::to_le_bytes(self)
36    }
37}
38impl CanRepr for u64 {
39    type Bytes = [u8; 8];
40    fn to_le_bytes(self) -> Self::Bytes {
41        u64::to_le_bytes(self)
42    }
43}
44
45/// Creates a CAN frame
46/// All the users have to do is specify `Repr`, `ID`, and `LEN`
47pub trait ToCanFrame: Sized + Into<Self::Repr> {
48    type Repr: CanRepr;
49
50    const ID: Id;
51    const LEN: usize;
52
53    #[doc(hidden)]
54    /// a hack to ensure that the `Repr` is specified correctly
55    const CHECK_BITS_FIT: () = assert!(
56        Self::LEN <= core::mem::size_of::<Self::Repr>(),
57        "BITS exceeds the backing integer's width"
58    );
59
60    #[doc(hidden)]
61    /// a hack to ensure that the `Repr` and `LEN` are 8 bytes or less (CAN 2.0)
62    const CHECK_LEN: () = assert!(
63        Self::LEN <= 8 && core::mem::size_of::<Self::Repr>() <= 8,
64        "BITS exceeds the backing integer's width"
65    );
66
67    fn to_can_frame<F: Frame>(self) -> F {
68        let bytes = self.into().to_le_bytes();
69        // this is guarranteed in bounds by the `CHECK_BITS_FIT`
70        // SAFETY: this is guarranteed to be within the size of a CAN frame by `CHECK_LEN`
71        unsafe { F::new(Self::ID, &bytes.as_ref()[..Self::LEN]).unwrap_unchecked() }
72    }
73}
74
75/// Error returned by the generated `try_with_*` / `try_set_*` accessors when a
76/// physical value can't be represented in a scaled field's bit width.
77///
78/// The plain `with_*` / `set_*` accessors saturate such values instead of
79/// failing; use the `try_*` variants when you need to detect the condition.
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub struct OutOfRange {
82    /// The field (snake_case accessor name) that rejected the value.
83    pub field: &'static str,
84}
85
86/// Scaled fixed-point conversion helpers used by the generated bitfields.
87///
88/// Every generated numeric field exposes the *physical* value as an `f32`
89/// (the Rust equivalent of the C `float` the definitions use), while the
90/// bitfield stores a raw `N`-bit integer. These `const` functions apply the
91/// divisor / multiplier from the JSON `formatter` spec and, for signed
92/// fields, sign-extend the stored bits.
93///
94/// They are referenced by the `#[bits(N, from = .., into = ..)]` attributes
95/// that `generate_all_messages!` emits, e.g.
96/// `#[bits(12, from = conv::div_from_u16::<12, 10>, into = conv::div_into_u16::<12, 10>)]`.
97///
98/// - `B` is the field width in bits (used for masking / sign-extension only).
99/// - `ARG` is the formatter argument (the divisor or multiplier).
100pub mod conv {
101    macro_rules! scaled {
102        ($u:ty, $i:ty, $w:literal,
103         $div_from:ident, $div_into:ident, $mul_from:ident, $mul_into:ident,
104         $sdiv_from:ident, $sdiv_into:ident, $smul_from:ident, $smul_into:ident) => {
105            /// unsigned, divide: physical = raw / ARG
106            pub const fn $div_from<const B: u32, const ARG: u32>(bits: $u) -> f32 {
107                bits as f32 / ARG as f32
108            }
109            /// The clamped result is always within the `#[bits(B)]`
110            /// range, so `bitfield_struct`'s bounds check never fires.
111            /// This is why a custom try is needed
112            pub const fn $div_into<const B: u32, const ARG: u32>(v: f32) -> $u {
113                let hi = (<$u>::MAX >> ($w - B)) as f32;
114                let raw = v * ARG as f32;
115                (if raw < 0.0 {
116                    0.0
117                } else if raw > hi {
118                    hi
119                } else {
120                    raw
121                }) as $u
122            }
123            /// unsigned, multiply: physical = raw * ARG
124            pub const fn $mul_from<const B: u32, const ARG: u32>(bits: $u) -> f32 {
125                bits as f32 * ARG as f32
126            }
127            /// Saturates the raw count to `[0, 2^B - 1]` (see `$div_into`).
128            pub const fn $mul_into<const B: u32, const ARG: u32>(v: f32) -> $u {
129                let hi = (<$u>::MAX >> ($w - B)) as f32;
130                let raw = v / ARG as f32;
131                (if raw < 0.0 {
132                    0.0
133                } else if raw > hi {
134                    hi
135                } else {
136                    raw
137                }) as $u
138            }
139            /// signed, divide: sign-extended over B bits on read.
140            pub const fn $sdiv_from<const B: u32, const ARG: u32>(bits: $u) -> f32 {
141                let sh = $w - B;
142                let s = ((bits << sh) as $i) >> sh;
143                s as f32 / ARG as f32
144            }
145            /// Saturates the raw count to `[-2^(B-1), 2^(B-1) - 1]`, then masks
146            /// to B bits so the two's-complement pattern fits the field.
147            pub const fn $sdiv_into<const B: u32, const ARG: u32>(v: f32) -> $u {
148                let half = 1i128 << (B - 1);
149                let hi = (half - 1) as f32;
150                let lo = -(half as f32);
151                let raw = v * ARG as f32;
152                let raw = if raw < lo {
153                    lo
154                } else if raw > hi {
155                    hi
156                } else {
157                    raw
158                };
159                let mask = <$u>::MAX >> ($w - B);
160                ((raw as $i) as $u) & mask
161            }
162            /// signed, multiply: sign-extended over B bits on read.
163            pub const fn $smul_from<const B: u32, const ARG: u32>(bits: $u) -> f32 {
164                let sh = $w - B;
165                let s = ((bits << sh) as $i) >> sh;
166                s as f32 * ARG as f32
167            }
168            /// Saturates the raw count to `[-2^(B-1), 2^(B-1) - 1]` (see `$sdiv_into`).
169            pub const fn $smul_into<const B: u32, const ARG: u32>(v: f32) -> $u {
170                let half = 1i128 << (B - 1);
171                let hi = (half - 1) as f32;
172                let lo = -(half as f32);
173                let raw = v / ARG as f32;
174                let raw = if raw < lo {
175                    lo
176                } else if raw > hi {
177                    hi
178                } else {
179                    raw
180                };
181                let mask = <$u>::MAX >> ($w - B);
182                ((raw as $i) as $u) & mask
183            }
184        };
185    }
186
187    scaled!(
188        u8,
189        i8,
190        8u32,
191        div_from_u8,
192        div_into_u8,
193        mul_from_u8,
194        mul_into_u8,
195        sdiv_from_u8,
196        sdiv_into_u8,
197        smul_from_u8,
198        smul_into_u8
199    );
200    scaled!(
201        u16,
202        i16,
203        16u32,
204        div_from_u16,
205        div_into_u16,
206        mul_from_u16,
207        mul_into_u16,
208        sdiv_from_u16,
209        sdiv_into_u16,
210        smul_from_u16,
211        smul_into_u16
212    );
213    scaled!(
214        u32,
215        i32,
216        32u32,
217        div_from_u32,
218        div_into_u32,
219        mul_from_u32,
220        mul_into_u32,
221        sdiv_from_u32,
222        sdiv_into_u32,
223        smul_from_u32,
224        smul_into_u32
225    );
226    scaled!(
227        u64,
228        i64,
229        64u32,
230        div_from_u64,
231        div_into_u64,
232        mul_from_u64,
233        mul_into_u64,
234        sdiv_from_u64,
235        sdiv_into_u64,
236        smul_from_u64,
237        smul_into_u64
238    );
239}
240
241impl ToCanFrame for ExampleDoNotUse {
242    type Repr = u64;
243    const ID: Id = Id::Standard(StandardId::new(0x00).unwrap());
244    const LEN: usize = 6;
245}
246
247/// Hand-written demo of what `generate_all_messages!` produces: an `f32`
248/// accessor backed by a scaled integer field.
249#[bitfield(u64)]
250struct ExampleDoNotUse {
251    /// `a` is stored in 4 bits and divided by 1000 to obtain the physical value.
252    #[bits(4, from = conv::div_from_u8::<4, 1000>, into = conv::div_into_u8::<4, 1000>)]
253    pub a: f32,
254    pub b: u16,
255    pub c: u16,
256    #[bits(12)]
257    _1: u16,
258    _2: u16,
259}
260
261// entrypoint to macro.  All expanded code must be no_std
262generate_all_messages!();