atsamd_hal/sercom/
i2c.rs

1//! Use the SERCOM peripheral for I2C communications
2//!
3//! Configuring an I2C peripheral occurs in three steps. First, you must create
4//! a set of [`Pads`] for use by the peripheral. Next, you assemble pieces into
5//! a [`Config`] struct. After configuring the peripheral, you then [`enable`]
6//! it, yielding a functional [`I2c`] struct. Transactions are performed using
7//! traits from embedded-hal ([v1.0](crate::ehal) and blocking traits from
8//! [v0.2](`crate::ehal_02`)).
9//!
10//! # [`Pads`]
11//!
12//! A [`Sercom`] uses two [`Pin`]s as peripheral [`Pad`]s, but only certain
13//! [`Pin`] combinations are acceptable. All [`Pin`]s must be mapped to the same
14//! [`Sercom`], and SDA is always [`Pad0`], while SCL is always [`Pad1`] (see
15//! the datasheet). SAMx5x chips have an additional constraint that [`Pin`]s
16//! must be in the same [`IoSet`].  This HAL makes it impossible to use invalid
17//! [`Pin`]/[`Pad`] combinations, and the [`Pads`] struct is responsible for
18//! enforcing these constraints.
19//!
20//! A [`Pads`] type takes three type parameters. The first type specifies the
21//! [`Sercom`], `SDA` and `SCL` represent the SDA and SCL pads respectively. A
22//! [`Pad`] is just a [`Pin`] configured in the correct [`PinMode`] that
23//! implements [`IsPad`]. The [`bsp_pins!`](crate::bsp_pins) macro can be used
24//! to define convenient type aliases for [`Pad`] types.
25//!
26//! ```no_run
27//! use atsamd_hal::gpio::{PA08, PA09, AlternateC};
28//! use atsamd_hal::sercom::{Sercom0, i2c};
29//! use atsamd_hal::typelevel::NoneT;
30//!
31//! type Sda = Pin<PA08, AlternateC>;
32//! type Scl = Pin<PA09, AlternateC>;
33//!
34//! type Pads = i2c::Pads<Sercom0, Sda, Scl>;
35//! ```
36//!
37//! Alternatively, you can use the [`PadsFromIds`] alias to define a set of
38//! `Pads` in terms of [`PinId`]s instead of [`Pin`]s. This is useful when you
39//! don't have [`Pin`] aliases pre-defined.
40//!
41//! ```no_run
42//! use atsamd_hal::gpio::{PA08, PA09};
43//! use atsamd_hal::sercom::{Sercom0, i2c};
44//!
45//! type Pads = i2c::PadsFromIds<Sercom0, PA08, PA09>;
46//! ```
47//!
48//! Instances of [`Pads`] are created using the [`new`](Pads::new) method.
49//!
50//! On SAMD21 and SAMx5x chips, [`new`](Pads::new) method automatically convert
51//! each pin to the correct [`PinMode`]. But for SAMD11 chips, users must
52//! manually convert each pin before calling the builder methods. This is a
53//! consequence of inherent ambiguities in the SAMD11 SERCOM pad definitions.
54//! Specifically, the same [`PinId`] can correspond to two different [`PadNum`]s
55//! for the *same* `Sercom`.
56//!
57//! ```no_run
58//! use atsamd_hal::pac::Peripherals;
59//! use atsamd_hal::gpio::Pins;
60//! use atsamd_hal::sercom::{Sercom0, i2c};
61//!
62//! let mut peripherals = Peripherals::take().unwrap();
63//! let pins = Pins::new(peripherals.PORT);
64//! let pads = i2c::Pads::<Sercom0>::new(pins.pa08, pins.pa09);
65//! ```
66//!
67//! # [`Config`]
68//!
69//! Next, create a [`Config`] struct, which represents the I2C peripheral in its
70//! disabled state. A [`Config`] is specified with one type parameters, the
71//! [`Pads`] type.
72//!
73//! Upon creation, the [`Config`] takes ownership of both the [`Pads`] struct
74//! and the PAC [`Sercom`] struct. It takes a reference to the PM, so that it
75//! can enable the APB clock, and it takes a frequency to indicate the GCLK
76//! configuration. Users are responsible for correctly configuring the GCLK.
77//!
78//! ```no_run
79//! use atsamd_hal::gpio::{PA08, PA09};
80//! use atsamd_hal::sercom::{Sercom0, i2c};
81//!
82//! type Pads = i2c::PadsFromIds<Sercom0, PA08, PA09>;
83//! type Config = i2c::Config<Pads>;
84//!
85//! let pm = peripherals.PM;
86//! let sercom = peripherals.SERCOM0;
87//! // Configure GCLK for 10 MHz
88//! let freq = 10.mhz();
89//! let config = i2c::Config::new(&pm, sercom, pads, freq);
90//! ```
91//!
92//! The [`Config`] struct can configure the peripheral in one of two ways:
93//!
94//! * A set of methods is provided to use in a builder pattern: for example
95//!   [`baud`](Config::baud), [`run_in_standby`](Config::run_in_standby), etc.
96//!   These methods take `self` and return `Self`.
97//! * A set of methods is provided to use as setters: for example
98//!   [`set_baud`](Config::set_baud),
99//!   [`set_run_in_standby`](Config::set_run_in_standby), etc. These methods
100//!   take `&mut self` and return nothing.
101//!
102//! In any case, the peripheral setup ends with a call to [`enable`], which
103//! consumes the [`Config`] and returns an enabled [`I2c`] peripheral.
104//!
105//! ```no_run
106//! let i2c = i2c::Config::new(&pm, sercom, pads, freq)
107//!     .baud(1.mhz())
108//!     .enable();
109//! ```
110//!
111//! Alternatively,
112//!
113//! ```no_run
114//! let i2c = i2c::Config::new(&mclk, sercom, pads, freq);
115//!     i2c.set_baud(1.mhz());
116//!     let i2c = i2c.enable();
117//! ```
118//!
119//! ## Reading the current configuration
120//!
121//! It is possible to read the current configuration by using the getter methods
122//! provided: for example [`get_baud`](Config::get_baud),
123//! [`get_run_in_standby`](Config::get_run_in_standby), etc.
124//!
125//! # [`I2c`]
126//!
127//! [`I2c`] structs can only be created from a [`Config`]. They have one type
128//! parameter, representing the underlying [`Config`].
129//!
130//! Only the [`I2c`] struct can actually perform transactions. To do so, use the
131//! [`embedded_hal::i2c::I2c`] trait.
132//!
133//! ```
134//! use embedded_hal::i2c::I2c;
135//!
136//! i2c.write(0x54, 0x0fe).unwrap();
137//! ```
138//!
139//! # Reading the current configuration
140//!
141//! The `AsRef<Config<P>>` trait is implemented for `I2c<Config<P>>`. This means
142//! you can use the `get_` methods implemented for `Config`, since they take an
143//! `&self` argument.
144//!
145//! ```no_run
146//! // Assume i2c is a I2c<C<P>>
147//! let baud = i2c.as_ref().get_baud();
148//! ```
149//!
150//! # Reconfiguring
151//!
152//! The [`reconfigure`] method gives out an `&mut Config` reference, which can
153//! then use the `set_*` methods.
154//!
155//! ```no_run
156//! use atsamd_hal::sercom::i2c::I2c;
157//!
158//! // Assume config is a valid Duplex I2C Config struct
159//! let i2c = config.enable();
160//!
161//! // Send/receive data...
162//!
163//! // Reconfigure I2C peripheral
164//! i2c.reconfigure(|c| c.set_run_in_standby(false));
165//!
166//! // Disable I2C peripheral
167//! let config = i2c.disable();
168//! ```
169//!
170//! # Non-supported features
171//!
172//! * Slave mode is not supported at this time.
173//! * High-speed mode is not supported.
174//! * 4-wire mode is not supported.
175//! * 32-bit extension mode is not supported (SAMx5x). If you need to transfer
176//!   slices, consider using the DMA methods instead <span class="stab
177//!   portability" title="Available on crate feature `dma`
178//!   only"><code>dma</code></span>.
179//!
180//! # Using I2C with DMA <span class="stab portability" title="Available on crate feature `dma` only"><code>dma</code></span>
181//!
182//! This HAL includes support for DMA-enabled I2C transfers. Use
183//! [`I2c::with_dma_channel`] to attach a DMA channel to the [`I2c`] struct. A
184//! DMA-enabled [`I2c`] implements the blocking
185//! [`embedded_hal::i2c::I2c`](crate::ehal::i2c::I2c) trait, which can be used
186//! to perform I2C transfers which are fast, continuous and low jitter, even if
187//! they are preemped by a higher priority interrupt.
188//!
189//!
190//! ```no_run
191//! use atsamd_hal::dmac::channel::{AnyChannel, Ready};
192//! use atsand_hal::sercom::i2c::{I2c, AnyConfig, Error};
193//! use atsamd_hal::embedded_hal::i2c::I2c;
194//! fn i2c_write_with_dma<A: AnyConfig, C: AnyChannel<Status = Ready>>(i2c: I2c<A>, channel: C, bytes: &[u8]) -> Result<(), Error>{
195//!     // Attach a DMA channel
196//!     let i2c = i2c.with_dma_channel(channel);
197//!     i2c.write(0x54, bytes)?;
198//! }
199//! ```
200//!
201//! ## Limitations of using DMA with I2C
202//!
203//! * The I2C peripheral only supports continuous DMA read/writes of up to 255
204//!   bytes. Trying to read/write with a transfer of 256 bytes or more will
205//!   result in a panic. This also applies to using [`I2c::transaction`] with
206//!   adjacent write/read operations of the same type; the total number of bytes
207//!   across all adjacent operations must not exceed 256. If you need continuous
208//!   transfers of 256 bytes or more, use the non-DMA [`I2c`] implementations.
209//!
210//! * When using [`I2c::transaction`] or [`I2c::write_read`], the
211//!   [`embedded_hal::i2c::I2c`] specification mandates that a REPEATED START
212//!   (instead of a STOP+START) is sent between transactions of a different type
213//!   (read/write). Unfortunately, in DMA mode, the hardware is only capable of
214//!   sending STOP+START. If you absolutely need repeated starts, the only
215//!   workaround is to use the I2C without DMA.
216//!
217//! * Using [`I2c::transaction`] consumes significantly more memory than the
218//!   other methods provided by [`embedded_hal::i2c::I2c`] (at least 256 bytes
219//!   extra).
220//!
221//! * When using [`I2c::transaction`], up to 17 adjacent operations of the same
222//!   type can be continuously handled by DMA without CPU intervention. If you
223//!   need more than 17 adjacent operations of the same type, the transfer will
224//!   reverted to using the byte-by-byte (non-DMA) implementation.
225//!
226//! All these limitations also apply to I2C transfers in async mode when using
227//! DMA. They do not apply to I2C transfers in async mode when not using DMA.
228//!
229//! # `async` operation <span class="stab portability" title="Available on crate feature `async` only"><code>async</code></span>
230//!
231//! An [`I2c`] can be used for `async` operations. Configuring an [`I2c`] in
232//! async mode is relatively simple:
233//!
234//! * Bind the corresponding `SERCOM` interrupt source to the SPI
235//!   [`InterruptHandler`] (refer to the module-level [`async_hal`]
236//!   documentation for more information).
237//! * Turn a previously configured [`I2c`] into an [`I2cFuture`] by calling
238//!   [`I2c::into_future`]
239//! * Optionally, add a DMA channel by using [`I2cFuture::with_dma_channel`].
240//!   The API is exactly the same whether a DMA channel is used or not.
241//! * Use the provided async methods for reading or writing to the I2C
242//!   peripheral. [`I2cFuture`] implements [`embedded_hal_async::i2c::I2c`].
243//!
244//! `I2cFuture` implements `AsRef<I2c>` and `AsMut<I2c>` so that it can be
245//! reconfigured using the regular [`I2c`] methods.
246//!
247//! ## Considerations when using `async` [`I2c`] with DMA <span class="stab portability" title="Available on crate feature `async` only"><code>async</code></span> <span class="stab portability" title="Available on crate feature `dma` only"><code>dma</code></span>
248//!
249//! * An [`I2c`] struct must be turned into an [`I2cFuture`] by calling
250//!   [`I2c::into_future`] before calling `with_dma_channel`. The DMA channel
251//!   itself must also be configured in async mode by using
252//!   [`DmaController::into_future`](crate::dmac::DmaController::into_future).
253//!   If a DMA channel is added to the [`I2c`] struct before it is turned into
254//!   an [`I2cFuture`], it will not be able to use DMA in async mode.
255//!
256//! ```
257//! // This will work
258//! let i2c = i2c.into_future().with_dma_channel(channel);
259//!
260//! // This won't
261//! let i2c = i2c.with_dma_channel(channel).into_future();
262//! ```
263//!
264//! ### Safety considerations
265//!
266//! In `async` mode, an I2C+DMA transfer does not require `'static` source and
267//! destination buffers. This, in theory, makes its use `unsafe`. However it is
268//! marked as safe for better ergonomics, and to enable the implementation of
269//! the [`embedded_hal_async::i2c::I2c`] trait.
270//!
271//! This means that, as an user, you **must** ensure that the [`Future`]s
272//! returned by the [`embedded_hal_async::i2c::I2c`] methods may never be
273//! forgotten through [`forget`] or by wrapping them with a [`ManuallyDrop`].
274//!
275//! The returned futures implement [`Drop`] and will automatically stop any
276//! ongoing transfers; this guarantees that the memory occupied by the
277//! now-dropped buffers may not be corrupted by running transfers.
278//!
279//! This means that using functions like [`futures::select_biased`] to implement
280//! timeouts is safe; transfers will be safely cancelled if the timeout expires.
281//!
282//! This also means that should you [`forget`] this [`Future`] after its first
283//! [`poll`] call, the transfer will keep running, ruining the now-reclaimed
284//! memory, as well as the rest of your day.
285//!
286//! * `await`ing is fine: the [`Future`] will run to completion.
287//! * Dropping an incomplete transfer is also fine. Dropping can happen, for
288//!   example, if the transfer doesn't complete before a timeout expires.
289//! * Dropping an incomplete transfer *without running its destructor* is
290//!   **unsound** and will trigger undefined behavior.
291//!
292//! ```ignore
293//! async fn always_ready() {}
294//!
295//! let mut buffer = [0x00; 10];
296//!
297//! // This is completely safe
298//! i2c.read(&mut buffer).await?;
299//!
300//! // This is also safe: we launch a transfer, which is then immediately cancelled
301//! futures::select_biased! {
302//!     _ = i2c.read(&mut buffer)?,
303//!     _ = always_ready(),
304//! }
305//!
306//! // This, while contrived, is also safe.
307//! {
308//!     use core::future::Future;
309//!
310//!     let future = i2c.read(&mut buffer);
311//!     futures::pin_mut!(future);
312//!     // Assume ctx is a `core::task::Context` given out by the executor.
313//!     // The future is polled, therefore starting the transfer
314//!     future.as_mut().poll(ctx);
315//!
316//!     // Future is dropped here - transfer is cancelled.
317//! }
318//!
319//! // DANGER: This is an example of undefined behavior
320//! {
321//!     use core::future::Future;
322//!     use core::ops::DerefMut;
323//!
324//!     let future = core::mem::ManuallyDrop::new(i2c.read(&mut buffer));
325//!     futures::pin_mut!(future);
326//!     // To actually make this example compile, we would need to wrap the returned
327//!     // future from `i2c.read()` in a newtype that implements Future, because we
328//!     // can't actually call as_mut() without being able to name the type we want
329//!     // to deref to.
330//!     let future_ref: &mut SomeNewTypeFuture = &mut future.as_mut();
331//!     future.as_mut().poll(ctx);
332//!
333//!     // Future is NOT dropped here - transfer is not cancelled, resulting un UB.
334//! }
335//! ```
336//!
337//! As you can see, unsoundness is relatively hard to come by - however, caution
338//! should still be exercised.
339//!
340//! [`enable`]: Config::enable
341//! [`disable`]: I2c::disable
342//! [`reconfigure`]: I2c::reconfigure
343//! [`bsp_pins`]: crate::bsp_pins
344//! [`Sercom`]: crate::sercom::Sercom
345//! [`Pad0`]: crate::sercom::pad::Pad0
346//! [`Pad1`]: crate::sercom::pad::Pad1
347//! [`Pad`]: crate::sercom::pad::Pad
348//! [`IoSet`]: crate::sercom::pad::IoSet
349//! [`IsPad`]: crate::sercom::pad::IsPad
350//! [`PadNum`]: crate::sercom::pad::PadNum
351//! [`Pin`]: crate::gpio::pin::Pin
352//! [`PinId`]: crate::gpio::pin::PinId
353//! [`PinMode`]: crate::gpio::pin::PinMode
354//! [`embedded_hal::i2c::I2c`]: crate::ehal::i2c::I2c
355//! [`I2c::transaction`]: crate::ehal::i2c::I2c::transaction
356//! [`I2c::write_read`]: crate::ehal::i2c::I2c::write_read
357//! [`async_hal`]: crate::async_hal
358//! [`forget`]: core::mem::forget
359//! [`ManuallyDrop`]: core::mem::ManuallyDrop
360//! [`Future`]: core::future::Future
361//! [`poll`]: core::future::Future::poll
362
363use atsamd_hal_macros::hal_module;
364
365#[hal_module(
366    any("sercom0-d11", "sercom0-d21") => "i2c/pads_thumbv6m.rs",
367    "sercom0-d5x" => "i2c/pads_thumbv7em.rs",
368)]
369mod pads {}
370
371pub use pads::*;
372
373mod reg;
374use reg::Registers;
375
376mod flags;
377pub use flags::*;
378
379mod config;
380pub use config::*;
381
382mod impl_ehal;
383
384#[cfg(feature = "async")]
385mod async_api;
386
387#[cfg(feature = "async")]
388pub use async_api::*;
389
390/// Word size for an I2C message
391pub type Word = u8;
392
393/// Inactive timeout configuration
394#[repr(u8)]
395#[derive(Clone, Copy)]
396pub enum InactiveTimeout {
397    /// Disabled
398    Disabled = 0x0,
399    /// 5-6 SCL cycles (50-60 us @ 100 kHz)
400    Us55 = 0x1,
401    ///10-11 SCL cycles (100-110 us @ 100 kHz)
402    Us105 = 0x2,
403    /// 20-21 SCL cycles (200-210 us @ 100 kHz)
404    Us205 = 0x3,
405}
406
407/// Abstraction over a I2C peripheral, allowing to perform I2C transactions.
408pub struct I2c<C: AnyConfig, D = crate::typelevel::NoneT> {
409    config: C,
410    _dma_channel: D,
411}
412
413impl<C: AnyConfig, D> I2c<C, D> {
414    /// Obtain a pointer to the `DATA` register. Necessary for DMA transfers.
415    #[inline]
416    pub fn data_ptr(&self) -> *mut Word {
417        self.config.as_ref().registers.data_ptr()
418    }
419
420    /// Read the interrupt flags
421    #[inline]
422    pub fn read_flags(&self) -> Flags {
423        self.config.as_ref().registers.read_flags()
424    }
425
426    /// Clear interrupt status flags
427    #[inline]
428    pub fn clear_flags(&mut self, flags: Flags) {
429        self.config.as_mut().registers.clear_flags(flags);
430    }
431
432    /// Enable interrupts for the specified flags.
433    #[inline]
434    pub fn enable_interrupts(&mut self, flags: Flags) {
435        self.config.as_mut().registers.enable_interrupts(flags);
436    }
437
438    /// Disable interrupts for the specified flags.
439    #[inline]
440    pub fn disable_interrupts(&mut self, flags: Flags) {
441        self.config.as_mut().registers.disable_interrupts(flags);
442    }
443
444    /// Read the status flags
445    #[inline]
446    pub fn read_status(&self) -> Status {
447        self.config.as_ref().registers.read_status()
448    }
449
450    /// Clear the status flags
451    #[inline]
452    pub fn clear_status(&mut self, status: Status) {
453        self.config.as_mut().registers.clear_status(status);
454    }
455
456    #[cfg(feature = "dma")]
457    #[inline]
458    pub(super) fn start_dma_write(&mut self, address: u8, xfer_len: u8) {
459        self.config
460            .as_mut()
461            .registers
462            .start_dma_write(address, xfer_len)
463    }
464
465    #[cfg(feature = "dma")]
466    #[inline]
467    pub(super) fn start_dma_read(&mut self, address: u8, xfer_len: u8) {
468        self.config
469            .as_mut()
470            .registers
471            .start_dma_read(address, xfer_len)
472    }
473
474    #[cfg(feature = "dma")]
475    #[inline]
476    pub(super) fn check_bus_status(&self) -> Result<(), Error> {
477        self.config.as_ref().registers.check_bus_status()
478    }
479
480    #[inline]
481    fn do_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
482        self.config.as_mut().registers.do_write(addr, bytes)
483    }
484
485    /// Continue a write operation that was issued before with
486    /// [`do_write`](Self::do_write) or [`continue_write`](Self::continue_write)
487    /// without a repeated start condition in between
488    #[inline]
489    fn continue_write(&mut self, bytes: &[u8]) -> Result<(), Error> {
490        self.config.as_mut().registers.continue_write(bytes)
491    }
492
493    #[inline]
494    fn do_read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Error> {
495        self.config.as_mut().registers.do_read(addr, bytes)
496    }
497
498    /// Continue a read operation that was issued before with
499    /// [`do_read`](Self::do_read) or [`continue_read`](Self::continue_read)
500    /// without a repeated start condition in between
501    #[inline]
502    fn continue_read(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
503        self.config.as_mut().registers.continue_read(bytes)
504    }
505
506    #[inline]
507    fn do_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
508        self.config
509            .as_mut()
510            .registers
511            .do_write_read(addr, bytes, buffer)
512    }
513    #[inline]
514    fn cmd_stop(&mut self) {
515        self.config.as_mut().registers.cmd_stop()
516    }
517
518    /// Reconfigure the I2C peripheral.
519    ///
520    /// Calling this method will temporarily disable the SERCOM peripheral, as
521    /// some registers are enable-protected. This may interrupt any ongoing
522    /// transactions.
523    ///
524    /// ```
525    /// use atsamd_hal::sercom::i2c::I2c;
526    /// i2c.reconfigure(|c| c.set_run_in_standby(false));
527    /// ```
528    #[inline]
529    pub fn reconfigure<F>(&mut self, update: F)
530    where
531        F: FnOnce(&mut SpecificConfig<C>),
532    {
533        self.config.as_mut().registers.enable_peripheral(false);
534        update(self.config.as_mut());
535        self.config.as_mut().registers.enable_peripheral(true);
536    }
537
538    /// Disable the I2C peripheral and return the underlying [`Config`]
539    #[inline]
540    pub fn disable(self) -> C {
541        let mut config = self.config;
542        config.as_mut().registers.disable();
543        config
544    }
545}
546
547impl<C: AnyConfig> I2c<C> {
548    /// Attach a DMA channel to this [`I2c`]. Its
549    /// [`embedded_hal::i2c::I2c`](crate::ehal::i2c::I2c) implementation will
550    /// use DMA to carry out its transactions.
551    #[cfg(feature = "dma")]
552    #[inline]
553    pub fn with_dma_channel<Chan: crate::dmac::AnyChannel<Status = crate::dmac::Ready>>(
554        self,
555        channel: Chan,
556    ) -> I2c<C, Chan> {
557        I2c {
558            config: self.config,
559            _dma_channel: channel,
560        }
561    }
562}
563
564#[cfg(feature = "dma")]
565impl<C, D, S> I2c<C, D>
566where
567    C: AnyConfig,
568    D: crate::dmac::AnyChannel<Status = S>,
569    S: crate::dmac::ReadyChannel,
570{
571    /// Reclaim the DMA channel. Any subsequent I2C operations will no longer
572    /// use DMA.
573    pub fn take_dma_channel(self) -> (I2c<C, crate::typelevel::NoneT>, D) {
574        (
575            I2c {
576                config: self.config,
577                _dma_channel: crate::typelevel::NoneT,
578            },
579            self._dma_channel,
580        )
581    }
582}
583
584impl<P: PadSet> AsRef<Config<P>> for I2c<Config<P>> {
585    #[inline]
586    fn as_ref(&self) -> &Config<P> {
587        self.config.as_ref()
588    }
589}