atsamd_hal/sercom/
mod.rs

1//! # Configure the SERCOM peripherals
2//!
3//! The SERCOM module is used to configure the SERCOM peripherals as USART, SPI
4//! or I2C interfaces.
5//!
6//! # Undocumented features
7//!
8//! The ATSAMx5x chips contain certain features that aren't documented in the
9//! datasheet. These features are implemented in the HAL based on
10//! experimentation with certain boards which have verifiably demonstrated that
11//! those features work as intended. These undocumented features are disabled by
12//! default, and can be enabled by enabling the `undoc-features` Cargo feature
13//!
14//! ## SAMD21:
15//! * `PA00` is I2C-capable according to `circuit_playground_express`. As such,
16//!   `PA00` implements [`IsI2cPad`].
17//!
18//! * `PA01` is I2C-capable according to `circuit_playground_express`. As such,
19//!   PA01 implements [`IsI2cPad`].
20//!
21//! * `PB02` is I2C-capable according to `circuit_playground_express`. As such,
22//!   PB02 implements [`IsI2cPad`].
23//!
24//! * `PB03` is I2C-capable according to `circuit_playground_express`. As such,
25//!   PB03 implements [`IsI2cPad`].
26//!
27//! ## SAMx5x devices:
28//! * `UndocIoSet1`: Implement an undocumented `IoSet` for PA16, PA17, PB22 &
29//!   PB23 configured for [`Sercom1`]. The `pygamer` & `feather_m4` use this
30//!   combination, but it is not listed as valid in the datasheet.
31//!
32//! * `UndocIoSet2`: Implement an undocumented `IoSet` for PA00, PA01, PB22 &
33//!   PB23 configured for [`Sercom1`]. The `itsybitsy_m4` uses this combination,
34//!   but it is not listed as valid in the datasheet.
35//!
36//! * [`PB02`] is I2C-capable according to `metro_m4`. As such, [`PB02`]
37//!   implements [`IsI2cPad`].
38//!
39//! * [`PB03`] is I2C-capable according to `metro_m4`. As such, [`PB03`]
40//!   implements [`IsI2cPad`].
41//!
42//! [`PB02`]: crate::gpio::pin::PB02
43//! [`PB03`]: crate::gpio::pin::PB03
44//! [`IsI2cPad`]: pad::IsI2cPad
45
46use atsamd_hal_macros::hal_cfg;
47
48use core::ops::Deref;
49
50use crate::pac;
51use pac::Peripherals;
52use pac::sercom0;
53
54#[hal_cfg("sercom0-d5x")]
55use pac::Mclk as ApbClkCtrl;
56#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
57use pac::Pm as ApbClkCtrl;
58
59#[cfg(feature = "dma")]
60use crate::dmac::TriggerSource;
61
62use crate::typelevel::Sealed;
63
64pub mod pad;
65pub use pad::*;
66
67pub mod i2c;
68pub mod spi;
69
70pub mod uart;
71
72#[cfg(feature = "dma")]
73pub mod dma;
74
75//==============================================================================
76//  Sercom
77//==============================================================================
78
79/// Type-level `enum` representing a Serial Communication Interface (SERCOM)
80pub trait Sercom: Sealed + Deref<Target = sercom0::RegisterBlock> {
81    /// SERCOM number
82    const NUM: usize;
83
84    /// RX Trigger source for DMA transactions
85    #[cfg(feature = "dma")]
86    const DMA_RX_TRIGGER: TriggerSource;
87
88    /// TX trigger source for DMA transactions
89    #[cfg(feature = "dma")]
90    const DMA_TX_TRIGGER: TriggerSource;
91
92    #[cfg(feature = "async")]
93    type Interrupt: crate::async_hal::interrupts::InterruptSource;
94
95    /// Enable the corresponding APB clock
96    fn enable_apb_clock(&mut self, ctrl: &ApbClkCtrl);
97
98    /// Get a reference to the sercom from a
99    /// [`Peripherals`] block
100    fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::sercom0::RegisterBlock;
101
102    /// Get a reference to this [`Sercom`]'s associated RX Waker
103    #[cfg(feature = "async")]
104    #[inline]
105    fn rx_waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
106        &crate::sercom::async_api::RX_WAKERS[Self::NUM]
107    }
108
109    /// Get a reference to this [`Sercom`]'s associated TX Waker
110    #[cfg(feature = "async")]
111    #[inline]
112    fn tx_waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
113        &crate::sercom::async_api::TX_WAKERS[Self::NUM]
114    }
115}
116
117macro_rules! sercom {
118    ( $apbmask:ident, $N:expr) => {
119        paste::paste! {
120            // use pac::$pac_type;
121            /// Type alias for the corresponding SERCOM instance
122            pub type [< Sercom $N >] = $crate::pac::[< Sercom $N >];
123            impl Sealed for [< Sercom $N >] {}
124            impl Sercom for [< Sercom $N >] {
125                const NUM: usize = $N;
126
127                #[cfg(feature = "dma")]
128                const DMA_RX_TRIGGER: TriggerSource = TriggerSource::[< Sercom $N Rx >];
129
130                #[cfg(feature = "dma")]
131                const DMA_TX_TRIGGER: TriggerSource = TriggerSource::[< Sercom $N Tx >];
132
133                #[cfg(feature = "async")]
134                #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
135                type Interrupt = $crate::async_hal::interrupts::[< SERCOM $N >];
136
137                #[cfg(feature = "async")]
138                #[hal_cfg("sercom0-d5x")]
139                type Interrupt = $crate::async_hal::interrupts::[< SERCOM $N >];
140
141                #[inline]
142                fn enable_apb_clock(&mut self, ctrl: &ApbClkCtrl) {
143                    ctrl.$apbmask().modify(|_, w| w.[< sercom $N _>]().set_bit());
144                }
145
146                #[inline]
147                fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::sercom0::RegisterBlock {
148                    &*peripherals.[< sercom $N >]
149                }
150            }
151
152
153        }
154    };
155}
156
157// d11 and d21 families
158#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
159sercom!(apbcmask, 0);
160
161#[hal_cfg(any("sercom1-d11", "sercom1-d21"))]
162sercom!(apbcmask, 1);
163
164#[hal_cfg(any("sercom2-d11", "sercom2-d21"))]
165sercom!(apbcmask, 2);
166
167#[hal_cfg("sercom3-d21")]
168sercom!(apbcmask, 3);
169
170#[hal_cfg("sercom4-d21")]
171sercom!(apbcmask, 4);
172
173#[hal_cfg("sercom5-d21")]
174sercom!(apbcmask, 5);
175
176// d5x family
177#[hal_cfg("sercom0-d5x")]
178sercom!(apbamask, 0);
179
180#[hal_cfg("sercom1-d5x")]
181sercom!(apbamask, 1);
182
183#[hal_cfg("sercom2-d5x")]
184sercom!(apbbmask, 2);
185
186#[hal_cfg("sercom3-d5x")]
187sercom!(apbbmask, 3);
188
189#[hal_cfg("sercom4-d5x")]
190sercom!(apbdmask, 4);
191
192#[hal_cfg("sercom5-d5x")]
193sercom!(apbdmask, 5);
194
195#[hal_cfg("sercom6-d5x")]
196sercom!(apbdmask, 6);
197
198#[hal_cfg("sercom7-d5x")]
199sercom!(apbdmask, 7);
200
201// Reserve space for the max number of SERCOM peripherals based on chip type,
202// even though some wakers may not be used on some chips if they actually don't
203// exist on variant's hardware
204#[hal_cfg("sercom0-d11")]
205#[cfg(feature = "async")]
206const NUM_SERCOM: usize = 3;
207
208#[hal_cfg("sercom0-d21")]
209#[cfg(feature = "async")]
210const NUM_SERCOM: usize = 6;
211
212#[hal_cfg("sercom0-d5x")]
213#[cfg(feature = "async")]
214const NUM_SERCOM: usize = 8;
215
216#[cfg(feature = "async")]
217pub(super) mod async_api {
218    use embassy_sync::waitqueue::AtomicWaker;
219
220    #[allow(clippy::declare_interior_mutable_const)]
221    const NEW_WAKER: AtomicWaker = AtomicWaker::new();
222    /// Waker for a RX event. By convention, if a SERCOM has only one type of
223    /// event (ie, I2C), this the waker to be used.
224    pub(super) static RX_WAKERS: [AtomicWaker; super::NUM_SERCOM] = [NEW_WAKER; super::NUM_SERCOM];
225    /// Waker for a TX event.
226    pub(super) static TX_WAKERS: [AtomicWaker; super::NUM_SERCOM] = [NEW_WAKER; super::NUM_SERCOM];
227}