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}