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