Skip to content

Commit

Permalink
Use a typed clock token to configure the Sercom peripherals
Browse files Browse the repository at this point in the history
  • Loading branch information
RobinMcCorkell committed Nov 23, 2022
1 parent 3f957ce commit 209eefb
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 68 deletions.
1 change: 1 addition & 0 deletions hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Add Advanced Encryption Standard (AES) peripheral support including RustCrypto compatible backend
- Add embedded-hal `InputPin` trait to EIC pins
- Use a typed `RtcClock` token to configure the RTC peripheral
- Use a typed `SercomNCoreClock` token to configure the SERCOM peripheral

# v0.15.1

Expand Down
19 changes: 11 additions & 8 deletions hal/src/sercom/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@
//!
//! Upon creation, the [`Config`] takes ownership of both the [`Pads`] struct
//! and the PAC [`Sercom`] struct. It takes a reference to the PM, so that it
//! can enable the APB clock, and it takes a frequency to indicate the GCLK
//! configuration. Users are responsible for correctly configuring the GCLK.
//! can enable the APB clock.
//!
//! ```no_run
//! use atsamd_hal::gpio::{PA08, PA09};
Expand All @@ -92,9 +91,13 @@
//!
//! let pm = peripherals.PM;
//! let sercom = peripherals.SERCOM0;
//!
//! // Configure GCLK for 10 MHz
//! let freq = 10.mhz();
//! let config = i2c::Config::new(&pm, sercom, pads, freq);
//! let clocks: GenericClockController = todo!();
//! let gclk: GClock = todo!();
//!
//! let core_clock = clocks.sercom0_core(&gclk).unwrap();
//! let config = i2c::Config::new(&pm, sercom, pads, core_clock);
//! ```
//!
//! The [`Config`] struct can configure the peripheral in one of two ways:
Expand All @@ -111,15 +114,15 @@
//! consumes the [`Config`] and returns an enabled [`I2c`] peripheral.
//!
//! ```no_run
//! let i2c = i2c::Config::new(&pm, sercom, pads, freq)
//! let i2c = i2c::Config::new(&pm, sercom, pads, core_clock)
//! .baud(1.mhz())
//! .enable();
//! ```
//!
//! Alternatively,
//!
//! ```no_run
//! let i2c = i2c::Config::new(&mclk, sercom, pads, freq);
//! let i2c = i2c::Config::new(&mclk, sercom, pads, core_clock);
//! i2c.set_baud(1.mhz());
//! let i2c = i2c.enable();
//! ```
Expand Down Expand Up @@ -212,11 +215,11 @@ implements the DMAC [`Buffer`]
trait. The provided [`send_with_dma`] and
[`receive_with_dma`] build and begin a
[`dmac::Transfer`], thus starting the I2C
in a non-blocking way.
in a non-blocking way.
Note that the [`init_dma_transfer`] method should be called immediately before
starting a DMA transfer with I2C. This will check that the bus is in a correct
state before starting the transfer, and providing a token type to pass to the
state before starting the transfer, and providing a token type to pass to the
[`send_with_dma`] and [`receive_with_dma`] methods.
Optionally, interrupts can be enabled on the provided
Expand Down
19 changes: 8 additions & 11 deletions hal/src/sercom/i2c/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{I2c, InactiveTimeout, PadSet, Registers};
use crate::{
pac::sercom0::i2cm::ctrla::MODE_A,
sercom::*,
time::Hertz,
time::{Clock as ClockTrait, Hertz},
typelevel::{Is, Sealed},
};

Expand Down Expand Up @@ -32,20 +32,20 @@ where
{
pub(super) registers: Registers<P::Sercom>,
pads: P,
freq: Hertz,
core_clock: <P::Sercom as Sercom>::CoreClock,
}

