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::*;