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::Peripherals;
36use pac::sercom0;
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
54pub mod uart;
55
56#[cfg(feature = "dma")]
57pub mod dma;
58
59//==============================================================================
60//  Sercom
61//==============================================================================
62
63/// Type-level `enum` representing a Serial Communication Interface (SERCOM)
64pub trait Sercom: Sealed + Deref<Target = sercom0::RegisterBlock> {
65    /// SERCOM number
66    const NUM: usize;
67
68    /// RX Trigger source for DMA transactions
69    #[cfg(feature = "dma")]
70    const DMA_RX_TRIGGER: TriggerSource;
71
72    /// TX trigger source for DMA transactions
73    #[cfg(feature = "dma")]
74    const DMA_TX_TRIGGER: TriggerSource;
75
76    #[cfg(feature = "async")]
77    type Interrupt: crate::async_hal::interrupts::InterruptSource;
78
79    /// Enable the corresponding APB clock
80    fn enable_apb_clock(&mut self, ctrl: &ApbClkCtrl);
81
82    /// Get a reference to the sercom from a
83    /// [`Peripherals`] block
84    fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::sercom0::RegisterBlock;
85
86    /// Get a reference to this [`Sercom`]'s associated RX Waker
87    #[cfg(feature = "async")]
88    #[inline]
89    fn rx_waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
90        &crate::sercom::async_api::RX_WAKERS[Self::NUM]
91    }
92
93    /// Get a reference to this [`Sercom`]'s associated TX Waker
94    #[cfg(feature = "async")]
95    #[inline]
96    fn tx_waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
97        &crate::sercom::async_api::TX_WAKERS[Self::NUM]
98    }
99}
100
101macro_rules! sercom {
102    ( $apbmask:ident, $N:expr) => {
103        paste::paste! {
104            // use pac::$pac_type;
105            /// Type alias for the corresponding SERCOM instance
106            pub type [< Sercom $N >] = $crate::pac::[< Sercom $N >];
107            impl Sealed for [< Sercom $N >] {}
108            impl Sercom for [< Sercom $N >] {
109                const NUM: usize = $N;
110
111                #[cfg(feature = "dma")]
112                const DMA_RX_TRIGGER: TriggerSource = TriggerSource::[< Sercom $N Rx >];
113
114                #[cfg(feature = "dma")]
115                const DMA_TX_TRIGGER: TriggerSource = TriggerSource::[< Sercom $N Tx >];
116
117                #[cfg(feature = "async")]
118                #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
119                type Interrupt = $crate::async_hal::interrupts::[< SERCOM $N >];
120
121                #[cfg(feature = "async")]
122                #[hal_cfg("sercom0-d5x")]
123                type Interrupt = $crate::async_hal::interrupts::[< SERCOM $N >];
124
125                #[inline]
126                fn enable_apb_clock(&mut self, ctrl: &ApbClkCtrl) {
127                    ctrl.$apbmask().modify(|_, w| w.[< sercom $N _>]().set_bit());
128                }
129
130                #[inline]
131                fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::sercom0::RegisterBlock {
132                    &*peripherals.[< sercom $N >]
133                }
134            }
135
136
137        }
138    };
139}
140
141// d11 and d21 families
142#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
143sercom!(apbcmask, 0);
144
145#[hal_cfg(any("sercom1-d11", "sercom1-d21"))]
146sercom!(apbcmask, 1);
147
148#[hal_cfg(any("sercom2-d11", "sercom2-d21"))]
149sercom!(apbcmask, 2);
150
151#[hal_cfg("sercom3-d21")]
152sercom!(apbcmask, 3);
153
154#[hal_cfg("sercom4-d21")]
155sercom!(apbcmask, 4);
156
157#[hal_cfg("sercom5-d21")]
158sercom!(apbcmask, 5);
159
160// d5x family
161#[hal_cfg("sercom0-d5x")]
162sercom!(apbamask, 0);
163
164#[hal_cfg("sercom1-d5x")]
165sercom!(apbamask, 1);
166
167#[hal_cfg("sercom2-d5x")]
168sercom!(apbbmask, 2);
169
170#[hal_cfg("sercom3-d5x")]
171sercom!(apbbmask, 3);
172
173#[hal_cfg("sercom4-d5x")]
174sercom!(apbdmask, 4);
175
176#[hal_cfg("sercom5-d5x")]
177sercom!(apbdmask, 5);
178
179#[hal_cfg("sercom6-d5x")]
180sercom!(apbdmask, 6);
181
182#[hal_cfg("sercom7-d5x")]
183sercom!(apbdmask, 7);
184
185// Reserve space for the max number of SERCOM peripherals based on chip type,
186// even though some wakers may not be used on some chips if they actually don't
187// exist on variant's hardware
188#[hal_cfg("sercom0-d11")]
189#[cfg(feature = "async")]
190const NUM_SERCOM: usize = 3;
191
192#[hal_cfg("sercom0-d21")]
193#[cfg(feature = "async")]
194const NUM_SERCOM: usize = 6;
195
196#[hal_cfg("sercom0-d5x")]
197#[cfg(feature = "async")]
198const NUM_SERCOM: usize = 8;
199
200#[cfg(feature = "async")]
201pub(super) mod async_api {
202    use embassy_sync::waitqueue::AtomicWaker;
203
204    #[allow(clippy::declare_interior_mutable_const)]
205    const NEW_WAKER: AtomicWaker = AtomicWaker::new();
206    /// Waker for a RX event. By convention, if a SERCOM has only one type of
207    /// event (ie, I2C), this the waker to be used.
208    pub(super) static RX_WAKERS: [AtomicWaker; super::NUM_SERCOM] = [NEW_WAKER; super::NUM_SERCOM];
209    /// Waker for a TX event.
210    pub(super) static TX_WAKERS: [AtomicWaker; super::NUM_SERCOM] = [NEW_WAKER; super::NUM_SERCOM];
211}