omnitiles/hw/
can.rs

1// SPDX-License-Identifier: MIT
2// © 2025–2026 Christopher Liu
3
4//! Controller Area Network (CAN) abstraction layer.
5//!
6//! - `CanBus` wraps a HAL `can::Can` instance in `bxcan::Can`.
7//! - Provides simple helpers for sending and receiving frames.
8
9use core::convert::Infallible;
10use nb::block;
11
12use bxcan::{self, Data, Frame, OverrunError, StandardId, TransmitStatus};
13use stm32f7xx_hal::can as hal_can;
14
15/// Wrapper around a bxcan CAN instance built from a HAL CAN peripheral.
16pub struct CanBus<I>
17where
18    hal_can::Can<I>: bxcan::Instance,
19{
20    can: bxcan::Can<hal_can::Can<I>>,
21}
22
23impl<I> CanBus<I>
24where
25    hal_can::Can<I>: bxcan::Instance,
26{
27    /// Create and enable a bxcan instance from a HAL CAN peripheral.
28    ///
29    /// * `hal_can` – the HAL CAN wrapper
30    /// * `btr` – value for the CAN_BTR register (bit timing). Get this from the
31    ///           reference manual or the bxcan timing tables.
32    /// * `loopback` – enable internal loopback
33    /// * `silent` – enable silent mode
34    pub fn new(hal_can: hal_can::Can<I>, btr: u32, loopback: bool, silent: bool) -> Self {
35        let can = bxcan::Can::builder(hal_can)
36            .set_bit_timing(btr)
37            .set_loopback(loopback)
38            .set_silent(silent)
39            .enable();
40
41        Self { can }
42    }
43
44    /// Access the underlying bxcan instance for advanced configuration.
45    pub fn inner(&mut self) -> &mut bxcan::Can<hal_can::Can<I>> {
46        &mut self.can
47    }
48
49    /// Consume the wrapper and get back the underlying HAL CAN instance.
50    pub fn free(self) -> hal_can::Can<I> {
51        self.can.free()
52    }
53
54    /// Transmit a pre-built CAN frame.
55    ///
56    /// Returns the bxcan `TransmitStatus`. The error type is `Infallible`.
57    pub fn transmit_frame(&mut self, frame: &Frame) -> Result<TransmitStatus, Infallible> {
58        block!(self.can.transmit(frame))
59    }
60
61    /// Transmit a data frame with a standard 11-bit ID.
62    ///
63    /// `data` must be at most 8 bytes, otherwise this returns `None`.
64    pub fn transmit_data(
65        &mut self,
66        id: StandardId,
67        data: &[u8],
68    ) -> Option<Result<TransmitStatus, Infallible>> {
69        let data = Data::new(data)?;
70        let frame = Frame::new_data(id, data);
71        Some(block!(self.can.transmit(&frame)))
72    }
73
74    /// Blocking receive of a frame.
75    ///
76    /// This will block inside bxcan until a frame is available or an overrun is detected.
77    pub fn receive(&mut self) -> Result<Frame, OverrunError> {
78        block!(self.can.receive())
79    }
80}
81
82/// Extra helpers for CAN instances that own filters (e.g., CAN1 on STM32F7).
83impl<I> CanBus<I>
84where
85    hal_can::Can<I>: bxcan::Instance + bxcan::FilterOwner,
86{
87    /// Configure CAN1 and CAN2 filters so that both accept all frames on FIFO0.
88    ///
89    /// This must be called on CAN1 (the filter owner).
90    pub fn configure_accept_all_filters_for_dual_can<I2>(&mut self, _can2: &mut hal_can::Can<I2>)
91    where
92        hal_can::Can<I2>: bxcan::Instance,
93    {
94        let regs = unsafe { &*stm32f7xx_hal::pac::CAN1::ptr() };
95
96        // Enter filter init mode
97        regs.fmr.modify(|_, w| w.finit().set_bit());
98
99        // Set CAN2SB = split point. F7 has 28 filter banks (0-13 for CAN1, 14-27 for CAN2).
100        regs.fmr.modify(|_, w| unsafe { w.can2sb().bits(14) });
101
102        // Clear all filter banks (disable all)
103        regs.fa1r.reset();
104        regs.fm1r.reset();
105        regs.fs1r.reset();
106        regs.ffa1r.reset();
107
108        // Set filter scale to 32-bit and mask mode for all banks
109        regs.fs1r.modify(|_, w| unsafe { w.bits(0x0FFF_FFFF) });
110        regs.fm1r.modify(|_, w| unsafe { w.bits(0x0000_0000) });
111
112        // FIFO assignment -> FIFO0 for all banks
113        regs.ffa1r.modify(|_, w| unsafe { w.bits(0x0000_0000) });
114
115        // Set FR1/FR2 = 0 for accept-all
116        // CAN1 bank 0
117        regs.fb[0].fr1.write(|w| unsafe { w.bits(0) });
118        regs.fb[0].fr2.write(|w| unsafe { w.bits(0) });
119
120        // CAN2 bank 14
121        regs.fb[14].fr1.write(|w| unsafe { w.bits(0) });
122        regs.fb[14].fr2.write(|w| unsafe { w.bits(0) });
123
124        // Activate bank 0 (CAN1) and bank 14 (CAN2)
125        regs.fa1r
126            .modify(|r, w| unsafe { w.bits(r.bits() | ((1 << 0) | (1 << 14))) });
127
128        // Leave filter init mode (FINIT = 0)
129        regs.fmr.modify(|_, w| w.finit().clear_bit());
130    }
131}