Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add I2C slave mode to fix #636 #671

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions hal/src/sercom/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,13 @@ pub use flags::*;
mod config;
pub use config::*;

mod mode;
pub use mode::*;

mod impl_ehal;

use crate::typelevel::Sealed;

/// Word size for an I2C message
pub type Word = u8;

Expand All @@ -294,17 +299,21 @@ pub enum InactiveTimeout {
}

/// Abstraction over a I2C peripheral, allowing to perform I2C transactions.
pub struct I2c<C: AnyConfig> {
pub struct I2c<C: AnyConfig<Mode = M>, M: I2cMode = Host> {
config: C,
}

impl<C: AnyConfig> I2c<C> {
/// Implementation for both modes
impl<C: AnyConfig<Mode = M>, M: I2cMode> I2c<C, M> {
/// Obtain a pointer to the `DATA` register. Necessary for DMA transfers.
#[inline]
pub fn data_ptr(&self) -> *mut Word {
self.config.as_ref().registers.data_ptr()
}
}

/// Host-only implementation
impl<C: AnyConfig<Mode = Host>> I2c<C, Host> {
/// Read the interrupt flags
#[inline]
pub fn read_flags(&self) -> Flags {
Expand Down Expand Up @@ -416,6 +425,34 @@ impl<C: AnyConfig> I2c<C> {
}
}

/// Client implementation
impl<C: AnyConfig<Mode = Client>> I2c<C, Client> {
/// Read the interrupt flags
#[inline]
pub fn read_flags(&self) -> ClientFlags {
self.config.as_ref().registers.read_flags()
}

/// Clear interrupt status flags
#[inline]
pub fn clear_flags(&mut self, flags: ClientFlags) {
self.config.as_mut().registers.clear_flags(flags);
}

/// Enable interrupts for the specified flags.
#[inline]
pub fn enable_interrupts(&mut self, flags: ClientFlags) {
self.config.as_mut().registers.enable_interrupts(flags);
}

/// Disable interrupts for the specified flags.
#[inline]
pub fn disable_interrupts(&mut self, flags: ClientFlags) {
self.config.as_mut().registers.disable_interrupts(flags);
}

}

impl<P: PadSet> AsRef<Config<P>> for I2c<Config<P>> {
#[inline]
fn as_ref(&self) -> &Config<P> {
Expand Down
121 changes: 121 additions & 0 deletions hal/src/sercom/i2c/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! WIP module for I2C client/slave configuration

mod config;
mod flags;
mod reg;

use super::Error;
use super::InactiveTimeout;
use super::PadSet;
use super::Status;
use super::Word;
pub use config::{ClientAnyConfig, ClientConfig, ClientSpecificConfig};
pub use flags::ClientFlags;
use reg::Registers;

/// Abstraction over an I2C peripheral in client mode
pub struct I2cClient<C: ClientAnyConfig> {
config: C,
}

impl<C: ClientAnyConfig> I2cClient<C> {
/// Obtain a pointer to the `DATA` register. Necessary for DMA transfers.
#[inline]
pub fn data_ptr(&self) -> *mut Word {
self.config.as_ref().registers.data_ptr()
}


/// Read the status flags
#[inline]
pub fn read_status(&self) -> Status {
self.config.as_ref().registers.read_status()
}

/// Clear the status flags
#[inline]
pub fn clear_status(&mut self, status: Status) {
self.config.as_mut().registers.clear_status(status);
}

#[cfg(feature = "dma")]
#[inline]
pub(super) fn start_dma_write(&mut self, address: u8, xfer_len: u8) {
self.config
.as_mut()
.registers
.start_dma_write(address, xfer_len)
}

#[cfg(feature = "dma")]
#[inline]
pub(super) fn start_dma_read(&mut self, address: u8, xfer_len: u8) {
self.config
.as_mut()
.registers
.start_dma_read(address, xfer_len)
}

#[cfg(feature = "dma")]
#[inline]
pub(super) fn check_bus_status(&self) -> Result<(), Error> {
self.config.as_ref().registers.check_bus_status()
}

#[inline]
fn do_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
self.config.as_mut().registers.do_write(addr, bytes)
}

#[inline]
fn do_read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Error> {
self.config.as_mut().registers.do_read(addr, bytes)
}

#[inline]
fn do_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
self.config
.as_mut()
.registers
.do_write_read(addr, bytes, buffer)
}
#[inline]
fn cmd_stop(&mut self) {
self.config.as_mut().registers.cmd_stop()
}

/// Reconfigure the I2C peripheral.
///
/// Calling this method will temporarily disable the SERCOM peripheral, as
/// some registers are enable-protected. This may interrupt any ongoing
/// transactions.
///
/// ```
/// use atsamd_hal::sercom::i2c::I2c;
/// i2c.reconfigure(|c| c.set_run_in_standby(false));
/// ```
#[inline]
pub fn reconfigure<F>(&mut self, update: F)
where
F: FnOnce(&mut ClientSpecificConfig<C>),
{
self.config.as_mut().registers.enable_peripheral(false);
update(self.config.as_mut());
self.config.as_mut().registers.enable_peripheral(true);
}

/// Disable the I2C peripheral and return the underlying [`Config`]
#[inline]
pub fn disable(self) -> C {
let mut config = self.config;
config.as_mut().registers.disable();
config
}
}

impl<P: PadSet> AsRef<ClientConfig<P>> for I2cClient<ClientConfig<P>> {
#[inline]
fn as_ref(&self) -> &ClientConfig<P> {
self.config.as_ref()
}
}
182 changes: 182 additions & 0 deletions hal/src/sercom/i2c/client/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
use super::{I2cClient, InactiveTimeout, PadSet, Registers};
use crate::{
sercom::{Sercom, APB_CLK_CTRL},
time::Hertz,
typelevel::{Is, Sealed},
};

/// A configurable, disabled I2C peripheral
///
/// This `struct` represents a configurable I2C peripheral in its disabled
/// state. It is generic over the set of [`Pads`].
/// Upon creation, the [`Config`] takes ownership of the
/// [`Sercom`] and resets it, returning it configured as an I2C peripheral
/// with a default configuration in Master mode.
///
/// [`Config`] uses a builder-pattern API to configure the peripheral,
/// culminating in a call to [`enable`], which consumes the [`Config`] and
/// returns an enabled [`I2c`].
///
/// [`enable`]: Config::enable
/// [`Pads`]: super::Pads
pub struct ClientConfig<P>
where
P: PadSet,
{
pub(super) registers: Registers<P::Sercom>,
pads: P,
freq: Hertz,
}

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

/// Create a new [`Config`] in the default configuration
///
/// This function will enable the corresponding APB clock, reset the
/// [`Sercom`] peripheral, and return a [`Config`] in the default
/// configuration. The only available operating mode is currently Master.
///
/// 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>,
) -> Self {
sercom.enable_apb_clock(apb_clk_ctrl);
Self::default(sercom, pads, freq)
}
}

impl<P: PadSet> ClientConfig<P> {
/// Obtain a reference to the PAC `SERCOM` struct
///
/// # Safety
///
/// Directly accessing the `SERCOM` could break the invariants of the
/// type-level tracking in this module, so it is unsafe.
#[inline]
pub unsafe fn sercom(&self) -> &P::Sercom {
&self.registers.sercom
}

/// Trigger the [`Sercom`]'s SWRST and return a [`Config`] in the
/// default configuration.
#[inline]
pub fn reset(self) -> Self {
Self::default(self.registers.sercom, self.pads, self.freq)
}

/// Consume the [`Config`], reset the peripheral, and return the
/// [`Sercom`] and [`Pads`](super::Pads)
#[inline]
pub fn free(mut self) -> (P::Sercom, P) {
self.registers.swrst();
(self.registers.free(), self.pads)
}

/// Run in standby mode (builder pattern version)
///
/// When set, the I2C peripheral will run in standby mode. See the
/// datasheet for more details.
#[inline]
pub fn run_in_standby(mut self, set: bool) -> Self {
self.set_run_in_standby(set);
self
}

/// Run in standby mode (setter version)
///
/// When set, the I2C peripheral will run in standby mode. See the
/// datasheet for more details.
#[inline]
pub fn set_run_in_standby(&mut self, set: bool) {
self.registers.set_run_in_standby(set);
}

/// Get the current run in standby mode
#[inline]
pub fn get_run_in_standby(&self) -> bool {
self.registers.get_run_in_standby()
}

/// Enable the I2C peripheral
///
/// I2C transactions are not possible until the peripheral is enabled.
#[inline]
pub fn enable(mut self) -> I2cClient<Self>
where
Self: ClientAnyConfig,
{
self.registers.enable();

I2cClient { config: self }
}
}

//=============================================================================
// AnyConfig
//=============================================================================

/// Type class for all possible [`Config`] types
///
/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
/// [`Config`] types. See the [`AnyKind`] documentation for more details on the
/// pattern.
///
/// In addition to the normal, [`AnyKind`] associated types. This trait also
/// copies the [`Sercom`] type, to make it easier
/// to apply bounds to these types at the next level of abstraction.
///
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
/// [type class]: crate::typelevel#type-classes
pub trait ClientAnyConfig: Is<Type = ClientSpecificConfig<Self>> {
type Sercom: Sercom;
type Pads: PadSet<Sercom = Self::Sercom>;
}

/// Type alias to recover the specific [`Config`] type from an implementation of
/// [`AnyConfig`]
pub type ClientSpecificConfig<C> = ClientConfig<<C as ClientAnyConfig>::Pads>;

/// Type alias to recover the specific [`Sercom`] type from an implementation of
/// [`AnyConfig`]
pub type ConfigSercom<C> = <C as ClientAnyConfig>::Sercom;

impl<P: PadSet> Sealed for ClientConfig<P> {}

impl<P: PadSet> ClientAnyConfig for ClientConfig<P> {
type Sercom = P::Sercom;
type Pads = P;
}

impl<P: PadSet> AsRef<Self> for ClientConfig<P> {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}

impl<P: PadSet> AsMut<Self> for ClientConfig<P> {
#[inline]
fn as_mut(&mut self) -> &mut Self {
self
}
}
3 changes: 3 additions & 0 deletions hal/src/sercom/i2c/client/flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use bitflags::bitflags;
use modular_bitfield::specifiers::{B1, B5};
use modular_bitfield::*;
Loading