delete old GPIO module
This commit is contained in:
parent
5f88f094ca
commit
81eaf8127c
@ -12,6 +12,11 @@
|
||||
//!
|
||||
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
||||
//! in a type-safe way.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
|
||||
pub use vorago_shared_periphs::gpio::*;
|
||||
|
||||
pub use vorago_shared_periphs::gpio::asynch;
|
||||
|
@ -1,947 +0,0 @@
|
||||
//! # Type-erased, value-level module for GPIO pins
|
||||
//!
|
||||
//! Although the type-level API is generally preferred, it is not suitable in
|
||||
//! all cases. Because each pin is represented by a distinct type, it is not
|
||||
//! possible to store multiple pins in a homogeneous data structure. The
|
||||
//! value-level API solves this problem by erasing the type information and
|
||||
//! tracking the pin at run-time.
|
||||
//!
|
||||
//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two
|
||||
//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`]
|
||||
//! respectively. The implementation of these types closely mirrors the
|
||||
//! type-level API.
|
||||
//!
|
||||
//! Instances of [`DynPin`] cannot be created directly. Rather, they must be
|
||||
//! created from their type-level equivalents using [`From`]/[`Into`].
|
||||
//!
|
||||
//! ```
|
||||
//! // Move a pin out of the Pins struct and convert to a DynPin
|
||||
//! let pa0: DynPin = pins.pa0.into();
|
||||
//! ```
|
||||
//!
|
||||
//! Conversions between pin modes use a value-level version of the type-level
|
||||
//! API.
|
||||
//!
|
||||
//! ```
|
||||
//! // Use one of the literal function names
|
||||
//! pa0.into_floating_input();
|
||||
//! // Use a method and a DynPinMode variant
|
||||
//! pa0.into_mode(DYN_FLOATING_INPUT);
|
||||
//! ```
|
||||
//!
|
||||
//! Because the pin state cannot be tracked at compile-time, many [`DynPin`]
|
||||
//! operations become fallible. Run-time checks are inserted to ensure that
|
||||
//! users don't try to, for example, set the output level of an input pin.
|
||||
//!
|
||||
//! Users may try to convert value-level pins back to their type-level
|
||||
//! equivalents. However, this option is fallible, because the compiler cannot
|
||||
//! guarantee the pin has the correct ID or is in the correct mode at
|
||||
//! compile-time. Use [TryFrom]/[TryInto] for this conversion.
|
||||
//!
|
||||
//! ```
|
||||
//! // Convert to a `DynPin`
|
||||
//! let pa0: DynPin = pins.pa0.into();
|
||||
//! // Change pin mode
|
||||
//! pa0.into_floating_input();
|
||||
//! // Convert back to a `Pin`
|
||||
//! let pa0: Pin<PA0, FloatingInput> = pa0.try_into().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! # Embedded HAL traits
|
||||
//!
|
||||
//! This module implements all of the embedded HAL GPIO traits for [`DynPin`].
|
||||
//! However, whereas the type-level API uses
|
||||
//! `Error = core::convert::Infallible`, the value-level API can return a real
|
||||
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
|
||||
//! operation, the trait functions will return
|
||||
//! [InvalidPinTypeError].
|
||||
|
||||
use super::{
|
||||
pin::{FilterType, Pin, PinId, PinMode},
|
||||
InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port,
|
||||
};
|
||||
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel};
|
||||
|
||||
//==================================================================================================
|
||||
// DynPinMode configurations
|
||||
//==================================================================================================
|
||||
|
||||
/// Value-level `enum` for disabled configurations
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum DynDisabled {
|
||||
Floating,
|
||||
PullDown,
|
||||
PullUp,
|
||||
}
|
||||
|
||||
/// Value-level `enum` for input configurations
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum DynInput {
|
||||
Floating,
|
||||
PullDown,
|
||||
PullUp,
|
||||
}
|
||||
|
||||
/// Value-level `enum` for output configurations
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum DynOutput {
|
||||
PushPull,
|
||||
OpenDrain,
|
||||
ReadablePushPull,
|
||||
ReadableOpenDrain,
|
||||
}
|
||||
|
||||
pub type DynAlternate = FunSel;
|
||||
|
||||
//==============================================================================
|
||||
// Error
|
||||
//==============================================================================
|
||||
|
||||
/// GPIO error type
|
||||
///
|
||||
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
|
||||
/// operations are fallible. This `enum` represents the corresponding errors.
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[error("Invalid pin type for operation: {0:?}")]
|
||||
pub struct InvalidPinTypeError(pub DynPinMode);
|
||||
|
||||
impl embedded_hal::digital::Error for InvalidPinTypeError {
|
||||
fn kind(&self) -> embedded_hal::digital::ErrorKind {
|
||||
embedded_hal::digital::ErrorKind::Other
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// DynPinMode
|
||||
//==================================================================================================
|
||||
|
||||
/// Value-level `enum` representing pin modes
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum DynPinMode {
|
||||
Input(DynInput),
|
||||
Output(DynOutput),
|
||||
Alternate(DynAlternate),
|
||||
}
|
||||
|
||||
/// Value-level variant of [`DynPinMode`] for floating input mode
|
||||
pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating);
|
||||
/// Value-level variant of [`DynPinMode`] for pull-down input mode
|
||||
pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown);
|
||||
/// Value-level variant of [`DynPinMode`] for pull-up input mode
|
||||
pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp);
|
||||
|
||||
/// Value-level variant of [`DynPinMode`] for push-pull output mode
|
||||
pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
|
||||
/// Value-level variant of [`DynPinMode`] for open-drain output mode
|
||||
pub const DYN_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::OpenDrain);
|
||||
/// Value-level variant of [`DynPinMode`] for readable push-pull output mode
|
||||
pub const DYN_RD_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadablePushPull);
|
||||
/// Value-level variant of [`DynPinMode`] for readable opendrain output mode
|
||||
pub const DYN_RD_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadableOpenDrain);
|
||||
|
||||
/// Value-level variant of [`DynPinMode`] for function select 1
|
||||
pub const DYN_ALT_FUNC_1: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel1);
|
||||
/// Value-level variant of [`DynPinMode`] for function select 2
|
||||
pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel2);
|
||||
/// Value-level variant of [`DynPinMode`] for function select 3
|
||||
pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3);
|
||||
|
||||
//==================================================================================================
|
||||
// DynGroup & DynPinId
|
||||
//==================================================================================================
|
||||
|
||||
pub type DynGroup = Port;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DynPinId {
|
||||
port: Port,
|
||||
num: u8,
|
||||
}
|
||||
|
||||
impl DynPinId {
|
||||
pub const fn new(port: Port, num: u8) -> Self {
|
||||
DynPinId { port, num }
|
||||
}
|
||||
|
||||
pub const fn port(&self) -> Port {
|
||||
self.port
|
||||
}
|
||||
pub const fn num(&self) -> u8 {
|
||||
self.num
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// ModeFields
|
||||
//==================================================================================================
|
||||
|
||||
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
||||
#[derive(Default)]
|
||||
struct ModeFields {
|
||||
dir: bool,
|
||||
opendrn: bool,
|
||||
pull_en: bool,
|
||||
/// true for pullup, false for pulldown
|
||||
pull_dir: bool,
|
||||
funsel: u8,
|
||||
enb_input: bool,
|
||||
}
|
||||
|
||||
impl From<DynPinMode> for ModeFields {
|
||||
#[inline]
|
||||
fn from(mode: DynPinMode) -> Self {
|
||||
let mut fields = Self::default();
|
||||
match mode {
|
||||
DynPinMode::Input(config) => {
|
||||
fields.dir = false;
|
||||
fields.funsel = FunSel::Sel0 as u8;
|
||||
match config {
|
||||
DynInput::Floating => (),
|
||||
DynInput::PullUp => {
|
||||
fields.pull_en = true;
|
||||
fields.pull_dir = true;
|
||||
}
|
||||
DynInput::PullDown => {
|
||||
fields.pull_en = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DynPinMode::Output(config) => {
|
||||
fields.dir = true;
|
||||
fields.funsel = FunSel::Sel0 as u8;
|
||||
match config {
|
||||
DynOutput::PushPull => (),
|
||||
DynOutput::OpenDrain => {
|
||||
fields.opendrn = true;
|
||||
}
|
||||
DynOutput::ReadableOpenDrain => {
|
||||
fields.enb_input = true;
|
||||
fields.opendrn = true;
|
||||
}
|
||||
DynOutput::ReadablePushPull => {
|
||||
fields.enb_input = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DynPinMode::Alternate(config) => {
|
||||
fields.funsel = config as u8;
|
||||
}
|
||||
}
|
||||
fields
|
||||
}
|
||||
}
|
||||
|
||||
/// Type definition to avoid confusion: These register blocks are identical
|
||||
type PortRegisterBlock = pac::porta::RegisterBlock;
|
||||
pub type PortReg = pac::ioconfig::Porta;
|
||||
|
||||
//==================================================================================================
|
||||
// DynPin
|
||||
//==================================================================================================
|
||||
|
||||
/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`]
|
||||
///
|
||||
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
|
||||
/// by the same type, and pins are tracked and distinguished at run-time.
|
||||
#[derive(Debug)]
|
||||
pub struct DynPin {
|
||||
id: DynPinId,
|
||||
mode: DynPinMode,
|
||||
}
|
||||
|
||||
impl DynPin {
|
||||
/// Create a new [DynPin]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Each [DynPin] must be a singleton. For a given [DynPinId], there
|
||||
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||
/// time. Violating this requirement is `unsafe`.
|
||||
#[inline]
|
||||
pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||
DynPin { id, mode }
|
||||
}
|
||||
|
||||
/// Steals a new [DynPin].
|
||||
///
|
||||
/// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without
|
||||
/// modifying any registers related to the behaviour of the pin. The user should call
|
||||
/// [Self::into_mode] to ensure the correct mode of the pin.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not
|
||||
/// used cocurrently somewhere else. The caller might also want to call [Self::into_mode]
|
||||
/// to ensure the correct desired state of the pin. It is recommended to create the pin using
|
||||
/// [Pin::downgrade] instead.
|
||||
pub const unsafe fn steal(id: DynPinId) -> Self {
|
||||
DynPin {
|
||||
id,
|
||||
mode: DYN_FLOATING_INPUT,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a copy of the pin ID
|
||||
#[inline]
|
||||
pub const fn id(&self) -> DynPinId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Return a copy of the pin mode
|
||||
#[inline]
|
||||
pub const fn mode(&self) -> DynPinMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// Convert the pin to the requested [`DynPinMode`]
|
||||
#[inline]
|
||||
pub fn into_mode(&mut self, mode: DynPinMode) {
|
||||
self.change_mode(mode);
|
||||
self.mode = mode;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_input_pin(&self) -> bool {
|
||||
matches!(self.mode, DynPinMode::Input(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_output_pin(&self) -> bool {
|
||||
matches!(self.mode, DynPinMode::Output(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_funsel_1(&mut self) {
|
||||
self.into_mode(DYN_ALT_FUNC_1);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_funsel_2(&mut self) {
|
||||
self.into_mode(DYN_ALT_FUNC_2);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_funsel_3(&mut self) {
|
||||
self.into_mode(DYN_ALT_FUNC_3);
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a floating input
|
||||
#[inline]
|
||||
pub fn into_floating_input(&mut self) {
|
||||
self.into_mode(DYN_FLOATING_INPUT);
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a pulled down input
|
||||
#[inline]
|
||||
pub fn into_pull_down_input(&mut self) {
|
||||
self.into_mode(DYN_PULL_DOWN_INPUT);
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a pulled up input
|
||||
#[inline]
|
||||
pub fn into_pull_up_input(&mut self) {
|
||||
self.into_mode(DYN_PULL_UP_INPUT);
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a push-pull output
|
||||
#[inline]
|
||||
pub fn into_push_pull_output(&mut self) {
|
||||
self.into_mode(DYN_PUSH_PULL_OUTPUT);
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a push-pull output
|
||||
#[inline]
|
||||
pub fn into_open_drain_output(&mut self) {
|
||||
self.into_mode(DYN_OPEN_DRAIN_OUTPUT);
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a push-pull output
|
||||
#[inline]
|
||||
pub fn into_readable_push_pull_output(&mut self) {
|
||||
self.into_mode(DYN_RD_PUSH_PULL_OUTPUT);
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a push-pull output
|
||||
#[inline]
|
||||
pub fn into_readable_open_drain_output(&mut self) {
|
||||
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
self.read_internal().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
self.read_internal()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
self.write_internal(false)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
self.write_internal(true)
|
||||
}
|
||||
|
||||
/// Toggle the logic level of an output pin
|
||||
#[inline(always)]
|
||||
pub fn toggle(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
if !self.is_output_pin() {
|
||||
return Err(InvalidPinTypeError(self.mode));
|
||||
}
|
||||
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||
if irq_cfg.route {
|
||||
self.configure_irqsel(irq_cfg.id);
|
||||
}
|
||||
if irq_cfg.enable_in_nvic {
|
||||
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
|
||||
// We only manipulate our own bit.
|
||||
self.port_reg()
|
||||
.irq_enb()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||
}
|
||||
|
||||
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
||||
if reset_irqsel {
|
||||
self.reset_irqsel();
|
||||
}
|
||||
// We only manipulate our own bit.
|
||||
self.port_reg()
|
||||
.irq_enb()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||
}
|
||||
|
||||
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||
///
|
||||
/// There is no way for the compiler to know if the conversion will be
|
||||
/// successful at compile-time. We must verify the conversion at run-time
|
||||
/// or refuse to perform it.
|
||||
#[inline]
|
||||
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
||||
if self.id == I::DYN && self.mode == M::DYN {
|
||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||
// corresponding `Pin`
|
||||
return Ok(unsafe { Pin::new() });
|
||||
}
|
||||
Err(InvalidPinTypeError(self.mode))
|
||||
}
|
||||
|
||||
/// Convert the pin into an async pin. The pin can be converted back by calling
|
||||
/// [InputDynPinAsync::release]
|
||||
pub fn into_async_input(
|
||||
self,
|
||||
irq: crate::pac::Interrupt,
|
||||
) -> Result<InputDynPinAsync, InvalidPinTypeError> {
|
||||
InputDynPinAsync::new(self, irq)
|
||||
}
|
||||
|
||||
/// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
|
||||
pub fn configure_irqsel(&mut self, id: pac::Interrupt) {
|
||||
let mut syscfg = unsafe { pac::Sysconfig::steal() };
|
||||
let irqsel = unsafe { pac::Irqsel::steal() };
|
||||
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||
match self.id().port() {
|
||||
// Set the correct interrupt number in the IRQSEL register
|
||||
super::Port::A => {
|
||||
irqsel
|
||||
.porta0(self.id().num() as usize)
|
||||
.write(|w| unsafe { w.bits(id as u32) });
|
||||
}
|
||||
super::Port::B => {
|
||||
irqsel
|
||||
.portb0(self.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(id as u32) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the IRQSEL peripheral value for this particular pin.
|
||||
pub fn reset_irqsel(&mut self) {
|
||||
let mut syscfg = unsafe { pac::Sysconfig::steal() };
|
||||
let irqsel = unsafe { pac::Irqsel::steal() };
|
||||
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||
match self.id().port() {
|
||||
// Set the correct interrupt number in the IRQSEL register
|
||||
super::Port::A => {
|
||||
irqsel
|
||||
.porta0(self.id().num() as usize)
|
||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||
}
|
||||
super::Port::B => {
|
||||
irqsel
|
||||
.portb0(self.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get DATAMASK bit for this particular pin
|
||||
#[inline(always)]
|
||||
pub fn datamask(&self) -> bool {
|
||||
(self.port_reg().datamask().read().bits() >> self.id().num) == 1
|
||||
}
|
||||
|
||||
/// Clear DATAMASK bit for this particular pin. This prevents access
|
||||
/// of the corresponding bit for output and input operations
|
||||
#[inline(always)]
|
||||
pub fn clear_datamask(&self) {
|
||||
self.port_reg()
|
||||
.datamask()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||
}
|
||||
|
||||
/// Set DATAMASK bit for this particular pin. 1 is the default
|
||||
/// state of the bit and allows access of the corresponding bit
|
||||
#[inline(always)]
|
||||
pub fn set_datamask(&self) {
|
||||
self.port_reg()
|
||||
.datamask()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.read_pin_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.write_pin_masked(false)
|
||||
}
|
||||
|
||||
/// Possible delays in clock cycles:
|
||||
/// - Delay 1: 1
|
||||
/// - Delay 2: 2
|
||||
/// - Delay 1 + Delay 2: 3
|
||||
#[inline]
|
||||
pub fn configure_delay(
|
||||
&mut self,
|
||||
delay_1: bool,
|
||||
delay_2: bool,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.configure_delay_internal(delay_1, delay_2);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
#[inline]
|
||||
pub fn configure_pulse_mode(
|
||||
&mut self,
|
||||
enable: bool,
|
||||
default_state: PinState,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.configure_pulse_mode_internal(enable, default_state);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
/// See p.37 and p.38 of the programmers guide for more information.
|
||||
#[inline]
|
||||
pub fn configure_filter_type(
|
||||
&mut self,
|
||||
filter: FilterType,
|
||||
clksel: FilterClkSel,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) => {
|
||||
self.configure_filter_type_internal(filter, clksel);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_edge_interrupt(
|
||||
&mut self,
|
||||
edge_type: InterruptEdge,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self.configure_edge_interrupt_internal(edge_type);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_level_interrupt(
|
||||
&mut self,
|
||||
level_type: InterruptLevel,
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self.configure_level_interrupt_internal(level_type);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the pin mode
|
||||
#[inline]
|
||||
pub(crate) fn change_mode(&mut self, mode: DynPinMode) {
|
||||
let ModeFields {
|
||||
dir,
|
||||
funsel,
|
||||
opendrn,
|
||||
pull_dir,
|
||||
pull_en,
|
||||
enb_input,
|
||||
} = mode.into();
|
||||
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
||||
iocfg.write(|w| {
|
||||
w.opendrn().bit(opendrn);
|
||||
w.pen().bit(pull_en);
|
||||
w.plevel().bit(pull_dir);
|
||||
w.iewo().bit(enb_input);
|
||||
unsafe { w.funsel().bits(funsel) }
|
||||
});
|
||||
let mask = self.mask_32();
|
||||
unsafe {
|
||||
if dir {
|
||||
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||
// Clear output
|
||||
portreg.clrout().write(|w| w.bits(mask));
|
||||
} else {
|
||||
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn port_reg(&self) -> &PortRegisterBlock {
|
||||
match self.id().port() {
|
||||
Port::A => unsafe { &(*pac::Porta::ptr()) },
|
||||
Port::B => unsafe { &(*pac::Portb::ptr()) },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn iocfg_port(&self) -> &PortReg {
|
||||
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
|
||||
match self.id().port() {
|
||||
Port::A => ioconfig.porta(self.id().num() as usize),
|
||||
Port::B => ioconfig.portb0(self.id().num() as usize),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_internal(&self) -> Result<bool, InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||
Ok(self.read_pin())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.write_pin(bit);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError(self.mode)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read the logic level of an output pin
|
||||
pub(crate) fn read_pin(&self) -> bool {
|
||||
let portreg = self.port_reg();
|
||||
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
||||
}
|
||||
|
||||
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
||||
/// cleared as well
|
||||
#[inline(always)]
|
||||
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||
if !self.datamask() {
|
||||
Err(IsMaskedError)
|
||||
} else {
|
||||
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the logic level of an output pin
|
||||
#[inline(always)]
|
||||
pub(crate) fn write_pin(&mut self, bit: bool) {
|
||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe {
|
||||
if bit {
|
||||
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||
} else {
|
||||
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
||||
/// cleared as well
|
||||
#[inline]
|
||||
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
||||
if !self.datamask() {
|
||||
Err(IsMaskedError)
|
||||
} else {
|
||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe {
|
||||
if bit {
|
||||
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||
} else {
|
||||
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle the logic level of an output pin
|
||||
#[inline(always)]
|
||||
pub fn toggle_with_togout_reg(&mut self) {
|
||||
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||
// this pin ID
|
||||
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||
}
|
||||
|
||||
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
||||
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
||||
#[inline]
|
||||
fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) {
|
||||
unsafe {
|
||||
self.port_reg()
|
||||
.irq_sen()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
match edge_type {
|
||||
InterruptEdge::HighToLow => {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
InterruptEdge::LowToHigh => {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
InterruptEdge::BothEdges => {
|
||||
self.port_reg()
|
||||
.irq_edge()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure which edge or level type triggers an interrupt
|
||||
#[inline]
|
||||
fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) {
|
||||
unsafe {
|
||||
self.port_reg()
|
||||
.irq_sen()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
if level == InterruptLevel::Low {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
} else {
|
||||
self.port_reg()
|
||||
.irq_evt()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for input pins
|
||||
#[inline]
|
||||
fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.iocfg_port().modify(|_, w| {
|
||||
// Safety: Only write to register for this Pin ID
|
||||
unsafe {
|
||||
w.flttype().bits(filter as u8);
|
||||
w.fltclk().bits(clksel as u8)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
if enable {
|
||||
portreg
|
||||
.pulse()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.pulse()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
if default_state == PinState::Low {
|
||||
portreg
|
||||
.pulsebase()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.pulsebase()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for output pins
|
||||
#[inline]
|
||||
fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
if delay_1 {
|
||||
portreg
|
||||
.delay1()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.delay1()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
if delay_2 {
|
||||
portreg
|
||||
.delay2()
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
} else {
|
||||
portreg
|
||||
.delay2()
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only serves disambiguation purposes for the Embedded HAL impl
|
||||
#[inline(always)]
|
||||
fn is_low_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
||||
self.is_low()
|
||||
}
|
||||
|
||||
// Only serves disambiguation purposes for the Embedded HAL impl
|
||||
#[inline(always)]
|
||||
fn is_high_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
||||
self.is_high()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn mask_32(&self) -> u32 {
|
||||
1 << self.id().num()
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Convert between Pin and DynPin
|
||||
//==================================================================================================
|
||||
|
||||
impl<I: PinId, M: PinMode> From<Pin<I, M>> for DynPin {
|
||||
/// Erase the type-level information in a [`Pin`] and return a value-level
|
||||
/// [`DynPin`]
|
||||
#[inline]
|
||||
fn from(pin: Pin<I, M>) -> Self {
|
||||
pin.downgrade()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId, M: PinMode> TryFrom<DynPin> for Pin<I, M> {
|
||||
type Error = InvalidPinTypeError;
|
||||
|
||||
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||
///
|
||||
/// There is no way for the compiler to know if the conversion will be
|
||||
/// successful at compile-time. We must verify the conversion at run-time
|
||||
/// or refuse to perform it.
|
||||
#[inline]
|
||||
fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
|
||||
pin.upgrade()
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Embedded HAL traits
|
||||
//==================================================================================================
|
||||
|
||||
impl embedded_hal::digital::ErrorType for DynPin {
|
||||
type Error = InvalidPinTypeError;
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::OutputPin for DynPin {
|
||||
#[inline]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.set_high()
|
||||
}
|
||||
#[inline]
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.set_low()
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::InputPin for DynPin {
|
||||
#[inline]
|
||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||
self.is_high_mut()
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||
self.is_low_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::StatefulOutputPin for DynPin {
|
||||
#[inline]
|
||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||
self.is_high_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||
self.is_low_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self.toggle()
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
//! # API for the GPIO peripheral
|
||||
//!
|
||||
//! The implementation of this GPIO module is heavily based on the
|
||||
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html).
|
||||
//!
|
||||
//! This API provides two different submodules, [pin] and [dynpin],
|
||||
//! representing two different ways to handle GPIO pins. The default, [pin],
|
||||
//! is a type-level API that tracks the state of each pin at compile-time. The
|
||||
//! alternative, [dynpin] is a type-erased, value-level API that tracks the
|
||||
//! state of each pin at run-time.
|
||||
//!
|
||||
//! The type-level API is strongly preferred. By representing the state of each
|
||||
//! pin within the type system, the compiler can detect logic errors at
|
||||
//! compile-time. Furthermore, the type-level API has absolutely zero run-time
|
||||
//! cost.
|
||||
//!
|
||||
//! If needed, [dynpin] can be used to erase the type-level differences
|
||||
//! between pins. However, by doing so, pins must now be tracked at run-time,
|
||||
//! and each pin has a non-zero memory footprint.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||
|
||||
//==================================================================================================
|
||||
// Errors, Definitions and Constants
|
||||
//==================================================================================================
|
||||
|
||||
pub const NUM_PINS_PORT_A: usize = 32;
|
||||
pub const NUM_PINS_PORT_B: usize = 24;
|
||||
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[error("The pin is masked")]
|
||||
pub struct IsMaskedError;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Port {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum InterruptEdge {
|
||||
HighToLow,
|
||||
LowToHigh,
|
||||
BothEdges,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum InterruptLevel {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PinState {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
pub mod dynpin;
|
||||
pub use dynpin::*;
|
||||
|
||||
pub mod pin;
|
||||
pub use pin::*;
|
||||
|
||||
pub mod asynch;
|
||||
pub use asynch::*;
|
@ -1,823 +0,0 @@
|
||||
//! # Type-level module for GPIO pins
|
||||
//!
|
||||
//! This documentation is strongly based on the
|
||||
//! [atsamd documentation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/pin/index.html).
|
||||
//!
|
||||
//! This module provides a type-level API for GPIO pins. It uses the type system
|
||||
//! to track the state of pins at compile-time. Representing GPIO pins in this
|
||||
//! manner incurs no run-time overhead. Each [`Pin`] struct is zero-sized, so
|
||||
//! there is no data to copy around. Instead, real code is generated as a side
|
||||
//! effect of type transformations, and the resulting assembly is nearly
|
||||
//! identical to the equivalent, hand-written C.
|
||||
//!
|
||||
//! To track the state of pins at compile-time, this module uses traits to
|
||||
//! represent [type classes] and types as instances of those type classes. For
|
||||
//! example, the trait [`InputConfig`] acts as a [type-level enum] of the
|
||||
//! available input configurations, and the types [`Floating`], [`PullDown`] and
|
||||
//! [`PullUp`] are its type-level variants.
|
||||
//!
|
||||
//! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and
|
||||
//! [`PinMode`].
|
||||
//!
|
||||
//! ```
|
||||
//! pub struct Pin<I, M>
|
||||
//! where
|
||||
//! I: PinId,
|
||||
//! M: PinMode,
|
||||
//! {
|
||||
//! // ...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! A [PinId] identifies a pin by it's group (A or B) and pin number. Each
|
||||
//! [PinId] instance is named according to its datasheet identifier, e.g.
|
||||
//! [PA2].
|
||||
//!
|
||||
//! A [PinMode] represents the various pin modes. The available [PinMode]
|
||||
//! variants are [`Input`], [`Output`] and [`Alternate`], each with its own
|
||||
//! corresponding configurations.
|
||||
//!
|
||||
//! It is not possible for users to create new instances of a [`Pin`]. Singleton
|
||||
//! instances of each pin are made available to users through the PinsX
|
||||
//! struct.
|
||||
//!
|
||||
//! Example for the pins of PORT A:
|
||||
//!
|
||||
//! To create the [PinsA] struct, users must supply the PAC
|
||||
//! [Port](crate::pac::Porta) peripheral. The [PinsA] struct takes
|
||||
//! ownership of the [Porta] and provides the corresponding pins. Each [`Pin`]
|
||||
//! within the [PinsA] struct can be moved out and used individually.
|
||||
//!
|
||||
//!
|
||||
//! ```
|
||||
//! let mut peripherals = Peripherals::take().unwrap();
|
||||
//! let pinsa = PinsA::new(peripherals.PORT);
|
||||
//! ```
|
||||
//!
|
||||
//! Pins can be converted between modes using several different methods.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! // Use one of the literal function names
|
||||
//! let pa0 = pinsa.pa0.into_floating_input();
|
||||
//! // Use a generic method and one of the `PinMode` variant types
|
||||
//! let pa0 = pinsa.pa0.into_mode::<FloatingInput>();
|
||||
//! // Specify the target type and use `From`/`Into`
|
||||
//! let pa0: Pin<PA0, FloatingInput> = pinsa.pa27.into();
|
||||
//! ```
|
||||
//!
|
||||
//! # Embedded HAL traits
|
||||
//!
|
||||
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
||||
//! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin],
|
||||
//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
|
||||
use super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||
use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port};
|
||||
use crate::{
|
||||
pac::{Porta, Portb},
|
||||
typelevel::Sealed,
|
||||
};
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::transmute;
|
||||
use paste::paste;
|
||||
|
||||
//==================================================================================================
|
||||
// Input configuration
|
||||
//==================================================================================================
|
||||
|
||||
/// Type-level enum for input configurations
|
||||
///
|
||||
/// The valid options are [Floating], [PullDown] and [PullUp].
|
||||
pub trait InputConfig: Sealed {
|
||||
/// Corresponding [DynInput]
|
||||
const DYN: DynInput;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Floating {}
|
||||
#[derive(Debug)]
|
||||
pub enum PullDown {}
|
||||
#[derive(Debug)]
|
||||
pub enum PullUp {}
|
||||
|
||||
impl InputConfig for Floating {
|
||||
const DYN: DynInput = DynInput::Floating;
|
||||
}
|
||||
impl InputConfig for PullDown {
|
||||
const DYN: DynInput = DynInput::PullDown;
|
||||
}
|
||||
impl InputConfig for PullUp {
|
||||
const DYN: DynInput = DynInput::PullUp;
|
||||
}
|
||||
|
||||
impl Sealed for Floating {}
|
||||
impl Sealed for PullDown {}
|
||||
impl Sealed for PullUp {}
|
||||
|
||||
/// Type-level variant of [`PinMode`] for floating input mode
|
||||
pub type InputFloating = Input<Floating>;
|
||||
/// Type-level variant of [`PinMode`] for pull-down input mode
|
||||
pub type InputPullDown = Input<PullDown>;
|
||||
/// Type-level variant of [`PinMode`] for pull-up input mode
|
||||
pub type InputPullUp = Input<PullUp>;
|
||||
|
||||
/// Type-level variant of [`PinMode`] for input modes
|
||||
///
|
||||
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
|
||||
/// [`PullUp`]
|
||||
#[derive(Debug)]
|
||||
pub struct Input<C: InputConfig> {
|
||||
cfg: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: InputConfig> Sealed for Input<C> {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum FilterType {
|
||||
SystemClock = 0,
|
||||
DirectInputWithSynchronization = 1,
|
||||
FilterOneClockCycle = 2,
|
||||
FilterTwoClockCycles = 3,
|
||||
FilterThreeClockCycles = 4,
|
||||
FilterFourClockCycles = 5,
|
||||
}
|
||||
|
||||
pub use crate::clock::FilterClkSel;
|
||||
|
||||
//==================================================================================================
|
||||
// Output configuration
|
||||
//==================================================================================================
|
||||
|
||||
pub trait OutputConfig: Sealed {
|
||||
const DYN: DynOutput;
|
||||
}
|
||||
|
||||
pub trait ReadableOutput: Sealed {}
|
||||
|
||||
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
|
||||
#[derive(Debug)]
|
||||
pub enum PushPull {}
|
||||
/// Type-level variant of [`OutputConfig`] for an open drain configuration
|
||||
#[derive(Debug)]
|
||||
pub enum OpenDrain {}
|
||||
|
||||
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
|
||||
#[derive(Debug)]
|
||||
pub enum ReadablePushPull {}
|
||||
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
|
||||
#[derive(Debug)]
|
||||
pub enum ReadableOpenDrain {}
|
||||
|
||||
impl Sealed for PushPull {}
|
||||
impl Sealed for OpenDrain {}
|
||||
impl Sealed for ReadableOpenDrain {}
|
||||
impl Sealed for ReadablePushPull {}
|
||||
impl ReadableOutput for ReadableOpenDrain {}
|
||||
impl ReadableOutput for ReadablePushPull {}
|
||||
|
||||
impl OutputConfig for PushPull {
|
||||
const DYN: DynOutput = DynOutput::PushPull;
|
||||
}
|
||||
impl OutputConfig for OpenDrain {
|
||||
const DYN: DynOutput = DynOutput::OpenDrain;
|
||||
}
|
||||
impl OutputConfig for ReadablePushPull {
|
||||
const DYN: DynOutput = DynOutput::ReadablePushPull;
|
||||
}
|
||||
impl OutputConfig for ReadableOpenDrain {
|
||||
const DYN: DynOutput = DynOutput::ReadableOpenDrain;
|
||||
}
|
||||
|
||||
/// Type-level variant of [`PinMode`] for output modes
|
||||
///
|
||||
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
|
||||
/// their respective readable versions
|
||||
#[derive(Debug)]
|
||||
pub struct Output<C: OutputConfig> {
|
||||
cfg: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: OutputConfig> Sealed for Output<C> {}
|
||||
|
||||
/// Type-level variant of [`PinMode`] for push-pull output mode
|
||||
pub type PushPullOutput = Output<PushPull>;
|
||||
/// Type-level variant of [`PinMode`] for open drain output mode
|
||||
pub type OutputOpenDrain = Output<OpenDrain>;
|
||||
|
||||
pub type OutputReadablePushPull = Output<ReadablePushPull>;
|
||||
pub type OutputReadableOpenDrain = Output<ReadableOpenDrain>;
|
||||
|
||||
//==================================================================================================
|
||||
// Alternate configurations
|
||||
//==================================================================================================
|
||||
|
||||
/// Type-level enum for alternate peripheral function configurations
|
||||
pub trait AlternateConfig: Sealed {
|
||||
const DYN: DynAlternate;
|
||||
}
|
||||
|
||||
pub enum Funsel1 {}
|
||||
pub enum Funsel2 {}
|
||||
pub enum Funsel3 {}
|
||||
|
||||
impl AlternateConfig for Funsel1 {
|
||||
const DYN: DynAlternate = DynAlternate::Sel1;
|
||||
}
|
||||
impl AlternateConfig for Funsel2 {
|
||||
const DYN: DynAlternate = DynAlternate::Sel2;
|
||||
}
|
||||
impl AlternateConfig for Funsel3 {
|
||||
const DYN: DynAlternate = DynAlternate::Sel3;
|
||||
}
|
||||
|
||||
impl Sealed for Funsel1 {}
|
||||
impl Sealed for Funsel2 {}
|
||||
impl Sealed for Funsel3 {}
|
||||
|
||||
/// Type-level variant of [`PinMode`] for alternate peripheral functions
|
||||
///
|
||||
/// Type `C` is an [`AlternateConfig`]
|
||||
pub struct Alternate<C: AlternateConfig> {
|
||||
cfg: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: AlternateConfig> Sealed for Alternate<C> {}
|
||||
|
||||
pub type AltFunc1 = Alternate<Funsel1>;
|
||||
pub type AltFunc2 = Alternate<Funsel2>;
|
||||
pub type AltFunc3 = Alternate<Funsel3>;
|
||||
|
||||
/// Type alias for the [`PinMode`] at reset
|
||||
pub type Reset = InputFloating;
|
||||
|
||||
//==================================================================================================
|
||||
// Pin modes
|
||||
//==================================================================================================
|
||||
|
||||
/// Type-level enum representing pin modes
|
||||
///
|
||||
/// The valid options are [Input], [Output] and [Alternate].
|
||||
pub trait PinMode: Sealed {
|
||||
/// Corresponding [DynPinMode]
|
||||
const DYN: DynPinMode;
|
||||
}
|
||||
|
||||
impl<C: InputConfig> PinMode for Input<C> {
|
||||
const DYN: DynPinMode = DynPinMode::Input(C::DYN);
|
||||
}
|
||||
impl<C: OutputConfig> PinMode for Output<C> {
|
||||
const DYN: DynPinMode = DynPinMode::Output(C::DYN);
|
||||
}
|
||||
impl<C: AlternateConfig> PinMode for Alternate<C> {
|
||||
const DYN: DynPinMode = DynPinMode::Alternate(C::DYN);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Pin IDs
|
||||
//==================================================================================================
|
||||
|
||||
/// Type-level enum for pin IDs
|
||||
pub trait PinId: Sealed {
|
||||
/// Corresponding [DynPinId]
|
||||
const DYN: DynPinId;
|
||||
}
|
||||
|
||||
macro_rules! pin_id {
|
||||
($Group:ident, $Id:ident, $NUM:literal) => {
|
||||
// Need paste macro to use ident in doc attribute
|
||||
paste! {
|
||||
#[doc = "Pin ID representing pin " $Id]
|
||||
#[derive(Debug)]
|
||||
pub enum $Id {}
|
||||
impl Sealed for $Id {}
|
||||
impl PinId for $Id {
|
||||
const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Pin
|
||||
//==================================================================================================
|
||||
|
||||
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
|
||||
#[derive(Debug)]
|
||||
pub struct Pin<I: PinId, M: PinMode> {
|
||||
inner: DynPin,
|
||||
phantom: PhantomData<(I, M)>,
|
||||
}
|
||||
|
||||
impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
/// Create a new [Pin]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Each [Pin] must be a singleton. For a given [PinId], there must be
|
||||
/// at most one corresponding [Pin] in existence at any given time.
|
||||
/// Violating this requirement is `unsafe`.
|
||||
#[inline]
|
||||
pub(crate) const unsafe fn new() -> Pin<I, M> {
|
||||
Pin {
|
||||
inner: DynPin::new(I::DYN, M::DYN),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn id(&self) -> DynPinId {
|
||||
self.inner.id()
|
||||
}
|
||||
|
||||
/// Convert the pin to the requested [`PinMode`]
|
||||
#[inline]
|
||||
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
||||
// Only modify registers if we are actually changing pin mode
|
||||
// This check should compile away
|
||||
if N::DYN != M::DYN {
|
||||
self.inner.change_mode(N::DYN);
|
||||
}
|
||||
// Safe because we drop the existing Pin
|
||||
unsafe { Pin::new() }
|
||||
}
|
||||
|
||||
/// Configure the pin for function select 1. See Programmer Guide p.40 for the function table
|
||||
#[inline]
|
||||
pub fn into_funsel_1(self) -> Pin<I, AltFunc1> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin for function select 2. See Programmer Guide p.40 for the function table
|
||||
#[inline]
|
||||
pub fn into_funsel_2(self) -> Pin<I, AltFunc2> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin for function select 3. See Programmer Guide p.40 for the function table
|
||||
#[inline]
|
||||
pub fn into_funsel_3(self) -> Pin<I, AltFunc3> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a floating input
|
||||
#[inline]
|
||||
pub fn into_floating_input(self) -> Pin<I, InputFloating> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a pulled down input
|
||||
#[inline]
|
||||
pub fn into_pull_down_input(self) -> Pin<I, InputPullDown> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a pulled up input
|
||||
#[inline]
|
||||
pub fn into_pull_up_input(self) -> Pin<I, InputPullUp> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a push-pull output
|
||||
#[inline]
|
||||
pub fn into_push_pull_output(self) -> Pin<I, PushPullOutput> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a readable push-pull output
|
||||
#[inline]
|
||||
pub fn into_readable_push_pull_output(self) -> Pin<I, OutputReadablePushPull> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a readable open-drain output
|
||||
#[inline]
|
||||
pub fn into_readable_open_drain_output(self) -> Pin<I, OutputReadableOpenDrain> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low(&self) -> bool {
|
||||
!self.inner.read_pin()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high(&self) -> bool {
|
||||
self.inner.read_pin()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.inner.datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_datamask(&mut self) {
|
||||
self.inner.clear_datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_datamask(&mut self) {
|
||||
self.inner.set_datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.inner.is_high_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.inner.is_low_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn downgrade(self) -> DynPin {
|
||||
self.inner
|
||||
}
|
||||
|
||||
// Those only serve for the embedded HAL implementations which have different mutability.
|
||||
|
||||
#[inline]
|
||||
fn is_low_mut(&mut self) -> bool {
|
||||
self.is_low()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_high_mut(&mut self) -> bool {
|
||||
self.is_high()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||
self.inner.enable_interrupt(irq_cfg);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
||||
self.inner.disable_interrupt(reset_irqsel);
|
||||
}
|
||||
|
||||
/// Configure the pin for an edge interrupt but does not enable the interrupt.
|
||||
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
||||
self.inner.configure_edge_interrupt(edge_type).unwrap();
|
||||
}
|
||||
|
||||
/// Configure the pin for a level interrupt but does not enable the interrupt.
|
||||
pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) {
|
||||
self.inner.configure_level_interrupt(level_type).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// AnyPin
|
||||
//==============================================================================
|
||||
|
||||
/// Type class for [`Pin`] types
|
||||
///
|
||||
/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
|
||||
/// [`Pin`] types. See the `AnyKind` documentation for more details on the
|
||||
/// pattern.
|
||||
///
|
||||
/// ## `v1` Compatibility
|
||||
///
|
||||
/// Normally, this trait would use `Is<Type = SpecificPin<Self>>` as a super
|
||||
/// trait. But doing so would restrict implementations to only the `v2` `Pin`
|
||||
/// type in this module. To aid in backwards compatibility, we want to implement
|
||||
/// `AnyPin` for the `v1` `Pin` type as well. This is possible for a few
|
||||
/// reasons. First, both structs are zero-sized, so there is no meaningful
|
||||
/// memory layout to begin with. And even if there were, the `v1` `Pin` type is
|
||||
/// a newtype wrapper around a `v2` `Pin`, and single-field structs are
|
||||
/// guaranteed to have the same layout as the field, even for `repr(Rust)`.
|
||||
///
|
||||
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||
/// [type class]: crate::typelevel#type-classes
|
||||
pub trait AnyPin
|
||||
where
|
||||
Self: Sealed,
|
||||
Self: From<SpecificPin<Self>>,
|
||||
Self: Into<SpecificPin<Self>>,
|
||||
Self: AsRef<SpecificPin<Self>>,
|
||||
Self: AsMut<SpecificPin<Self>>,
|
||||
{
|
||||
/// [`PinId`] of the corresponding [`Pin`]
|
||||
type Id: PinId;
|
||||
/// [`PinMode`] of the corresponding [`Pin`]
|
||||
type Mode: PinMode;
|
||||
}
|
||||
|
||||
impl<I, M> Sealed for Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode,
|
||||
{
|
||||
}
|
||||
|
||||
impl<I, M> AnyPin for Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode,
|
||||
{
|
||||
type Id = I;
|
||||
type Mode = M;
|
||||
}
|
||||
|
||||
/// Type alias to recover the specific [`Pin`] type from an implementation of
|
||||
/// [`AnyPin`]
|
||||
///
|
||||
/// See the [`AnyKind`] documentation for more details on the pattern.
|
||||
///
|
||||
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||
pub type SpecificPin<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
|
||||
|
||||
impl<P: AnyPin> AsRef<P> for SpecificPin<P> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &P {
|
||||
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
|
||||
// both are zero-sized, and single-field, newtype structs are guaranteed
|
||||
// to have the same layout as the field anyway, even for repr(Rust).
|
||||
unsafe { transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut P {
|
||||
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
|
||||
// both are zero-sized, and single-field, newtype structs are guaranteed
|
||||
// to have the same layout as the field anyway, even for repr(Rust).
|
||||
unsafe { transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Additional functionality
|
||||
//==================================================================================================
|
||||
|
||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||
/// Convert the pin into an async pin. The pin can be converted back by calling
|
||||
/// [InputPinAsync::release]
|
||||
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
|
||||
InputPinAsync::new(self, irq)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
self.inner.write_pin(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low(&mut self) {
|
||||
self.inner.write_pin(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn toggle(&mut self) {
|
||||
self.inner.toggle().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.inner.set_high_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.inner.set_low_masked()
|
||||
}
|
||||
|
||||
/// See p.53 of the programmers guide for more information.
|
||||
/// Possible delays in clock cycles:
|
||||
/// - Delay 1: 1
|
||||
/// - Delay 2: 2
|
||||
/// - Delay 1 + Delay 2: 3
|
||||
#[inline]
|
||||
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
||||
self.inner.configure_delay(delay_1, delay_2).unwrap();
|
||||
}
|
||||
|
||||
/// See p.52 of the programmers guide for more information.
|
||||
///
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||
self.inner
|
||||
.configure_pulse_mode(enable, default_state)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||
/// See p.37 and p.38 of the programmers guide for more information.
|
||||
#[inline]
|
||||
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.inner.configure_filter_type(filter, clksel).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Embedded HAL traits
|
||||
//==================================================================================================
|
||||
|
||||
impl<I, M> embedded_hal::digital::ErrorType for Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode,
|
||||
{
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
|
||||
#[inline]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.set_high();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.set_low();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Input<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: InputConfig,
|
||||
{
|
||||
#[inline]
|
||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high_mut())
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_low_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: OutputConfig + ReadableOutput,
|
||||
{
|
||||
#[inline]
|
||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
#[inline]
|
||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_low())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self.toggle();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Pin definitions
|
||||
//==================================================================================================
|
||||
|
||||
macro_rules! pins {
|
||||
(
|
||||
$Port:ident, $PinsName:ident, $($Id:ident,)+,
|
||||
) => {
|
||||
paste!(
|
||||
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
|
||||
#[derive(Debug)]
|
||||
pub struct $PinsName {
|
||||
port: $Port,
|
||||
$(
|
||||
#[doc = "Pin " $Id]
|
||||
pub [<$Id:lower>]: Pin<$Id, Reset>,
|
||||
)+
|
||||
}
|
||||
|
||||
impl $PinsName {
|
||||
/// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral
|
||||
/// is optional because it might be required to create pin definitions for both
|
||||
/// ports.
|
||||
#[inline]
|
||||
pub fn new(
|
||||
syscfg: &mut va108xx::Sysconfig,
|
||||
port: $Port
|
||||
) -> $PinsName {
|
||||
syscfg.peripheral_clk_enable().modify(|_, w| {
|
||||
w.[<$Port:lower>]().set_bit();
|
||||
w.gpio().set_bit();
|
||||
w.ioconfig().set_bit()
|
||||
});
|
||||
$PinsName {
|
||||
//iocfg,
|
||||
port,
|
||||
// Safe because we only create one `Pin` per `PinId`
|
||||
$(
|
||||
[<$Id:lower>]: unsafe { Pin::new() },
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the peripheral ID
|
||||
/// Safety: Read-only register
|
||||
pub fn get_perid() -> u32 {
|
||||
let port = unsafe { &(*$Port::ptr()) };
|
||||
port.perid().read().bits()
|
||||
}
|
||||
|
||||
/// Consumes the Pins struct and returns the port definitions
|
||||
pub fn release(self) -> $Port {
|
||||
self.port
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! declare_pins {
|
||||
(
|
||||
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal),)+]
|
||||
) => {
|
||||
pins!($Port, $PinsName, $($Id,)+,);
|
||||
$(
|
||||
pin_id!($Group, $Id, $NUM);
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
declare_pins!(
|
||||
A,
|
||||
PinsA,
|
||||
Porta,
|
||||
[
|
||||
(PA0, 0),
|
||||
(PA1, 1),
|
||||
(PA2, 2),
|
||||
(PA3, 3),
|
||||
(PA4, 4),
|
||||
(PA5, 5),
|
||||
(PA6, 6),
|
||||
(PA7, 7),
|
||||
(PA8, 8),
|
||||
(PA9, 9),
|
||||
(PA10, 10),
|
||||
(PA11, 11),
|
||||
(PA12, 12),
|
||||
(PA13, 13),
|
||||
(PA14, 14),
|
||||
(PA15, 15),
|
||||
(PA16, 16),
|
||||
(PA17, 17),
|
||||
(PA18, 18),
|
||||
(PA19, 19),
|
||||
(PA20, 20),
|
||||
(PA21, 21),
|
||||
(PA22, 22),
|
||||
(PA23, 23),
|
||||
(PA24, 24),
|
||||
(PA25, 25),
|
||||
(PA26, 26),
|
||||
(PA27, 27),
|
||||
(PA28, 28),
|
||||
(PA29, 29),
|
||||
(PA30, 30),
|
||||
(PA31, 31),
|
||||
]
|
||||
);
|
||||
|
||||
declare_pins!(
|
||||
B,
|
||||
PinsB,
|
||||
Portb,
|
||||
[
|
||||
(PB0, 0),
|
||||
(PB1, 1),
|
||||
(PB2, 2),
|
||||
(PB3, 3),
|
||||
(PB4, 4),
|
||||
(PB5, 5),
|
||||
(PB6, 6),
|
||||
(PB7, 7),
|
||||
(PB8, 8),
|
||||
(PB9, 9),
|
||||
(PB10, 10),
|
||||
(PB11, 11),
|
||||
(PB12, 12),
|
||||
(PB13, 13),
|
||||
(PB14, 14),
|
||||
(PB15, 15),
|
||||
(PB16, 16),
|
||||
(PB17, 17),
|
||||
(PB18, 18),
|
||||
(PB19, 19),
|
||||
(PB20, 20),
|
||||
(PB21, 21),
|
||||
(PB22, 22),
|
||||
(PB23, 23),
|
||||
]
|
||||
);
|
@ -15,6 +15,13 @@ const CLK_100K: Hertz = Hertz::from_raw(100_000);
|
||||
const CLK_400K: Hertz = Hertz::from_raw(400_000);
|
||||
const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum I2cId {
|
||||
A = 0,
|
||||
B = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FifoEmptyMode {
|
||||
@ -111,16 +118,18 @@ pub type I2cRegBlock = pac::i2ca::RegisterBlock;
|
||||
|
||||
/// Common trait implemented by all PAC peripheral access structures. The register block
|
||||
/// format is the same for all SPI blocks.
|
||||
pub trait Instance: Deref<Target = I2cRegBlock> {
|
||||
const IDX: u8;
|
||||
pub trait I2cMarker: Deref<Target = I2cRegBlock> {
|
||||
const ID: I2cId;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
const PTR: *const I2cRegBlock;
|
||||
|
||||
fn ptr() -> *const I2cRegBlock;
|
||||
}
|
||||
|
||||
impl Instance for pac::I2ca {
|
||||
const IDX: u8 = 0;
|
||||
impl I2cMarker for pac::I2ca {
|
||||
const ID: I2cId = I2cId::A;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
|
||||
const PTR: *const I2cRegBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const I2cRegBlock {
|
||||
@ -128,9 +137,10 @@ impl Instance for pac::I2ca {
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for pac::I2cb {
|
||||
const IDX: u8 = 1;
|
||||
impl I2cMarker for pac::I2cb {
|
||||
const ID: I2cId = I2cId::B;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
|
||||
const PTR: *const I2cRegBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const I2cRegBlock {
|
||||
@ -290,32 +300,27 @@ impl Sealed for SlaveConfig {}
|
||||
// I2C Base
|
||||
//==================================================================================================
|
||||
|
||||
pub struct I2cBase<I2C> {
|
||||
i2c: I2C,
|
||||
pub struct I2cCommon {
|
||||
id: I2cId,
|
||||
reg_block: *const I2cRegBlock,
|
||||
sys_clk: Hertz,
|
||||
}
|
||||
|
||||
impl<I2C> I2cBase<I2C> {
|
||||
#[inline]
|
||||
fn unwrap_addr(addr: I2cAddress) -> (u16, u32) {
|
||||
match addr {
|
||||
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2c: Instance> I2cBase<I2c> {
|
||||
pub fn new(
|
||||
impl I2cCommon {
|
||||
pub fn new<I2c: I2cMarker>(
|
||||
sys_clk: Hertz,
|
||||
i2c: I2c,
|
||||
_i2c: I2c,
|
||||
speed_mode: I2cSpeed,
|
||||
ms_cfg: Option<&MasterConfig>,
|
||||
sl_cfg: Option<&SlaveConfig>,
|
||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||
enable_peripheral_clock(I2c::PERIPH_SEL);
|
||||
|
||||
let mut i2c_base = I2cBase { i2c, sys_clk };
|
||||
let mut i2c_base = I2cCommon {
|
||||
id: I2c::ID,
|
||||
reg_block: I2c::PTR,
|
||||
sys_clk,
|
||||
};
|
||||
if let Some(ms_cfg) = ms_cfg {
|
||||
i2c_base.cfg_master(ms_cfg);
|
||||
}
|
||||
@ -327,6 +332,32 @@ impl<I2c: Instance> I2cBase<I2c> {
|
||||
Ok(i2c_base)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> I2cId {
|
||||
self.id
|
||||
}
|
||||
|
||||
// Returns the address and the address mode bit.
|
||||
#[inline]
|
||||
fn unwrap_addr(addr: I2cAddress) -> (u16, u32) {
|
||||
match addr {
|
||||
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the raw register block.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents safety guarantees by the HAL.
|
||||
pub const unsafe fn regs(&self) -> &'static I2cRegBlock {
|
||||
self.regs_priv()
|
||||
}
|
||||
|
||||
const fn regs_priv(&self) -> &'static I2cRegBlock {
|
||||
unsafe { &*self.reg_block }
|
||||
}
|
||||
|
||||
fn cfg_master(&mut self, ms_cfg: &MasterConfig) {
|
||||
let (txfemd, rxfemd) = match (ms_cfg.tx_fe_mode, ms_cfg.rx_fe_mode) {
|
||||
(FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false),
|
||||
@ -334,18 +365,17 @@ impl<I2c: Instance> I2cBase<I2c> {
|
||||
(FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false),
|
||||
(FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true),
|
||||
};
|
||||
self.i2c.ctrl().modify(|_, w| {
|
||||
let regs = self.regs_priv();
|
||||
regs.ctrl().modify(|_, w| {
|
||||
w.txfemd().bit(txfemd);
|
||||
w.rxffmd().bit(rxfemd);
|
||||
w.dlgfilter().bit(ms_cfg.dlg_filt);
|
||||
w.algfilter().bit(ms_cfg.alg_filt)
|
||||
});
|
||||
if let Some(ref tm_cfg) = ms_cfg.tm_cfg {
|
||||
self.i2c
|
||||
.tmconfig()
|
||||
.write(|w| unsafe { w.bits(tm_cfg.reg()) });
|
||||
regs.tmconfig().write(|w| unsafe { w.bits(tm_cfg.reg()) });
|
||||
}
|
||||
self.i2c.fifo_clr().write(|w| {
|
||||
regs.fifo_clr().write(|w| {
|
||||
w.rxfifo().set_bit();
|
||||
w.txfifo().set_bit()
|
||||
});
|
||||
@ -358,47 +388,43 @@ impl<I2c: Instance> I2cBase<I2c> {
|
||||
(FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false),
|
||||
(FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true),
|
||||
};
|
||||
self.i2c.s0_ctrl().modify(|_, w| {
|
||||
let regs = self.regs_priv();
|
||||
regs.s0_ctrl().modify(|_, w| {
|
||||
w.txfemd().bit(txfemd);
|
||||
w.rxffmd().bit(rxfemd)
|
||||
});
|
||||
self.i2c.s0_fifo_clr().write(|w| {
|
||||
regs.s0_fifo_clr().write(|w| {
|
||||
w.rxfifo().set_bit();
|
||||
w.txfifo().set_bit()
|
||||
});
|
||||
let max_words = sl_cfg.max_words;
|
||||
if let Some(max_words) = max_words {
|
||||
self.i2c
|
||||
.s0_maxwords()
|
||||
regs.s0_maxwords()
|
||||
.write(|w| unsafe { w.bits((1 << 31) | max_words as u32) });
|
||||
}
|
||||
let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr);
|
||||
// The first bit is the read/write value. Normally, both read and write are matched
|
||||
// using the RWMASK bit of the address mask register
|
||||
self.i2c
|
||||
.s0_address()
|
||||
regs.s0_address()
|
||||
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
|
||||
if let Some(addr_mask) = sl_cfg.addr_mask {
|
||||
self.i2c
|
||||
.s0_addressmask()
|
||||
regs.s0_addressmask()
|
||||
.write(|w| unsafe { w.bits((addr_mask << 1) as u32) });
|
||||
}
|
||||
if let Some(addr_b) = sl_cfg.addr_b {
|
||||
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
|
||||
self.i2c
|
||||
.s0_addressb()
|
||||
regs.s0_addressb()
|
||||
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
|
||||
}
|
||||
if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
|
||||
self.i2c
|
||||
.s0_addressmaskb()
|
||||
regs.s0_addressmaskb()
|
||||
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) });
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn filters(&mut self, digital_filt: bool, analog_filt: bool) {
|
||||
self.i2c.ctrl().modify(|_, w| {
|
||||
self.regs_priv().ctrl().modify(|_, w| {
|
||||
w.dlgfilter().bit(digital_filt);
|
||||
w.algfilter().bit(analog_filt)
|
||||
});
|
||||
@ -406,7 +432,7 @@ impl<I2c: Instance> I2cBase<I2c> {
|
||||
|
||||
#[inline]
|
||||
pub fn fifo_empty_mode(&mut self, rx: FifoEmptyMode, tx: FifoEmptyMode) {
|
||||
self.i2c.ctrl().modify(|_, w| {
|
||||
self.regs_priv().ctrl().modify(|_, w| {
|
||||
w.txfemd().bit(tx as u8 != 0);
|
||||
w.rxffmd().bit(rx as u8 != 0)
|
||||
});
|
||||
@ -429,7 +455,7 @@ impl<I2c: Instance> I2cBase<I2c> {
|
||||
speed_mode: I2cSpeed,
|
||||
) -> Result<(), ClockTooSlowForFastI2cError> {
|
||||
let clk_div = self.calc_clk_div(speed_mode)?;
|
||||
self.i2c
|
||||
self.regs_priv()
|
||||
.clkscale()
|
||||
.write(|w| unsafe { w.bits(((speed_mode as u32) << 31) | clk_div as u32) });
|
||||
Ok(())
|
||||
@ -437,14 +463,14 @@ impl<I2c: Instance> I2cBase<I2c> {
|
||||
|
||||
pub fn load_address(&mut self, addr: u16) {
|
||||
// Load address
|
||||
self.i2c
|
||||
self.regs_priv()
|
||||
.address()
|
||||
.write(|w| unsafe { w.bits((addr << 1) as u32) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stop_cmd(&mut self) {
|
||||
self.i2c
|
||||
self.regs_priv()
|
||||
.cmd()
|
||||
.write(|w| unsafe { w.bits(I2cCmd::Stop as u32) });
|
||||
}
|
||||
@ -454,20 +480,20 @@ impl<I2c: Instance> I2cBase<I2c> {
|
||||
// I2C Master
|
||||
//==================================================================================================
|
||||
|
||||
pub struct I2cMaster<I2c, Addr = SevenBitAddress> {
|
||||
i2c_base: I2cBase<I2c>,
|
||||
pub struct I2cMaster<Addr = SevenBitAddress> {
|
||||
inner: I2cCommon,
|
||||
addr: PhantomData<Addr>,
|
||||
}
|
||||
|
||||
impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
pub fn new(
|
||||
impl<Addr> I2cMaster<Addr> {
|
||||
pub fn new<I2c: I2cMarker>(
|
||||
sysclk: Hertz,
|
||||
i2c: I2c,
|
||||
cfg: MasterConfig,
|
||||
speed_mode: I2cSpeed,
|
||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||
Ok(I2cMaster {
|
||||
i2c_base: I2cBase::new(sysclk, i2c, speed_mode, Some(&cfg), None)?,
|
||||
inner: I2cCommon::new(sysclk, i2c, speed_mode, Some(&cfg), None)?,
|
||||
addr: PhantomData,
|
||||
}
|
||||
.enable_master())
|
||||
@ -475,32 +501,41 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
|
||||
#[inline]
|
||||
pub fn cancel_transfer(&self) {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.cmd()
|
||||
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_fifo(&self) {
|
||||
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.fifo_clr()
|
||||
.write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_rx_fifo(&self) {
|
||||
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.fifo_clr()
|
||||
.write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_master(self) -> Self {
|
||||
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.enable().set_bit());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_master(self) -> Self {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.enable().clear_bit());
|
||||
self
|
||||
@ -508,21 +543,21 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
|
||||
#[inline(always)]
|
||||
fn load_fifo(&self, word: u8) {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.data()
|
||||
.write(|w| unsafe { w.bits(word as u32) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_fifo(&self) -> u8 {
|
||||
self.i2c_base.i2c.data().read().bits() as u8
|
||||
self.inner.regs_priv().data().read().bits() as u8
|
||||
}
|
||||
|
||||
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
|
||||
self.clear_tx_fifo();
|
||||
if *init_cmd == I2cCmd::Start {
|
||||
self.i2c_base.stop_cmd()
|
||||
self.inner.stop_cmd()
|
||||
}
|
||||
}
|
||||
|
||||
@ -534,13 +569,13 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
) -> Result<(), Error> {
|
||||
let mut iter = bytes.into_iter();
|
||||
// Load address
|
||||
let (addr, addr_mode_bit) = I2cBase::<I2c>::unwrap_addr(addr);
|
||||
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||
let (addr, addr_mode_bit) = I2cCommon::unwrap_addr(addr);
|
||||
self.inner.regs_priv().address().write(|w| unsafe {
|
||||
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||
});
|
||||
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.cmd()
|
||||
.write(|w| unsafe { w.bits(init_cmd as u32) });
|
||||
let mut load_if_next_available = || {
|
||||
@ -549,7 +584,7 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
}
|
||||
};
|
||||
loop {
|
||||
let status_reader = self.i2c_base.i2c.status().read();
|
||||
let status_reader = self.inner.regs_priv().status().read();
|
||||
if status_reader.arblost().bit_is_set() {
|
||||
self.error_handler_write(&init_cmd);
|
||||
return Err(Error::ArbitrationLost);
|
||||
@ -584,8 +619,8 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
return Err(Error::DataTooLarge);
|
||||
}
|
||||
// Load number of words
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.words()
|
||||
.write(|w| unsafe { w.bits(len as u32) });
|
||||
let mut bytes = output.iter();
|
||||
@ -614,8 +649,8 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
self.clear_rx_fifo();
|
||||
|
||||
// Load number of words
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.words()
|
||||
.write(|w| unsafe { w.bits(len as u32) });
|
||||
let (addr, addr_mode_bit) = match addr {
|
||||
@ -623,15 +658,15 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||
};
|
||||
// Load address
|
||||
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||
self.inner.regs_priv().address().write(|w| unsafe {
|
||||
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||
});
|
||||
|
||||
let mut buf_iter = buffer.iter_mut();
|
||||
let mut read_bytes = 0;
|
||||
// Start receive transfer
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.cmd()
|
||||
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
|
||||
let mut read_if_next_available = || {
|
||||
@ -640,7 +675,7 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
}
|
||||
};
|
||||
loop {
|
||||
let status_reader = self.i2c_base.i2c.status().read();
|
||||
let status_reader = self.inner.regs_priv().status().read();
|
||||
if status_reader.arblost().bit_is_set() {
|
||||
self.clear_rx_fifo();
|
||||
return Err(Error::ArbitrationLost);
|
||||
@ -664,11 +699,11 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
||||
// Embedded HAL I2C implementations
|
||||
//======================================================================================
|
||||
|
||||
impl<I2c> embedded_hal::i2c::ErrorType for I2cMaster<I2c, SevenBitAddress> {
|
||||
impl embedded_hal::i2c::ErrorType for I2cMaster<SevenBitAddress> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<I2c: Instance> embedded_hal::i2c::I2c for I2cMaster<I2c, SevenBitAddress> {
|
||||
impl embedded_hal::i2c::I2c for I2cMaster<SevenBitAddress> {
|
||||
fn transaction(
|
||||
&mut self,
|
||||
address: SevenBitAddress,
|
||||
@ -688,11 +723,11 @@ impl<I2c: Instance> embedded_hal::i2c::I2c for I2cMaster<I2c, SevenBitAddress> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2c> embedded_hal::i2c::ErrorType for I2cMaster<I2c, TenBitAddress> {
|
||||
impl embedded_hal::i2c::ErrorType for I2cMaster<TenBitAddress> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<I2c: Instance> embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<I2c, TenBitAddress> {
|
||||
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<TenBitAddress> {
|
||||
fn transaction(
|
||||
&mut self,
|
||||
address: TenBitAddress,
|
||||
@ -714,20 +749,20 @@ impl<I2c: Instance> embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<I2c, Ten
|
||||
// I2C Slave
|
||||
//==================================================================================================
|
||||
|
||||
pub struct I2cSlave<I2c, Addr = SevenBitAddress> {
|
||||
i2c_base: I2cBase<I2c>,
|
||||
pub struct I2cSlave<Addr = SevenBitAddress> {
|
||||
inner: I2cCommon,
|
||||
addr: PhantomData<Addr>,
|
||||
}
|
||||
|
||||
impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||
fn new_generic(
|
||||
impl<Addr> I2cSlave<Addr> {
|
||||
fn new_generic<I2c: I2cMarker>(
|
||||
sys_clk: Hertz,
|
||||
i2c: I2c,
|
||||
cfg: SlaveConfig,
|
||||
speed_mode: I2cSpeed,
|
||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||
Ok(I2cSlave {
|
||||
i2c_base: I2cBase::new(sys_clk, i2c, speed_mode, None, Some(&cfg))?,
|
||||
inner: I2cCommon::new(sys_clk, i2c, speed_mode, None, Some(&cfg))?,
|
||||
addr: PhantomData,
|
||||
}
|
||||
.enable_slave())
|
||||
@ -735,8 +770,8 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||
|
||||
#[inline]
|
||||
pub fn enable_slave(self) -> Self {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.s0_ctrl()
|
||||
.modify(|_, w| w.enable().set_bit());
|
||||
self
|
||||
@ -744,8 +779,8 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||
|
||||
#[inline]
|
||||
pub fn disable_slave(self) -> Self {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.s0_ctrl()
|
||||
.modify(|_, w| w.enable().clear_bit());
|
||||
self
|
||||
@ -753,29 +788,29 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||
|
||||
#[inline(always)]
|
||||
fn load_fifo(&self, word: u8) {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.s0_data()
|
||||
.write(|w| unsafe { w.bits(word as u32) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_fifo(&self) -> u8 {
|
||||
self.i2c_base.i2c.s0_data().read().bits() as u8
|
||||
self.inner.regs_priv().s0_data().read().bits() as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clear_tx_fifo(&self) {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.s0_fifo_clr()
|
||||
.write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clear_rx_fifo(&self) {
|
||||
self.i2c_base
|
||||
.i2c
|
||||
self.inner
|
||||
.regs_priv()
|
||||
.s0_fifo_clr()
|
||||
.write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
@ -783,7 +818,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||
/// Get the last address that was matched by the slave control and the corresponding
|
||||
/// master direction
|
||||
pub fn last_address(&self) -> (I2cDirection, u32) {
|
||||
let bits = self.i2c_base.i2c.s0_lastaddress().read().bits();
|
||||
let bits = self.inner.regs_priv().s0_lastaddress().read().bits();
|
||||
match bits & 0x01 {
|
||||
0 => (I2cDirection::Send, bits >> 1),
|
||||
1 => (I2cDirection::Read, bits >> 1),
|
||||
@ -810,7 +845,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||
self.load_fifo(*bytes.next().unwrap());
|
||||
}
|
||||
|
||||
let status_reader = self.i2c_base.i2c.s0_status().read();
|
||||
let status_reader = self.inner.regs_priv().s0_status().read();
|
||||
let mut load_if_next_available = || {
|
||||
if let Some(next_byte) = bytes.next() {
|
||||
self.load_fifo(*next_byte);
|
||||
@ -850,7 +885,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||
}
|
||||
};
|
||||
loop {
|
||||
let status_reader = self.i2c_base.i2c.s0_status().read();
|
||||
let status_reader = self.inner.regs_priv().s0_status().read();
|
||||
if status_reader.idle().bit_is_set() {
|
||||
if read_bytes != len {
|
||||
return Err(Error::InsufficientDataReceived);
|
||||
@ -864,9 +899,9 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2c: Instance> I2cSlave<I2c, SevenBitAddress> {
|
||||
impl I2cSlave<SevenBitAddress> {
|
||||
/// Create a new I2C slave for seven bit addresses
|
||||
pub fn new(
|
||||
pub fn new<I2c: I2cMarker>(
|
||||
sys_clk: Hertz,
|
||||
i2c: I2c,
|
||||
cfg: SlaveConfig,
|
||||
@ -879,8 +914,8 @@ impl<I2c: Instance> I2cSlave<I2c, SevenBitAddress> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
|
||||
pub fn new_ten_bit_addr(
|
||||
impl I2cSlave<TenBitAddress> {
|
||||
pub fn new_ten_bit_addr<I2c: I2cMarker>(
|
||||
sys_clk: Hertz,
|
||||
i2c: I2c,
|
||||
cfg: SlaveConfig,
|
||||
|
@ -8,6 +8,7 @@
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use vorago_shared_periphs::gpio::IoPeriphPin;
|
||||
use vorago_shared_periphs::PeripheralSelect;
|
||||
|
||||
use crate::clock::enable_peripheral_clock;
|
||||
@ -60,7 +61,7 @@ impl<Mode> PwmPin<Mode> {
|
||||
pub fn new<Pin: TimPin, Tim: TimMarker + TimRegInterface>(
|
||||
sys_clk: Hertz,
|
||||
pin_and_tim: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
initial_frequency: Hertz,
|
||||
) -> Result<Self, TimMissmatchError> {
|
||||
if Pin::TIM_ID != Tim::ID {
|
||||
return Err(TimMissmatchError {
|
||||
@ -68,11 +69,12 @@ impl<Mode> PwmPin<Mode> {
|
||||
tim_id: Tim::ID,
|
||||
});
|
||||
}
|
||||
IoPeriphPin::new(Pin::PIN_ID, Pin::FUN_SEL, None);
|
||||
let mut pin = PwmPin {
|
||||
tim_id: Tim::ID,
|
||||
current_duty: 0,
|
||||
current_lower_limit: 0,
|
||||
current_period: initial_period.into(),
|
||||
current_period: initial_frequency,
|
||||
current_rst_val: 0,
|
||||
sys_clk,
|
||||
mode: PhantomData,
|
||||
@ -84,7 +86,7 @@ impl<Mode> PwmPin<Mode> {
|
||||
.tim_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | pin_and_tim.1.mask_32()) });
|
||||
pin.enable_pwm_a();
|
||||
pin.set_period(initial_period);
|
||||
pin.set_period(initial_frequency);
|
||||
Ok(pin)
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,7 @@ pub type SpiRegBlock = pac::spia::RegisterBlock;
|
||||
pub trait SpiMarker: Deref<Target = SpiRegBlock> + Sealed {
|
||||
const ID: SpiId;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
const PTR: *const SpiRegBlock;
|
||||
|
||||
fn ptr() -> *const SpiRegBlock;
|
||||
|
||||
@ -104,6 +105,7 @@ pub trait SpiMarker: Deref<Target = SpiRegBlock> + Sealed {
|
||||
impl SpiMarker for pac::Spia {
|
||||
const ID: SpiId = SpiId::A;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
|
||||
const PTR: *const SpiRegBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const SpiRegBlock {
|
||||
@ -115,6 +117,7 @@ impl Sealed for pac::Spia {}
|
||||
impl SpiMarker for pac::Spib {
|
||||
const ID: SpiId = SpiId::B;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
|
||||
const PTR: *const SpiRegBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const SpiRegBlock {
|
||||
@ -126,6 +129,7 @@ impl Sealed for pac::Spib {}
|
||||
impl SpiMarker for pac::Spic {
|
||||
const ID: SpiId = SpiId::C;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
|
||||
const PTR: *const SpiRegBlock = Self::PTR;
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr() -> *const SpiRegBlock {
|
||||
@ -445,7 +449,7 @@ pub struct SpiIdMissmatchError;
|
||||
/// SPI peripheral driver structure.
|
||||
pub struct Spi<Word = u8> {
|
||||
id: SpiId,
|
||||
reg_block: *mut SpiRegBlock,
|
||||
reg_block: *const SpiRegBlock,
|
||||
cfg: SpiConfig,
|
||||
sys_clk: Hertz,
|
||||
/// Fill word for read-only SPI transactions.
|
||||
@ -535,7 +539,7 @@ where
|
||||
spi.ctrl1().modify(|_, w| w.enable().set_bit());
|
||||
Spi {
|
||||
id: SpiI::ID,
|
||||
reg_block: spi.reg_block(),
|
||||
reg_block: SpiI::PTR,
|
||||
cfg: spi_cfg,
|
||||
sys_clk,
|
||||
fill_word: Default::default(),
|
||||
@ -545,11 +549,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn reg_block_mut(&mut self) -> &'static mut SpiRegBlock {
|
||||
unsafe { &mut *(self.reg_block) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn reg_block(&self) -> &'static SpiRegBlock {
|
||||
unsafe { &*(self.reg_block) }
|
||||
@ -612,7 +611,7 @@ where
|
||||
/// corresponding [HwChipSelectId].
|
||||
#[inline]
|
||||
pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) {
|
||||
self.reg_block_mut().ctrl1().modify(|_, w| {
|
||||
self.reg_block().ctrl1().modify(|_, w| {
|
||||
w.sod().clear_bit();
|
||||
unsafe {
|
||||
w.ss().bits(hw_cs as u8);
|
||||
@ -659,8 +658,8 @@ where
|
||||
}
|
||||
|
||||
fn flush_internal(&mut self) {
|
||||
let reg_block_mut = self.reg_block_mut();
|
||||
let mut status_reg = reg_block_mut.status().read();
|
||||
let reg_block = self.reg_block();
|
||||
let mut status_reg = reg_block.status().read();
|
||||
while status_reg.tfe().bit_is_clear()
|
||||
|| status_reg.rne().bit_is_set()
|
||||
|| status_reg.busy().bit_is_set()
|
||||
@ -668,7 +667,7 @@ where
|
||||
if status_reg.rne().bit_is_set() {
|
||||
self.read_fifo_unchecked();
|
||||
}
|
||||
status_reg = reg_block_mut.status().read();
|
||||
status_reg = reg_block.status().read();
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,9 +682,9 @@ where
|
||||
// The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
|
||||
// initialization. Returns the amount of written bytes.
|
||||
fn initial_send_fifo_pumping_with_words(&mut self, words: &[Word]) -> usize {
|
||||
let reg_block_mut = self.reg_block_mut();
|
||||
let reg_block = self.reg_block();
|
||||
if self.blockmode {
|
||||
reg_block_mut.ctrl1().modify(|_, w| w.mtxpause().set_bit());
|
||||
reg_block.ctrl1().modify(|_, w| w.mtxpause().set_bit());
|
||||
}
|
||||
// Fill the first half of the write FIFO
|
||||
let mut current_write_idx = 0;
|
||||
@ -699,7 +698,7 @@ where
|
||||
current_write_idx += 1;
|
||||
}
|
||||
if self.blockmode {
|
||||
reg_block_mut
|
||||
reg_block
|
||||
.ctrl1()
|
||||
.modify(|_, w| w.mtxpause().clear_bit());
|
||||
}
|
||||
@ -709,9 +708,9 @@ where
|
||||
// The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
|
||||
// initialization.
|
||||
fn initial_send_fifo_pumping_with_fill_words(&mut self, send_len: usize) -> usize {
|
||||
let reg_block_mut = self.reg_block_mut();
|
||||
let reg_block = self.reg_block();
|
||||
if self.blockmode {
|
||||
reg_block_mut.ctrl1().modify(|_, w| w.mtxpause().set_bit());
|
||||
reg_block.ctrl1().modify(|_, w| w.mtxpause().set_bit());
|
||||
}
|
||||
// Fill the first half of the write FIFO
|
||||
let mut current_write_idx = 0;
|
||||
@ -725,7 +724,7 @@ where
|
||||
current_write_idx += 1;
|
||||
}
|
||||
if self.blockmode {
|
||||
reg_block_mut
|
||||
reg_block
|
||||
.ctrl1()
|
||||
.modify(|_, w| w.mtxpause().clear_bit());
|
||||
}
|
||||
@ -739,7 +738,7 @@ where
|
||||
{
|
||||
#[inline(always)]
|
||||
fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> {
|
||||
if self.reg_block_mut().status().read().tnf().bit_is_clear() {
|
||||
if self.reg_block().status().read().tnf().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
self.write_fifo_unchecked(data);
|
||||
@ -748,14 +747,14 @@ where
|
||||
|
||||
#[inline(always)]
|
||||
fn write_fifo_unchecked(&mut self, data: u32) {
|
||||
self.reg_block_mut()
|
||||
self.reg_block()
|
||||
.data()
|
||||
.write(|w| unsafe { w.bits(data) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_fifo(&mut self) -> nb::Result<u32, Infallible> {
|
||||
if self.reg_block_mut().status().read().rne().bit_is_clear() {
|
||||
if self.reg_block().status().read().rne().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
Ok(self.read_fifo_unchecked())
|
||||
@ -763,7 +762,7 @@ where
|
||||
|
||||
#[inline(always)]
|
||||
fn read_fifo_unchecked(&mut self) -> u32 {
|
||||
self.reg_block_mut().data().read().bits()
|
||||
self.reg_block().data().read().bits()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,10 @@ use crate::{
|
||||
};
|
||||
use fugit::RateExtU32;
|
||||
use vorago_shared_periphs::{
|
||||
gpio::{Pin, PinIdProvider},
|
||||
gpio::{Pin, PinId, PinIdProvider},
|
||||
ioconfig::regs::FunSel,
|
||||
sysconfig::enable_peripheral_clock,
|
||||
PeripheralSelect, Port,
|
||||
PeripheralSelect,
|
||||
};
|
||||
|
||||
/// Get the peripheral block of a TIM peripheral given the index.
|
||||
@ -161,8 +161,7 @@ impl CascadeSource {
|
||||
//==================================================================================================
|
||||
|
||||
pub trait TimPin: Sealed {
|
||||
const PORT: Port;
|
||||
const OFFSET: usize;
|
||||
const PIN_ID: PinId;
|
||||
const FUN_SEL: FunSel;
|
||||
const TIM_ID: TimId;
|
||||
}
|
||||
@ -221,8 +220,7 @@ macro_rules! pin_and_tim {
|
||||
where
|
||||
$Px: PinIdProvider,
|
||||
{
|
||||
const PORT: Port = $Px::ID.port();
|
||||
const OFFSET: usize = $Px::ID.offset();
|
||||
const PIN_ID: PinId = $Px::ID;
|
||||
const FUN_SEL: FunSel = $FunSel;
|
||||
const TIM_ID: TimId = TimId($ID);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use va108xx_hal::{
|
||||
const ADT75_I2C_ADDR: u8 = 0b1001000;
|
||||
|
||||
pub struct Adt75TempSensor {
|
||||
sensor_if: I2cMaster<pac::I2ca, SevenBitAddress>,
|
||||
sensor_if: I2cMaster<SevenBitAddress>,
|
||||
cmd_buf: [u8; 1],
|
||||
current_reg: RegAddresses,
|
||||
}
|
||||
|
@ -6,10 +6,6 @@
|
||||
//! which must be provided for async support to work. However, it provides the
|
||||
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
|
||||
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
|
||||
use core::future::Future;
|
||||
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
@ -62,34 +62,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug RTT",
|
||||
"servertype": "jlink",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M0",
|
||||
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||
"preLaunchTask": "rust: cargo build rtt",
|
||||
"serverArgs": [
|
||||
"-jtagconf",
|
||||
"-1,-1"
|
||||
],
|
||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/rtt-log",
|
||||
"interface": "jtag",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
|
@ -31,20 +31,6 @@
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "rust: cargo build rtt",
|
||||
"type": "shell",
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"--example",
|
||||
"rtt-log"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "rust: cargo build systick",
|
||||
"type": "shell",
|
||||
@ -319,4 +305,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user