mcan/
lib.rs

1#![no_std]
2#![warn(missing_docs)]
3//! # MCAN
4//!
5//! ## Overview
6//! This crate provides a platform-agnostic CAN HAL.
7//!
8//! It provides the following features:
9//!
10//! - classical CAN and CAN FD with bitrate switching support
11//! - safe Message RAM layouting system via [`SharedMemory`] that prevents
12//!   misconfiguration through compile-time checks
13//! - modular interrupt handling abstractions that enable lock-less usage of HW
14//!   interrupt lines
15//! - message transmission using dedicated buffers, FIFO and priority queue
16//! - message transmission cancellation
17//! - message reception using dedicated buffers and two FIFOs
18//! - filter settings
19//!
20//! MCAN is embedded in the MCU like all other peripherals. The interface
21//! between them includes two clock signal lines, two HW interrupt lines, a
22//! single memory-mapped HW register and a dedicated, shared RAM memory region
23//! (referred to as Message RAM) that both CPU and MCAN can access and share
24//! information through.
25//!
26//! For the MCAN abstractions to be considered operational, this interface has
27//! to be properly configured. The latter is assured through the safety
28//! requirements of [`mcan_core`] traits which platform-specific HALs are
29//! expected to implement.
30//!
31//! In order to use MCAN, one has to instantiate [`CanConfigurable`] and
32//! [`finalize`] it. Its constructor requires an instance of an
33//! [`Dependencies`] implementing struct and holds onto it until it's
34//! [`released`]. Safety requirements of the `Dependencies` trait
35//! guarantee a correct state of MCAN interfaces during its operation.
36//!
37//! ## Message RAM Configuration
38//!
39//! All the platform specific details are covered by
40//! `Dependencies` **apart from** the Message RAM configuration.
41//! This is because it is hard to enclose all the safety requirements for the
42//! shared message RAM on a platform-specific HAL level.
43//!
44//! The MCAN uses 16-bit addressing internally. It means that all higher bytes
45//! of the addressing has to be configured externally to the MCAN HAL.
46//! [`Dependencies::eligible_message_ram_start`] implemented by
47//! platform-specific HAL provides a way to `mcan` to verify if the memory
48//! region provided by a user is sound; yet it is up to the user to put it in a
49//! valid, accessible to MCAN, RAM memory region.
50//!
51//! One can configure the Message RAM as follows
52//! - specify a custom `MEMORY` entry in a linker script mapped to the valid RAM
53//!   memory region
54//! - introduce a custom, `.bss` like (`NOLOAD` property), section - eg. `.can`
55//! - map the input section to the `MEMORY` entry
56//! - use the `#[link_section]` attribute in a code to link a static variable to
57//!   this memory region
58//!
59//! Example of a linker script
60//! ```text
61//! MEMORY
62//! {
63//!   FLASH : ORIGIN = 0x400000, LENGTH = 2M
64//!   CAN : ORIGIN = 0x20400000, LENGTH = 64K
65//!   RAM : ORIGIN = 0x20410000, LENGTH = 192K
66//! }
67//!
68//! SECTIONS {
69//!   .can (NOLOAD) :
70//!   {
71//!     *(.can .can.*);
72//!   } > CAN
73//! }
74//! ```
75//!
76//! Code example
77//!
78//! ```no_run
79//! use mcan::generic_array::typenum::consts::*;
80//! use mcan::messageram::SharedMemory;
81//! use mcan::message::{tx, rx};
82//! use mcan::prelude::*;
83//! struct Capacities;
84//! impl mcan::messageram::Capacities for Capacities {
85//!     type StandardFilters = U128;
86//!     type ExtendedFilters = U64;
87//!     type RxBufferMessage = rx::Message<64>;
88//!     type DedicatedRxBuffers = U64;
89//!     type RxFifo0Message = rx::Message<64>;
90//!     type RxFifo0 = U64;
91//!     type RxFifo1Message = rx::Message<64>;
92//!     type RxFifo1 = U64;
93//!     type TxMessage = tx::Message<64>;
94//!     type TxBuffers = U32;
95//!     type DedicatedTxBuffers = U0;
96//!     type TxEventFifo = U32;
97//! }
98//!
99//! #[link_section = ".can"]
100//! static mut MESSAGE_RAM: SharedMemory<Capacities> = SharedMemory::new();
101//! ```
102//!
103//! When it comes to the [`RTIC`] framework, suggested way of setting the shared
104//! memory up would be to use task-local resource in an `init` task. Reference
105//! to a task-local resource in an `init` has a static lifetime which is
106//! suitable for configuring MCAN and returning it from `init`. It allows a user
107//! to avoid the unsafe memory access to a static variable.
108//!
109//! ```ignore
110//! #[rtic::app(device = hal::pac, peripherals = true, dispatchers = [SOME_DISPATCHER])]
111//! mod app {
112//!    #[init(local = [
113//!        #[link_section = ".can"]
114//!        message_ram: SharedMemory<Capacities> = SharedMemory::new()
115//!    ])]
116//!    fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) {
117//!        // `ctx.local.message_ram` is a reference with a static lifetime
118//!        // ...
119//!    }
120//! }
121//! ```
122//!
123//! ## General usage example
124//!
125//! In order to use the MCAN abstractions one shall
126//! - instantiate an `Dependencies` implementing struct
127//! - setup the Message RAM
128//!     - implement [`Capacities`] trait on a marker type
129//!     - allocate the memory via [`SharedMemory`] type
130//!
131//! ```no_run
132//! # use mcan::generic_array::typenum::consts::*;
133//! # use mcan::messageram::SharedMemory;
134//! # use mcan::message::{tx, rx};
135//! # use mcan::prelude::*;
136//! # use fugit::RateExtU32 as _;
137//! # struct Capacities;
138//! # impl mcan::messageram::Capacities for Capacities {
139//! #     type StandardFilters = U128;
140//! #     type ExtendedFilters = U64;
141//! #     type RxBufferMessage = rx::Message<64>;
142//! #     type DedicatedRxBuffers = U64;
143//! #     type RxFifo0Message = rx::Message<64>;
144//! #     type RxFifo0 = U64;
145//! #     type RxFifo1Message = rx::Message<64>;
146//! #     type RxFifo1 = U64;
147//! #     type TxMessage = tx::Message<64>;
148//! #     type TxBuffers = U32;
149//! #     type DedicatedTxBuffers = U0;
150//! #     type TxEventFifo = U32;
151//! # }
152//! # #[link_section = ".can"]
153//! # static mut MESSAGE_RAM: SharedMemory<Capacities> = SharedMemory::new();
154//! # struct Can0;
155//! # unsafe impl mcan::core::CanId for Can0 {
156//! #     const ADDRESS: *const () = 0xDEAD0000 as *const _;
157//! # }
158//! # pub mod hal {
159//! #     pub mod can {
160//! #         pub struct Dependencies(());
161//! #         unsafe impl<ID: mcan::core::CanId> mcan::core::Dependencies<ID> for Dependencies {
162//! #             fn eligible_message_ram_start(&self) -> *const () { unreachable!() }
163//! #             fn host_clock(&self) -> fugit::HertzU32 { unreachable!() }
164//! #             fn can_clock(&self) -> fugit::HertzU32 { unreachable!() }
165//! #         }
166//! #         impl Dependencies {
167//! #             pub fn new() -> Result<Dependencies, ()> {
168//! #                 Ok(Dependencies(()))
169//! #             }
170//! #         }
171//! #     }
172//! # }
173//! use mcan::config::{BitTiming, Mode};
174//! use mcan::interrupt::{Interrupt, InterruptLine};
175//! use mcan::filter::{Action, Filter, ExtFilter};
176//! use mcan::embedded_can as ecan;
177//!
178//! let dependencies = hal::can::Dependencies::new(/* all required parameters */).unwrap();
179//! let mut can = mcan::bus::CanConfigurable::<'_, Can0, _, _>::new(
180//!     500.kHz(),
181//!     dependencies,
182//!     unsafe { &mut MESSAGE_RAM }
183//! ).unwrap();
184//!
185//! // MCAN is still disabled and user can access and modify the underlying
186//! // config struct. More information can be found in `mcan::config` module.
187//! can.config().mode = Mode::Fd {
188//!     allow_bit_rate_switching: true,
189//!     data_phase_timing: BitTiming::new(1.MHz()),
190//! };
191//!
192//! // Example interrupt configuration
193//! let interrupts_to_be_enabled = can
194//!     .interrupts()
195//!     .split(
196//!         [
197//!             Interrupt::RxFifo0NewMessage,
198//!             Interrupt::RxFifo0Full,
199//!             Interrupt::RxFifo0MessageLost,
200//!         ]
201//!         .into_iter()
202//!         .collect(),
203//!     )
204//!     .unwrap();
205//! let line_0_interrupts = can
206//!     .interrupt_configuration()
207//!     .enable_line_0(interrupts_to_be_enabled);
208//!
209//! let interrupts_to_be_enabled = can
210//!     .interrupts()
211//!     .split(
212//!         [
213//!             Interrupt::RxFifo1NewMessage,
214//!             Interrupt::RxFifo1Full,
215//!             Interrupt::RxFifo1MessageLost,
216//!         ]
217//!         .into_iter()
218//!         .collect(),
219//!     )
220//!     .unwrap();
221//! let line_1_interrupts = can
222//!     .interrupt_configuration()
223//!     .enable_line_1(interrupts_to_be_enabled);
224//!
225//! // Example filters configuration
226//! // This filter will put all messages with a standard ID into RxFifo0
227//! can.filters_standard()
228//!     .push(Filter::Classic {
229//!         action: Action::StoreFifo0,
230//!         filter: ecan::StandardId::MAX,
231//!         mask: ecan::StandardId::ZERO,
232//!     })
233//!     .unwrap_or_else(|_| panic!("Standard filter application failed"));
234//!
235//! // This filter will put all messages with a extended ID into RxFifo1
236//! can.filters_extended()
237//!     .push(ExtFilter::Classic {
238//!         action: Action::StoreFifo1,
239//!         filter: ecan::ExtendedId::MAX,
240//!         mask: ecan::ExtendedId::ZERO,
241//!     })
242//!     .unwrap_or_else(|_| panic!("Extended filter application failed"));
243//!
244//! // Call to `finalize` puts MCAN into operational mode
245//! let can = can.finalize().unwrap();
246//!
247//! // `can` object can be split into independent pieces
248//! let rx_fifo_0 = can.rx_fifo_0;
249//! let rx_fifo_1 = can.rx_fifo_1;
250//! let tx = can.tx;
251//! let tx_event_fifo = can.tx_event_fifo;
252//! let aux = can.aux;
253//! ```
254//!
255//! [`RTIC`]: https://rtic.rs
256//! [`CanConfigurable`]: crate::bus::CanConfigurable
257//! [`finalize`]: crate::bus::CanConfigurable::finalize
258//! [`released`]: crate::bus::Can::release
259//! [`Dependencies`]: mcan_core::Dependencies
260//! [`Dependencies::eligible_message_ram_start`]: mcan_core::Dependencies::eligible_message_ram_start
261//! [`Capacities`]: crate::messageram::Capacities
262//! [`SharedMemory`]: crate::messageram::SharedMemory
263
264pub mod bus;
265pub mod config;
266pub mod filter;
267pub mod interrupt;
268pub mod message;
269pub mod messageram;
270pub mod prelude;
271pub mod reg;
272pub mod rx_dedicated_buffers;
273pub mod rx_fifo;
274pub mod tx_buffers;
275pub mod tx_event_fifo;
276
277pub use embedded_can;
278pub use generic_array;
279pub use mcan_core as core;
280
281// For svd2rust generated code that refers to everything via `crate::...`
282use reg::generic::*;