mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-10-29 21:30:54 +00:00
feat: embassy-lpc55 hal with gpio and pint driver
This commit is contained in:
parent
a23f56b3dd
commit
e7e245eeb7
2
ci.sh
2
ci.sh
@ -171,6 +171,7 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf \
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
||||
@ -227,6 +228,7 @@ cargo batch \
|
||||
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wb \
|
||||
--- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \
|
||||
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wl \
|
||||
--- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/lpc55s69 \
|
||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \
|
||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf9160 \
|
||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns,skip-include --out-dir out/examples/boot/nrf9120 \
|
||||
|
17
embassy-nxp/Cargo.toml
Normal file
17
embassy-nxp/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "embassy-nxp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.7"
|
||||
cortex-m-rt = "0.7.0"
|
||||
critical-section = "1.1.2"
|
||||
embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
|
||||
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
|
||||
lpc55-pac = "0.5.0"
|
||||
defmt = "0.3.8"
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
rt = ["lpc55-pac/rt"]
|
361
embassy-nxp/src/gpio.rs
Normal file
361
embassy-nxp/src/gpio.rs
Normal file
@ -0,0 +1,361 @@
|
||||
use embassy_hal_internal::impl_peripheral;
|
||||
|
||||
use crate::pac_utils::*;
|
||||
use crate::{peripherals, Peripheral, PeripheralRef};
|
||||
|
||||
pub(crate) fn init() {
|
||||
// Enable clocks for GPIO, PINT, and IOCON
|
||||
syscon_reg()
|
||||
.ahbclkctrl0
|
||||
.modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable());
|
||||
}
|
||||
|
||||
/// The GPIO pin level for pins set on "Digital" mode.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum Level {
|
||||
/// Logical low. Corresponds to 0V.
|
||||
Low,
|
||||
/// Logical high. Corresponds to VDD.
|
||||
High,
|
||||
}
|
||||
|
||||
/// Pull setting for a GPIO input set on "Digital" mode.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Pull {
|
||||
/// No pull.
|
||||
None,
|
||||
/// Internal pull-up resistor.
|
||||
Up,
|
||||
/// Internal pull-down resistor.
|
||||
Down,
|
||||
}
|
||||
|
||||
/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum Bank {
|
||||
Bank0 = 0,
|
||||
Bank1 = 1,
|
||||
}
|
||||
|
||||
/// GPIO output driver. Internally, this is a specialized [Flex] pin.
|
||||
pub struct Output<'d> {
|
||||
pub(crate) pin: Flex<'d>,
|
||||
}
|
||||
|
||||
impl<'d> Output<'d> {
|
||||
/// Create GPIO output driver for a [Pin] with the provided [initial output](Level).
|
||||
#[inline]
|
||||
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level) -> Self {
|
||||
let mut pin = Flex::new(pin);
|
||||
pin.set_as_output();
|
||||
let mut result = Self { pin };
|
||||
|
||||
match initial_output {
|
||||
Level::High => result.set_high(),
|
||||
Level::Low => result.set_low(),
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn set_high(&mut self) {
|
||||
gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
|
||||
}
|
||||
|
||||
pub fn set_low(&mut self) {
|
||||
gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
|
||||
}
|
||||
|
||||
pub fn toggle(&mut self) {
|
||||
gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) })
|
||||
}
|
||||
|
||||
/// Get the current output level of the pin. Note that the value returned by this function is
|
||||
/// the voltage level reported by the pin, not the value set by the output driver.
|
||||
pub fn level(&self) -> Level {
|
||||
let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits();
|
||||
if bits & self.pin.bit() != 0 {
|
||||
Level::High
|
||||
} else {
|
||||
Level::Low
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// GPIO input driver. Internally, this is a specialized [Flex] pin.
|
||||
pub struct Input<'d> {
|
||||
pub(crate) pin: Flex<'d>,
|
||||
}
|
||||
|
||||
impl<'d> Input<'d> {
|
||||
/// Create GPIO output driver for a [Pin] with the provided [Pull].
|
||||
#[inline]
|
||||
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, pull: Pull) -> Self {
|
||||
let mut pin = Flex::new(pin);
|
||||
pin.set_as_input();
|
||||
let mut result = Self { pin };
|
||||
result.set_pull(pull);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Set the pull configuration for the pin. To disable the pull, use [Pull::None].
|
||||
pub fn set_pull(&mut self, pull: Pull) {
|
||||
match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), {
|
||||
register.modify(|_, w| match pull {
|
||||
Pull::None => w.mode().inactive(),
|
||||
Pull::Up => w.mode().pull_up(),
|
||||
Pull::Down => w.mode().pull_down(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the current input level of the pin.
|
||||
pub fn read(&self) -> Level {
|
||||
let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits();
|
||||
if bits & self.pin.bit() != 0 {
|
||||
Level::High
|
||||
} else {
|
||||
Level::Low
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a
|
||||
/// reference to a type-erased pin called ["AnyPin"](AnyPin).
|
||||
pub struct Flex<'d> {
|
||||
pub(crate) pin: PeripheralRef<'d, AnyPin>,
|
||||
}
|
||||
|
||||
impl<'d> Flex<'d> {
|
||||
/// Wrap the pin in a `Flex`.
|
||||
///
|
||||
/// Note: you cannot assume that the pin will be in Digital mode after this call.
|
||||
#[inline]
|
||||
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd) -> Self {
|
||||
Self {
|
||||
pin: pin.into_ref().map_into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the bank of this pin. See also [Bank].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use embassy_nxp::gpio::{Bank, Flex};
|
||||
///
|
||||
/// let p = embassy_nxp::init(Default::default());
|
||||
/// let pin = Flex::new(p.PIO1_15);
|
||||
///
|
||||
/// assert_eq!(pin.pin_bank(), Bank::Bank1);
|
||||
/// ```
|
||||
pub fn pin_bank(&self) -> Bank {
|
||||
self.pin.pin_bank()
|
||||
}
|
||||
|
||||
/// Get the number of this pin within its bank. See also [Bank].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use embassy_nxp::gpio::Flex;
|
||||
///
|
||||
/// let p = embassy_nxp::init(Default::default());
|
||||
/// let pin = Flex::new(p.PIO1_15);
|
||||
///
|
||||
/// assert_eq!(pin.pin_number(), 15 as u8);
|
||||
/// ```
|
||||
pub fn pin_number(&self) -> u8 {
|
||||
self.pin.pin_number()
|
||||
}
|
||||
|
||||
/// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note:
|
||||
/// PIOx_0 is bit 0, PIOx_1 is bit 1, etc.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use embassy_nxp::gpio::Flex;
|
||||
///
|
||||
/// let p = embassy_nxp::init(Default::default());
|
||||
/// let pin = Flex::new(p.PIO1_3);
|
||||
///
|
||||
/// assert_eq!(pin.bit(), 0b0000_1000);
|
||||
/// ```
|
||||
pub fn bit(&self) -> u32 {
|
||||
1 << self.pin.pin_number()
|
||||
}
|
||||
|
||||
/// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default
|
||||
/// setting for pins is (usually) non-digital.
|
||||
fn set_as_digital(&mut self) {
|
||||
match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), {
|
||||
register.modify(|_, w| w.digimode().digital());
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the pin in output mode. This implies setting the pin to digital mode, which this
|
||||
/// function handles itself.
|
||||
pub fn set_as_output(&mut self) {
|
||||
self.set_as_digital();
|
||||
gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) })
|
||||
}
|
||||
|
||||
pub fn set_as_input(&mut self) {
|
||||
self.set_as_digital();
|
||||
gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.
|
||||
pub(crate) trait SealedPin: Sized {
|
||||
fn pin_bank(&self) -> Bank;
|
||||
fn pin_number(&self) -> u8;
|
||||
}
|
||||
|
||||
/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an
|
||||
/// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the
|
||||
/// `embassy-nxp` crate due to the [SealedPin] trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + SealedPin + Sized + 'static {
|
||||
/// Degrade to a generic pin struct
|
||||
fn degrade(self) -> AnyPin {
|
||||
AnyPin {
|
||||
pin_bank: self.pin_bank(),
|
||||
pin_number: self.pin_number(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the pin number within a bank
|
||||
#[inline]
|
||||
fn pin(&self) -> u8 {
|
||||
self.pin_number()
|
||||
}
|
||||
|
||||
/// Returns the bank of this pin
|
||||
#[inline]
|
||||
fn bank(&self) -> Bank {
|
||||
self.pin_bank()
|
||||
}
|
||||
}
|
||||
|
||||
/// Type-erased GPIO pin.
|
||||
pub struct AnyPin {
|
||||
pin_bank: Bank,
|
||||
pin_number: u8,
|
||||
}
|
||||
|
||||
impl AnyPin {
|
||||
/// Unsafely create a new type-erased pin.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you’re only using one instance of this type at a time.
|
||||
pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Self {
|
||||
Self { pin_bank, pin_number }
|
||||
}
|
||||
}
|
||||
|
||||
impl_peripheral!(AnyPin);
|
||||
|
||||
impl Pin for AnyPin {}
|
||||
impl SealedPin for AnyPin {
|
||||
#[inline]
|
||||
fn pin_bank(&self) -> Bank {
|
||||
self.pin_bank
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pin_number(&self) -> u8 {
|
||||
self.pin_number
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_pin {
|
||||
($name:ident, $bank:expr, $pin_num:expr) => {
|
||||
impl Pin for peripherals::$name {}
|
||||
impl SealedPin for peripherals::$name {
|
||||
#[inline]
|
||||
fn pin_bank(&self) -> Bank {
|
||||
$bank
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pin_number(&self) -> u8 {
|
||||
$pin_num
|
||||
}
|
||||
}
|
||||
|
||||
impl From<peripherals::$name> for crate::gpio::AnyPin {
|
||||
fn from(val: peripherals::$name) -> Self {
|
||||
crate::gpio::Pin::degrade(val)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_pin!(PIO0_0, Bank::Bank0, 0);
|
||||
impl_pin!(PIO0_1, Bank::Bank0, 1);
|
||||
impl_pin!(PIO0_2, Bank::Bank0, 2);
|
||||
impl_pin!(PIO0_3, Bank::Bank0, 3);
|
||||
impl_pin!(PIO0_4, Bank::Bank0, 4);
|
||||
impl_pin!(PIO0_5, Bank::Bank0, 5);
|
||||
impl_pin!(PIO0_6, Bank::Bank0, 6);
|
||||
impl_pin!(PIO0_7, Bank::Bank0, 7);
|
||||
impl_pin!(PIO0_8, Bank::Bank0, 8);
|
||||
impl_pin!(PIO0_9, Bank::Bank0, 9);
|
||||
impl_pin!(PIO0_10, Bank::Bank0, 10);
|
||||
impl_pin!(PIO0_11, Bank::Bank0, 11);
|
||||
impl_pin!(PIO0_12, Bank::Bank0, 12);
|
||||
impl_pin!(PIO0_13, Bank::Bank0, 13);
|
||||
impl_pin!(PIO0_14, Bank::Bank0, 14);
|
||||
impl_pin!(PIO0_15, Bank::Bank0, 15);
|
||||
impl_pin!(PIO0_16, Bank::Bank0, 16);
|
||||
impl_pin!(PIO0_17, Bank::Bank0, 17);
|
||||
impl_pin!(PIO0_18, Bank::Bank0, 18);
|
||||
impl_pin!(PIO0_19, Bank::Bank0, 19);
|
||||
impl_pin!(PIO0_20, Bank::Bank0, 20);
|
||||
impl_pin!(PIO0_21, Bank::Bank0, 21);
|
||||
impl_pin!(PIO0_22, Bank::Bank0, 22);
|
||||
impl_pin!(PIO0_23, Bank::Bank0, 23);
|
||||
impl_pin!(PIO0_24, Bank::Bank0, 24);
|
||||
impl_pin!(PIO0_25, Bank::Bank0, 25);
|
||||
impl_pin!(PIO0_26, Bank::Bank0, 26);
|
||||
impl_pin!(PIO0_27, Bank::Bank0, 27);
|
||||
impl_pin!(PIO0_28, Bank::Bank0, 28);
|
||||
impl_pin!(PIO0_29, Bank::Bank0, 29);
|
||||
impl_pin!(PIO0_30, Bank::Bank0, 30);
|
||||
impl_pin!(PIO0_31, Bank::Bank0, 31);
|
||||
impl_pin!(PIO1_0, Bank::Bank1, 0);
|
||||
impl_pin!(PIO1_1, Bank::Bank1, 1);
|
||||
impl_pin!(PIO1_2, Bank::Bank1, 2);
|
||||
impl_pin!(PIO1_3, Bank::Bank1, 3);
|
||||
impl_pin!(PIO1_4, Bank::Bank1, 4);
|
||||
impl_pin!(PIO1_5, Bank::Bank1, 5);
|
||||
impl_pin!(PIO1_6, Bank::Bank1, 6);
|
||||
impl_pin!(PIO1_7, Bank::Bank1, 7);
|
||||
impl_pin!(PIO1_8, Bank::Bank1, 8);
|
||||
impl_pin!(PIO1_9, Bank::Bank1, 9);
|
||||
impl_pin!(PIO1_10, Bank::Bank1, 10);
|
||||
impl_pin!(PIO1_11, Bank::Bank1, 11);
|
||||
impl_pin!(PIO1_12, Bank::Bank1, 12);
|
||||
impl_pin!(PIO1_13, Bank::Bank1, 13);
|
||||
impl_pin!(PIO1_14, Bank::Bank1, 14);
|
||||
impl_pin!(PIO1_15, Bank::Bank1, 15);
|
||||
impl_pin!(PIO1_16, Bank::Bank1, 16);
|
||||
impl_pin!(PIO1_17, Bank::Bank1, 17);
|
||||
impl_pin!(PIO1_18, Bank::Bank1, 18);
|
||||
impl_pin!(PIO1_19, Bank::Bank1, 19);
|
||||
impl_pin!(PIO1_20, Bank::Bank1, 20);
|
||||
impl_pin!(PIO1_21, Bank::Bank1, 21);
|
||||
impl_pin!(PIO1_22, Bank::Bank1, 22);
|
||||
impl_pin!(PIO1_23, Bank::Bank1, 23);
|
||||
impl_pin!(PIO1_24, Bank::Bank1, 24);
|
||||
impl_pin!(PIO1_25, Bank::Bank1, 25);
|
||||
impl_pin!(PIO1_26, Bank::Bank1, 26);
|
||||
impl_pin!(PIO1_27, Bank::Bank1, 27);
|
||||
impl_pin!(PIO1_28, Bank::Bank1, 28);
|
||||
impl_pin!(PIO1_29, Bank::Bank1, 29);
|
||||
impl_pin!(PIO1_30, Bank::Bank1, 30);
|
||||
impl_pin!(PIO1_31, Bank::Bank1, 31);
|
95
embassy-nxp/src/lib.rs
Normal file
95
embassy-nxp/src/lib.rs
Normal file
@ -0,0 +1,95 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod gpio;
|
||||
mod pac_utils;
|
||||
pub mod pint;
|
||||
|
||||
pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
||||
pub use lpc55_pac as pac;
|
||||
|
||||
/// Initialize the `embassy-nxp` HAL with the provided configuration.
|
||||
///
|
||||
/// This returns the peripheral singletons that can be used for creating drivers.
|
||||
///
|
||||
/// This should only be called once and at startup, otherwise it panics.
|
||||
pub fn init(_config: config::Config) -> Peripherals {
|
||||
gpio::init();
|
||||
pint::init();
|
||||
|
||||
crate::Peripherals::take()
|
||||
}
|
||||
|
||||
embassy_hal_internal::peripherals! {
|
||||
// External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
|
||||
// peripheral types (e.g. I2C).
|
||||
PIO0_0,
|
||||
PIO0_1,
|
||||
PIO0_2,
|
||||
PIO0_3,
|
||||
PIO0_4,
|
||||
PIO0_5,
|
||||
PIO0_6,
|
||||
PIO0_7,
|
||||
PIO0_8,
|
||||
PIO0_9,
|
||||
PIO0_10,
|
||||
PIO0_11,
|
||||
PIO0_12,
|
||||
PIO0_13,
|
||||
PIO0_14,
|
||||
PIO0_15,
|
||||
PIO0_16,
|
||||
PIO0_17,
|
||||
PIO0_18,
|
||||
PIO0_19,
|
||||
PIO0_20,
|
||||
PIO0_21,
|
||||
PIO0_22,
|
||||
PIO0_23,
|
||||
PIO0_24,
|
||||
PIO0_25,
|
||||
PIO0_26,
|
||||
PIO0_27,
|
||||
PIO0_28,
|
||||
PIO0_29,
|
||||
PIO0_30,
|
||||
PIO0_31,
|
||||
PIO1_0,
|
||||
PIO1_1,
|
||||
PIO1_2,
|
||||
PIO1_3,
|
||||
PIO1_4,
|
||||
PIO1_5,
|
||||
PIO1_6,
|
||||
PIO1_7,
|
||||
PIO1_8,
|
||||
PIO1_9,
|
||||
PIO1_10,
|
||||
PIO1_11,
|
||||
PIO1_12,
|
||||
PIO1_13,
|
||||
PIO1_14,
|
||||
PIO1_15,
|
||||
PIO1_16,
|
||||
PIO1_17,
|
||||
PIO1_18,
|
||||
PIO1_19,
|
||||
PIO1_20,
|
||||
PIO1_21,
|
||||
PIO1_22,
|
||||
PIO1_23,
|
||||
PIO1_24,
|
||||
PIO1_25,
|
||||
PIO1_26,
|
||||
PIO1_27,
|
||||
PIO1_28,
|
||||
PIO1_29,
|
||||
PIO1_30,
|
||||
PIO1_31,
|
||||
}
|
||||
|
||||
/// HAL configuration for the NXP board.
|
||||
pub mod config {
|
||||
#[derive(Default)]
|
||||
pub struct Config {}
|
||||
}
|
323
embassy-nxp/src/pac_utils.rs
Normal file
323
embassy-nxp/src/pac_utils.rs
Normal file
@ -0,0 +1,323 @@
|
||||
/// Get the GPIO register block. This is used to configure all GPIO pins.
|
||||
///
|
||||
/// # Safety
|
||||
/// Due to the type system of peripherals, access to the settings of a single pin is possible only
|
||||
/// by a single thread at a time. Read/Write operations on a single registers are NOT atomic. You
|
||||
/// must ensure that the GPIO registers are not accessed concurrently by multiple threads.
|
||||
pub(crate) fn gpio_reg() -> &'static lpc55_pac::gpio::RegisterBlock {
|
||||
unsafe { &*lpc55_pac::GPIO::ptr() }
|
||||
}
|
||||
|
||||
/// Get the IOCON register block.
|
||||
///
|
||||
/// # Safety
|
||||
/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
|
||||
/// registers are not accessed concurrently by multiple threads.
|
||||
pub(crate) fn iocon_reg() -> &'static lpc55_pac::iocon::RegisterBlock {
|
||||
unsafe { &*lpc55_pac::IOCON::ptr() }
|
||||
}
|
||||
|
||||
/// Get the INPUTMUX register block.
|
||||
///
|
||||
/// # Safety
|
||||
/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
|
||||
/// registers are not accessed concurrently by multiple threads.
|
||||
pub(crate) fn inputmux_reg() -> &'static lpc55_pac::inputmux::RegisterBlock {
|
||||
unsafe { &*lpc55_pac::INPUTMUX::ptr() }
|
||||
}
|
||||
|
||||
/// Get the SYSCON register block.
|
||||
///
|
||||
/// # Safety
|
||||
/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
|
||||
/// registers are not accessed concurrently by multiple threads.
|
||||
pub(crate) fn syscon_reg() -> &'static lpc55_pac::syscon::RegisterBlock {
|
||||
unsafe { &*lpc55_pac::SYSCON::ptr() }
|
||||
}
|
||||
|
||||
/// Get the PINT register block.
|
||||
///
|
||||
/// # Safety
|
||||
/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
|
||||
/// registers are not accessed concurrently by multiple threads.
|
||||
pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock {
|
||||
unsafe { &*lpc55_pac::PINT::ptr() }
|
||||
}
|
||||
|
||||
/// Match the pin bank and number of a pin to the corresponding IOCON register.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use embassy_nxp::gpio::Bank;
|
||||
/// use embassy_nxp::pac_utils::{iocon_reg, match_iocon};
|
||||
///
|
||||
/// // Make pin PIO1_6 digital and set it to pull-down mode.
|
||||
/// match_iocon!(register, iocon_reg(), Bank::Bank1, 6, {
|
||||
/// register.modify(|_, w| w.mode().pull_down().digimode().digital());
|
||||
/// });
|
||||
/// ```
|
||||
macro_rules! match_iocon {
|
||||
($register:ident, $iocon_register:expr, $pin_bank:expr, $pin_number:expr, $action:expr) => {
|
||||
match ($pin_bank, $pin_number) {
|
||||
(Bank::Bank0, 0) => {
|
||||
let $register = &($iocon_register).pio0_0;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 1) => {
|
||||
let $register = &($iocon_register).pio0_1;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 2) => {
|
||||
let $register = &($iocon_register).pio0_2;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 3) => {
|
||||
let $register = &($iocon_register).pio0_3;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 4) => {
|
||||
let $register = &($iocon_register).pio0_4;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 5) => {
|
||||
let $register = &($iocon_register).pio0_5;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 6) => {
|
||||
let $register = &($iocon_register).pio0_6;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 7) => {
|
||||
let $register = &($iocon_register).pio0_7;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 8) => {
|
||||
let $register = &($iocon_register).pio0_8;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 9) => {
|
||||
let $register = &($iocon_register).pio0_9;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 10) => {
|
||||
let $register = &($iocon_register).pio0_10;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 11) => {
|
||||
let $register = &($iocon_register).pio0_11;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 12) => {
|
||||
let $register = &($iocon_register).pio0_12;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 13) => {
|
||||
let $register = &($iocon_register).pio0_13;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 14) => {
|
||||
let $register = &($iocon_register).pio0_14;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 15) => {
|
||||
let $register = &($iocon_register).pio0_15;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 16) => {
|
||||
let $register = &($iocon_register).pio0_16;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 17) => {
|
||||
let $register = &($iocon_register).pio0_17;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 18) => {
|
||||
let $register = &($iocon_register).pio0_18;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 19) => {
|
||||
let $register = &($iocon_register).pio0_19;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 20) => {
|
||||
let $register = &($iocon_register).pio0_20;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 21) => {
|
||||
let $register = &($iocon_register).pio0_21;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 22) => {
|
||||
let $register = &($iocon_register).pio0_22;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 23) => {
|
||||
let $register = &($iocon_register).pio0_23;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 24) => {
|
||||
let $register = &($iocon_register).pio0_24;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 25) => {
|
||||
let $register = &($iocon_register).pio0_25;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 26) => {
|
||||
let $register = &($iocon_register).pio0_26;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 27) => {
|
||||
let $register = &($iocon_register).pio0_27;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 28) => {
|
||||
let $register = &($iocon_register).pio0_28;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 29) => {
|
||||
let $register = &($iocon_register).pio0_29;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 30) => {
|
||||
let $register = &($iocon_register).pio0_30;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank0, 31) => {
|
||||
let $register = &($iocon_register).pio0_31;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 0) => {
|
||||
let $register = &($iocon_register).pio1_0;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 1) => {
|
||||
let $register = &($iocon_register).pio1_1;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 2) => {
|
||||
let $register = &($iocon_register).pio1_2;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 3) => {
|
||||
let $register = &($iocon_register).pio1_3;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 4) => {
|
||||
let $register = &($iocon_register).pio1_4;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 5) => {
|
||||
let $register = &($iocon_register).pio1_5;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 6) => {
|
||||
let $register = &($iocon_register).pio1_6;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 7) => {
|
||||
let $register = &($iocon_register).pio1_7;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 8) => {
|
||||
let $register = &($iocon_register).pio1_8;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 9) => {
|
||||
let $register = &($iocon_register).pio1_9;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 10) => {
|
||||
let $register = &($iocon_register).pio1_10;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 11) => {
|
||||
let $register = &($iocon_register).pio1_11;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 12) => {
|
||||
let $register = &($iocon_register).pio1_12;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 13) => {
|
||||
let $register = &($iocon_register).pio1_13;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 14) => {
|
||||
let $register = &($iocon_register).pio1_14;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 15) => {
|
||||
let $register = &($iocon_register).pio1_15;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 16) => {
|
||||
let $register = &($iocon_register).pio1_16;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 17) => {
|
||||
let $register = &($iocon_register).pio1_17;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 18) => {
|
||||
let $register = &($iocon_register).pio1_18;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 19) => {
|
||||
let $register = &($iocon_register).pio1_19;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 20) => {
|
||||
let $register = &($iocon_register).pio1_20;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 21) => {
|
||||
let $register = &($iocon_register).pio1_21;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 22) => {
|
||||
let $register = &($iocon_register).pio1_22;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 23) => {
|
||||
let $register = &($iocon_register).pio1_23;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 24) => {
|
||||
let $register = &($iocon_register).pio1_24;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 25) => {
|
||||
let $register = &($iocon_register).pio1_25;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 26) => {
|
||||
let $register = &($iocon_register).pio1_26;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 27) => {
|
||||
let $register = &($iocon_register).pio1_27;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 28) => {
|
||||
let $register = &($iocon_register).pio1_28;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 29) => {
|
||||
let $register = &($iocon_register).pio1_29;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 30) => {
|
||||
let $register = &($iocon_register).pio1_30;
|
||||
$action;
|
||||
}
|
||||
(Bank::Bank1, 31) => {
|
||||
let $register = &($iocon_register).pio1_31;
|
||||
$action;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use match_iocon;
|
440
embassy-nxp/src/pint.rs
Normal file
440
embassy-nxp/src/pint.rs
Normal file
@ -0,0 +1,440 @@
|
||||
//! Pin Interrupt module.
|
||||
use core::cell::RefCell;
|
||||
use core::future::Future;
|
||||
use core::pin::Pin as FuturePin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use critical_section::Mutex;
|
||||
use embassy_hal_internal::{Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::gpio::{self, AnyPin, Level, SealedPin};
|
||||
use crate::pac::interrupt;
|
||||
use crate::pac_utils::*;
|
||||
|
||||
struct PinInterrupt {
|
||||
assigned: bool,
|
||||
waker: AtomicWaker,
|
||||
/// If true, the interrupt was triggered due to this PinInterrupt. This is used to determine if
|
||||
/// an [InputFuture] should return Poll::Ready.
|
||||
at_fault: bool,
|
||||
}
|
||||
|
||||
impl PinInterrupt {
|
||||
pub fn interrupt_active(&self) -> bool {
|
||||
self.assigned
|
||||
}
|
||||
|
||||
/// Mark the interrupt as assigned to a pin.
|
||||
pub fn enable(&mut self) {
|
||||
self.assigned = true;
|
||||
self.at_fault = false;
|
||||
}
|
||||
|
||||
/// Mark the interrupt as available.
|
||||
pub fn disable(&mut self) {
|
||||
self.assigned = false;
|
||||
self.at_fault = false;
|
||||
}
|
||||
|
||||
/// Returns true if the interrupt was triggered due to this PinInterrupt.
|
||||
///
|
||||
/// If this function returns true, it will also reset the at_fault flag.
|
||||
pub fn at_fault(&mut self) -> bool {
|
||||
let val = self.at_fault;
|
||||
self.at_fault = false;
|
||||
val
|
||||
}
|
||||
|
||||
/// Set the at_fault flag to true.
|
||||
pub fn fault(&mut self) {
|
||||
self.at_fault = true;
|
||||
}
|
||||
}
|
||||
|
||||
const NEW_PIN_INTERRUPT: PinInterrupt = PinInterrupt {
|
||||
assigned: false,
|
||||
waker: AtomicWaker::new(),
|
||||
at_fault: false,
|
||||
};
|
||||
const INTERUPT_COUNT: usize = 8;
|
||||
static PIN_INTERRUPTS: Mutex<RefCell<[PinInterrupt; INTERUPT_COUNT]>> =
|
||||
Mutex::new(RefCell::new([NEW_PIN_INTERRUPT; INTERUPT_COUNT]));
|
||||
|
||||
fn next_available_interrupt() -> Option<usize> {
|
||||
critical_section::with(|cs| {
|
||||
for (i, pin_interrupt) in PIN_INTERRUPTS.borrow(cs).borrow().iter().enumerate() {
|
||||
if !pin_interrupt.interrupt_active() {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Edge {
|
||||
Rising,
|
||||
Falling,
|
||||
Both,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum InterruptOn {
|
||||
Level(Level),
|
||||
Edge(Edge),
|
||||
}
|
||||
|
||||
pub(crate) fn init() {
|
||||
syscon_reg().ahbclkctrl0.modify(|_, w| w.pint().enable());
|
||||
|
||||
// Enable interrupts
|
||||
unsafe {
|
||||
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT0);
|
||||
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT1);
|
||||
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT2);
|
||||
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT3);
|
||||
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT4);
|
||||
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT5);
|
||||
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6);
|
||||
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7);
|
||||
};
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
struct InputFuture<'d> {
|
||||
#[allow(dead_code)]
|
||||
pin: PeripheralRef<'d, AnyPin>,
|
||||
interrupt_number: usize,
|
||||
}
|
||||
|
||||
impl<'d> InputFuture<'d> {
|
||||
/// Create a new input future. Returns None if all interrupts are in use.
|
||||
fn new(pin: impl Peripheral<P = impl gpio::Pin> + 'd, interrupt_on: InterruptOn) -> Option<Self> {
|
||||
let pin = pin.into_ref().map_into();
|
||||
let interrupt_number = next_available_interrupt()?;
|
||||
|
||||
// Clear interrupt, just in case
|
||||
pint_reg()
|
||||
.rise
|
||||
.write(|w| unsafe { w.rdet().bits(1 << interrupt_number) });
|
||||
pint_reg()
|
||||
.fall
|
||||
.write(|w| unsafe { w.fdet().bits(1 << interrupt_number) });
|
||||
|
||||
// Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number)
|
||||
inputmux_reg().pintsel[interrupt_number]
|
||||
.write(|w| unsafe { w.intpin().bits(32 * pin.pin_bank() as u8 + pin.pin_number()) });
|
||||
|
||||
match interrupt_on {
|
||||
InterruptOn::Level(level) => {
|
||||
// Set pin interrupt register to edge sensitive or level sensitive
|
||||
// 0 = edge sensitive, 1 = level sensitive
|
||||
pint_reg()
|
||||
.isel
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << interrupt_number)) });
|
||||
|
||||
// Enable level interrupt.
|
||||
//
|
||||
// Note: Level sensitive interrupts are enabled by the same register as rising edge
|
||||
// is activated.
|
||||
|
||||
// 0 = no-op, 1 = enable
|
||||
pint_reg()
|
||||
.sienr
|
||||
.write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
|
||||
|
||||
// Set active level
|
||||
match level {
|
||||
Level::Low => {
|
||||
// 0 = no-op, 1 = select LOW
|
||||
pint_reg()
|
||||
.cienf
|
||||
.write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
|
||||
}
|
||||
Level::High => {
|
||||
// 0 = no-op, 1 = select HIGH
|
||||
pint_reg()
|
||||
.sienf
|
||||
.write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
|
||||
}
|
||||
}
|
||||
}
|
||||
InterruptOn::Edge(edge) => {
|
||||
// Set pin interrupt register to edge sensitive or level sensitive
|
||||
// 0 = edge sensitive, 1 = level sensitive
|
||||
pint_reg()
|
||||
.isel
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << interrupt_number)) });
|
||||
|
||||
// Enable rising/falling edge detection
|
||||
match edge {
|
||||
Edge::Rising => {
|
||||
// 0 = no-op, 1 = enable rising edge
|
||||
pint_reg()
|
||||
.sienr
|
||||
.write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
|
||||
// 0 = no-op, 1 = disable falling edge
|
||||
pint_reg()
|
||||
.cienf
|
||||
.write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
|
||||
}
|
||||
Edge::Falling => {
|
||||
// 0 = no-op, 1 = enable falling edge
|
||||
pint_reg()
|
||||
.sienf
|
||||
.write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
|
||||
// 0 = no-op, 1 = disable rising edge
|
||||
pint_reg()
|
||||
.cienr
|
||||
.write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) });
|
||||
}
|
||||
Edge::Both => {
|
||||
// 0 = no-op, 1 = enable
|
||||
pint_reg()
|
||||
.sienr
|
||||
.write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
|
||||
pint_reg()
|
||||
.sienf
|
||||
.write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
||||
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
||||
|
||||
pin_interrupt.enable();
|
||||
});
|
||||
|
||||
Some(Self { pin, interrupt_number })
|
||||
}
|
||||
|
||||
/// Returns true if the interrupt was triggered for this pin.
|
||||
fn interrupt_triggered(&self) -> bool {
|
||||
let interrupt_number = self.interrupt_number;
|
||||
|
||||
// Initially, we determine if the interrupt was triggered by this InputFuture by checking
|
||||
// the flags of the interrupt_number. However, by the time we get to this point, the
|
||||
// interrupt may have been triggered again, so we needed to clear the cpu flags immediately.
|
||||
// As a solution, we mark which [PinInterrupt] is responsible for the interrupt ("at fault")
|
||||
critical_section::with(|cs| {
|
||||
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
||||
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
||||
|
||||
pin_interrupt.at_fault()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for InputFuture<'d> {
|
||||
fn drop(&mut self) {
|
||||
let interrupt_number = self.interrupt_number;
|
||||
|
||||
// Disable pin interrupt
|
||||
// 0 = no-op, 1 = disable
|
||||
pint_reg()
|
||||
.cienr
|
||||
.write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) });
|
||||
pint_reg()
|
||||
.cienf
|
||||
.write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
||||
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
||||
|
||||
pin_interrupt.disable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Future for InputFuture<'d> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let interrupt_number = self.interrupt_number;
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
||||
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
||||
|
||||
pin_interrupt.waker.register(cx.waker());
|
||||
});
|
||||
|
||||
if self.interrupt_triggered() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_interrupt(interrupt_number: usize) {
|
||||
pint_reg()
|
||||
.rise
|
||||
.write(|w| unsafe { w.rdet().bits(1 << interrupt_number) });
|
||||
pint_reg()
|
||||
.fall
|
||||
.write(|w| unsafe { w.fdet().bits(1 << interrupt_number) });
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
||||
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
||||
|
||||
pin_interrupt.fault();
|
||||
pin_interrupt.waker.wake();
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn PIN_INT0() {
|
||||
handle_interrupt(0);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn PIN_INT1() {
|
||||
handle_interrupt(1);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn PIN_INT2() {
|
||||
handle_interrupt(2);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn PIN_INT3() {
|
||||
handle_interrupt(3);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn PIN_INT4() {
|
||||
handle_interrupt(4);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn PIN_INT5() {
|
||||
handle_interrupt(5);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn PIN_INT6() {
|
||||
handle_interrupt(6);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn PIN_INT7() {
|
||||
handle_interrupt(7);
|
||||
}
|
||||
|
||||
impl gpio::Flex<'_> {
|
||||
/// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you
|
||||
/// try to wait for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_any_edge(&mut self) -> Option<()> {
|
||||
InputFuture::new(&mut self.pin, InterruptOn::Edge(Edge::Both))?.await;
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
||||
/// for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_falling_edge(&mut self) -> Option<()> {
|
||||
InputFuture::new(&mut self.pin, InterruptOn::Edge(Edge::Falling))?.await;
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
||||
/// for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_rising_edge(&mut self) -> Option<()> {
|
||||
InputFuture::new(&mut self.pin, InterruptOn::Edge(Edge::Rising))?.await;
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
||||
/// more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_low(&mut self) -> Option<()> {
|
||||
InputFuture::new(&mut self.pin, InterruptOn::Level(Level::Low))?.await;
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
||||
/// more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_high(&mut self) -> Option<()> {
|
||||
InputFuture::new(&mut self.pin, InterruptOn::Level(Level::High))?.await;
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
impl gpio::Input<'_> {
|
||||
/// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you
|
||||
/// try to wait for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_any_edge(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_any_edge().await
|
||||
}
|
||||
|
||||
/// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
||||
/// for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_falling_edge(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_falling_edge().await
|
||||
}
|
||||
|
||||
/// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
||||
/// for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_rising_edge(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_rising_edge().await
|
||||
}
|
||||
|
||||
/// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
||||
/// more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_low(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_low().await
|
||||
}
|
||||
|
||||
/// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
||||
/// more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_high(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_high().await
|
||||
}
|
||||
}
|
||||
|
||||
impl gpio::Output<'_> {
|
||||
/// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you
|
||||
/// try to wait for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_any_edge(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_any_edge().await
|
||||
}
|
||||
|
||||
/// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
||||
/// for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_falling_edge(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_falling_edge().await
|
||||
}
|
||||
|
||||
/// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
||||
/// for more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_rising_edge(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_rising_edge().await
|
||||
}
|
||||
|
||||
/// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
||||
/// more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_low(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_low().await
|
||||
}
|
||||
|
||||
/// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
||||
/// more than 8 pins, this function will return `None`.
|
||||
pub async fn wait_for_high(&mut self) -> Option<()> {
|
||||
self.pin.wait_for_high().await
|
||||
}
|
||||
}
|
8
examples/lpc55s69/.cargo/config.toml
Normal file
8
examples/lpc55s69/.cargo/config.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
runner = "probe-rs run --chip LPC55S69JBD100"
|
||||
|
||||
[build]
|
||||
target = "thumbv8m.main-none-eabihf"
|
||||
|
||||
[env]
|
||||
DEFMT_LOG = "debug"
|
22
examples/lpc55s69/Cargo.toml
Normal file
22
examples/lpc55s69/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "embassy-nxp-lpc55s69-examples"
|
||||
version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
|
||||
[dependencies]
|
||||
embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] }
|
||||
embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
|
||||
embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt"] }
|
||||
panic-halt = "0.2.0"
|
||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||
cortex-m-rt = {version = "0.7.0"}
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||
panic-semihosting = "0.6.0"
|
||||
|
||||
[profile.release]
|
||||
debug = 2
|
35
examples/lpc55s69/build.rs
Normal file
35
examples/lpc55s69/build.rs
Normal file
@ -0,0 +1,35 @@
|
||||
//! This build script copies the `memory.x` file from the crate root into
|
||||
//! a directory where the linker can always find it at build time.
|
||||
//! For many projects this is optional, as the linker always searches the
|
||||
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||
//! are using a workspace or have a more complicated build setup, this
|
||||
//! build script becomes required. Additionally, by requesting that
|
||||
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||
//! updating `memory.x` ensures a rebuild of the application with the
|
||||
//! new memory settings.
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Put `memory.x` in our output directory and ensure it's
|
||||
// on the linker search path.
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// By default, Cargo will re-run a build script whenever
|
||||
// any file in the project changes. By specifying `memory.x`
|
||||
// here, we ensure the build script is only re-run when
|
||||
// `memory.x` is changed.
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
|
||||
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||
}
|
28
examples/lpc55s69/memory.x
Normal file
28
examples/lpc55s69/memory.x
Normal file
@ -0,0 +1,28 @@
|
||||
/* File originally from lpc55-hal repo: https://github.com/lpc55/lpc55-hal/blob/main/memory.x */
|
||||
MEMORY
|
||||
{
|
||||
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
|
||||
|
||||
/* for use with standard link.x */
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
|
||||
/* would be used with proper link.x */
|
||||
/* needs changes to r0 (initialization code) */
|
||||
/* SRAM0 : ORIGIN = 0x20000000, LENGTH = 64K */
|
||||
/* SRAM1 : ORIGIN = 0x20010000, LENGTH = 64K */
|
||||
/* SRAM2 : ORIGIN = 0x20020000, LENGTH = 64K */
|
||||
/* SRAM3 : ORIGIN = 0x20030000, LENGTH = 64K */
|
||||
|
||||
/* CASPER SRAM regions */
|
||||
/* SRAMX0: ORIGIN = 0x1400_0000, LENGTH = 4K /1* to 0x1400_0FFF *1/ */
|
||||
/* SRAMX1: ORIGIN = 0x1400_4000, LENGTH = 4K /1* to 0x1400_4FFF *1/ */
|
||||
|
||||
/* USB1 SRAM regin */
|
||||
/* USB1_SRAM : ORIGIN = 0x40100000, LENGTH = 16K */
|
||||
|
||||
/* To define our own USB RAM section in one regular */
|
||||
/* RAM, probably easiest to shorten length of RAM */
|
||||
/* above, and use this freed RAM section */
|
||||
|
||||
}
|
||||
|
33
examples/lpc55s69/src/bin/blinky_nop.rs
Normal file
33
examples/lpc55s69/src/bin/blinky_nop.rs
Normal file
@ -0,0 +1,33 @@
|
||||
//! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on PIO1_6.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use cortex_m::asm::nop;
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_nxp::gpio::{Level, Output};
|
||||
use {defmt_rtt as _, panic_halt as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_nxp::init(Default::default());
|
||||
|
||||
let mut led = Output::new(p.PIO1_6, Level::Low);
|
||||
|
||||
loop {
|
||||
info!("led off!");
|
||||
led.set_high();
|
||||
|
||||
for _ in 0..200_000 {
|
||||
nop();
|
||||
}
|
||||
|
||||
info!("led on!");
|
||||
led.set_low();
|
||||
|
||||
for _ in 0..200_000 {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
}
|
25
examples/lpc55s69/src/bin/button_executor.rs
Normal file
25
examples/lpc55s69/src/bin/button_executor.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on
|
||||
//! PIO1_6 and a button (labeled "user") on PIO1_9.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_nxp::gpio::{Input, Level, Output, Pull};
|
||||
use {defmt_rtt as _, panic_halt as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let p = embassy_nxp::init(Default::default());
|
||||
|
||||
let mut led = Output::new(p.PIO1_6, Level::Low);
|
||||
let mut button = Input::new(p.PIO1_9, Pull::Up);
|
||||
|
||||
info!("Entered main loop");
|
||||
loop {
|
||||
button.wait_for_rising_edge().await;
|
||||
info!("Button pressed");
|
||||
led.toggle();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user