impl<P: PadSet> Config<P> {
/// Create a new [`Config`] in the default configuration.
#[inline]
fn default(sercom: P::Sercom, pads: P, freq: impl Into<Hertz>) -> Self {
fn default(sercom: P::Sercom, pads: P, core_clock: <P::Sercom as Sercom>::CoreClock) -> Self {
let mut registers = Registers::new(sercom);
registers.swrst();
registers.set_op_mode(MODE_A::I2C_MASTER);
Self {
registers,
pads,
freq: freq.into(),
core_clock,
}
}

Expand All @@ -57,18 +57,15 @@ impl<P: PadSet> Config<P> {
///
/// Note that [`Config`] takes ownership of both the
/// PAC [`Sercom`] struct as well as the [`Pads`](super::Pads).
///
/// Users must configure GCLK manually. The `freq` parameter represents the
/// GCLK frequency for this [`Sercom`] instance.
#[inline]
pub fn new(
apb_clk_ctrl: &APB_CLK_CTRL,
mut sercom: P::Sercom,
pads: P,
freq: impl Into<Hertz>,
core_clock: <P::Sercom as Sercom>::CoreClock,
) -> Self {
sercom.enable_apb_clock(apb_clk_ctrl);
Self::default(sercom, pads, freq)
Self::default(sercom, pads, core_clock)
}
}

Expand All @@ -88,7 +85,7 @@ impl<P: PadSet> Config<P> {
/// default configuration.
#[inline]
pub fn reset(self) -> Config<P> {
Config::default(self.registers.sercom, self.pads, self.freq)
Config::default(self.registers.sercom, self.pads, self.core_clock)
}

/// Consume the [`Config`], reset the peripheral, and return the [`Sercom`]
Expand Down Expand Up @@ -146,7 +143,7 @@ impl<P: PadSet> Config<P> {
/// the maximum supported baud rate.
#[inline]
pub fn set_baud(&mut self, baud: impl Into<Hertz>) {
self.registers.set_baud(self.freq, baud);
self.registers.set_baud(self.core_clock.freq(), baud);
}

/// Get the contents of the `BAUD` register and the current baud mode. Note
Expand Down
30 changes: 24 additions & 6 deletions hal/src/sercom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
feature = "min-samd51g",
doc = "
# Undocumented features
The ATSAMx5x chips contain certain features that aren't documented in the datasheet.
The ATSAMx5x chips contain certain features that aren't documented in the datasheet.
These features are implemented in the HAL based on experimentation with certain boards
which have verifiably demonstrated that those features work as intended.
Expand Down Expand Up @@ -37,19 +37,34 @@ use paste::paste;
use seq_macro::seq;

use crate::pac;
use crate::time::Clock;

#[cfg(feature = "min-samd51g")]
use pac::MCLK as APB_CLK_CTRL;
#[cfg(any(feature = "samd11", feature = "samd21"))]
use pac::PM as APB_CLK_CTRL;

use pac::{sercom0, SERCOM0, SERCOM1};
use pac::sercom0;
use {
crate::clock::{Sercom0CoreClock, Sercom1CoreClock},
pac::{SERCOM0, SERCOM1},
};

#[cfg(any(feature = "samd21", feature = "min-samd51g"))]
use pac::{SERCOM2, SERCOM3};
use {
crate::clock::{Sercom2CoreClock, Sercom3CoreClock},
pac::{SERCOM2, SERCOM3},
};
#[cfg(any(feature = "min-samd21g", feature = "min-samd51g"))]
use pac::{SERCOM4, SERCOM5};
use {
crate::clock::{Sercom4CoreClock, Sercom5CoreClock},
pac::{SERCOM4, SERCOM5},
};
#[cfg(feature = "min-samd51n")]
use pac::{SERCOM6, SERCOM7};
use {
crate::clock::{Sercom6CoreClock, Sercom7CoreClock},
pac::{SERCOM6, SERCOM7},
};

#[cfg(feature = "dma")]
use crate::dmac::TriggerSource;
Expand All @@ -73,6 +88,8 @@ pub mod dma;

/// Type-level `enum` representing a Serial Communication Interface (SERCOM)
pub trait Sercom: Sealed + Deref<Target = sercom0::RegisterBlock> {
/// SERCOM core clock type
type CoreClock: Clock;
/// SERCOM number
const NUM: usize;
/// RX Trigger source for DMA transactions
Expand All @@ -93,6 +110,7 @@ macro_rules! sercom {
pub type Sercom~N = SERCOM~N;
impl Sealed for Sercom~N {}
impl Sercom for Sercom~N {
type CoreClock = [<Sercom ~N CoreClock>];
const NUM: usize = N;
#[cfg(feature = "dma")]
const DMA_RX_TRIGGER: TriggerSource = TriggerSource::[<SERCOM~N _RX>];
Expand Down
42 changes: 21 additions & 21 deletions hal/src/sercom/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@ type Pads = spi::PadsFromIds<Sercom0, IoSet1, PA08, NoneT, PA09>;
//!
//! Upon creation, the [`Config`] takes ownership of both the [`Pads`] and the
//! PAC [`Sercom`] struct. It takes a reference to the `PM` or `MCLK`, so that
//! it can enable the APB clock, and it takes a frequency to indicate the GCLK
//! configuration. Users are responsible for correctly configuring the GCLK.
//! it can enable the APB clock.
//!
//! ```
//! use atsamd_hal::time::U32Ext;
Expand All @@ -173,14 +172,18 @@ type Pads = spi::PadsFromIds<Sercom0, IoSet1, PA08, NoneT, PA09>;
//! // SAMD11/SAMD21 version
//! let pm = peripherals.PM;
//! let sercom = peripherals.SERCOM0;
//! let freq = 10.mhz();
//! let config = spi::Config::new(&pm, sercom, pads, freq);
//!
//! // Configure GCLK for 10 MHz
//! let clocks: GenericClockController = todo!();
//! let gclk: GClock = todo!();
//!
//! let core_clock = clocks.sercom0_core(&gclk).unwrap();
//! let config = spi::Config::new(&pm, sercom, pads, core_clock);
//!
//! // SAMx5x version
//! let mclk = peripherals.MCLK;
//! let sercom = peripherals.SERCOM0;
//! let freq = 10.mhz();
//! let config = spi::Config::new(&mclk, sercom, pads, freq);
//! let config = spi::Config::new(&mclk, sercom, pads, core_clock);
//! ```
//!
//! The [`Config`] uses two different APIs for configuration. For most
Expand All @@ -200,15 +203,15 @@ type Pads = spi::PadsFromIds<Sercom0, IoSet1, PA08, NoneT, PA09>;
//! use embedded_hal::spi::MODE_1;
//!
//! // SAMD11/SAMD21 version
//! let spi = spi::Config::new(&pm, sercom, pads, freq)
//! let spi = spi::Config::new(&pm, sercom, pads, core_clock)
//! .baud(1.mhz())
//! .char_size::<NineBit>()
//! .bit_order(BitOrder::LsbFirst)
//! .spi_mode(MODE_1)
//! .enable();
//!
//! // SAMx5x version
//! let spi = spi::Config::new(&mclk, sercom, pads, freq)
//! let spi = spi::Config::new(&mclk, sercom, pads, core_clock)
//! .baud(1.mhz())
//! .length::<U2>()
//! .bit_order(BitOrder::LsbFirst)
Expand Down Expand Up @@ -320,7 +323,7 @@ use embedded_hal::spi;
pub use embedded_hal::spi::{Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};

use crate::sercom::*;
use crate::time::Hertz;
use crate::time::{Clock as ClockTrait, Hertz};
use crate::typelevel::{Is, NoneT, Sealed};

mod reg;
Expand Down Expand Up @@ -643,13 +646,13 @@ where
pads: P,
mode: PhantomData<M>,
size: PhantomData<Z>,
freq: Hertz,
core_clock: <P::Sercom as Sercom>::CoreClock,
}

impl<P: ValidPads> Config<P> {
/// Create a new [`Config`] in the default configuration.
#[inline]
fn default(sercom: P::Sercom, pads: P, freq: impl Into<Hertz>) -> Self {
fn default(sercom: P::Sercom, pads: P, core_clock: <P::Sercom as Sercom>::CoreClock) -> Self {
let mut regs = Registers { sercom };
regs.reset();
regs.set_op_mode(Master::MODE, Master::MSSEN);
Expand All @@ -663,7 +666,7 @@ impl<P: ValidPads> Config<P> {
pads,
mode: PhantomData,
size: PhantomData,
freq: freq.into(),
core_clock,
}
}

Expand All @@ -683,18 +686,15 @@ impl<P: ValidPads> Config<P> {
#[cfg_attr(feature = "min-samd51g", doc = "[`Length`] of `U1`")]
/// for SAMx5x chips. Note that [`Config`] takes ownership of both the
/// PAC [`Sercom`] struct as well as the [`Pads`].
///
/// Users must configure GCLK manually. The `freq` parameter represents the
/// GCLK frequency for this [`Sercom`] instance.
#[inline]
pub fn new(
apb_clk_ctrl: &APB_CLK_CTRL,
mut sercom: P::Sercom,
pads: P,
freq: impl Into<Hertz>,
core_clock: <P::Sercom as Sercom>::CoreClock,
) -> Self {
sercom.enable_apb_clock(apb_clk_ctrl);
Self::default(sercom, pads, freq)
Self::default(sercom, pads, core_clock)
}
}

Expand All @@ -716,7 +716,7 @@ where
pads: self.pads,
mode: PhantomData,
size: PhantomData,
freq: self.freq,
core_clock: self.core_clock,
}
}

Expand All @@ -733,7 +733,7 @@ where
/// default configuration.
#[inline]
pub fn reset(self) -> Config<P> {
Config::default(self.regs.sercom, self.pads, self.freq)
Config::default(self.regs.sercom, self.pads, self.core_clock)
}

/// Consume the [`Config`], reset the peripheral, and return the [`Sercom`]
Expand Down Expand Up @@ -871,7 +871,7 @@ where
/// The returned baud rate may not exactly match what was set.
#[inline]
pub fn get_baud(&mut self) -> Hertz {
self.regs.get_baud(self.freq)
self.regs.get_baud(self.core_clock.freq())
}

/// Set the baud rate
Expand All @@ -882,7 +882,7 @@ where
/// 512. Values outside this range will saturate at the extremes.
#[inline]
pub fn set_baud(&mut self, baud: impl Into<Hertz>) {
self.regs.set_baud(self.freq, baud);
self.regs.set_baud(self.core_clock.freq(), baud);
}

/// Set the baud rate using the builder API
Expand Down
16 changes: 10 additions & 6 deletions hal/src/sercom/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,21 @@ type Pads = uart::PadsFromIds<Sercom0, PA08, PA09>;
//!
//! Upon creation, the [`Config`] takes ownership of both the [`Pads`] struct
//! and the PAC [`Sercom`] struct. It takes a reference to the PM, so that it
//! can enable the APB clock, and it takes a frequency to indicate the GCLK
//! configuration. Users are responsible for correctly configuring the GCLK.
//! can enable the APB clock.
//!
//! ```
//! use atsamd_hal::time::U32Ext;
//! use atsamd_hal::clock::{GenericClockController, GClock};
//!
//! let pm = peripherals.PM;
//! let sercom = peripherals.SERCOM0;
//!
//! // Configure GCLK for 10 MHz
//! let freq = 10.mhz();
//! let config = uart::Config::new(&pm, sercom, pads, freq);
//! let clocks: GenericClockController = todo!();
//! let gclk: GClock = todo!();
//!
//! let core_clock = clocks.sercom0_core(&gclk).unwrap();
//! let config = uart::Config::new(&pm, sercom, pads, core_clock);
//! ```
//!
//! The [`Config`] struct can configure the peripheral in one of two ways:
Expand All @@ -125,7 +129,7 @@ type Pads = uart::PadsFromIds<Sercom0, PA08, PA09>;
//! ```
//! use atsamd_hal::sercom::uart::{StopBits, NineBit, BitOrder, BaudMode, Oversampling};
//!
//! let uart = uart::Config::new(&mclk, sercom, pads, freq)
//! let uart = uart::Config::new(&mclk, sercom, pads, core_clock)
//! .baud(1.mhz(), BaudMode::Arithmetic(Oversampling::Bits16))
//! .char_size::<NineBit>()
//! .bit_order(BitOrder::LsbFirst)
Expand All @@ -138,7 +142,7 @@ type Pads = uart::PadsFromIds<Sercom0, PA08, PA09>;
//! ```
//! use atsamd_hal::sercom::uart::{StopBits, NineBit, BitOrder, BaudMode, Oversampling};
//!
//! let uart = uart::Config::new(&mclk, sercom, pads, freq);
//! let uart = uart::Config::new(&mclk, sercom, pads, core_clock);
//! uart.set_baud(1.mhz(), BaudMode::Arithmetic(Oversampling::Bits16));
//! uart.set_char_size::<NineBit>();
//! uart.set_bit_order(BitOrder::LsbFirst);
Expand Down
Loading

0 comments on commit 209eefb

Please sign in to comment.