still a lot to do
This commit is contained in:
parent
2835231f2d
commit
ecf2f4bf11
@ -1,2 +0,0 @@
|
||||
//! GPIO support module.
|
||||
pub use vorago_shared_periphs::gpio::*;
|
366
va108xx-hal/src/gpio/asynch.rs
Normal file
366
va108xx-hal/src/gpio/asynch.rs
Normal file
@ -0,0 +1,366 @@
|
||||
//! # Async GPIO functionality for the VA108xx family.
|
||||
//!
|
||||
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
||||
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
|
||||
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
|
||||
//! 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;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use portable_atomic::AtomicBool;
|
||||
use va108xx::{self as pac};
|
||||
|
||||
use crate::InterruptConfig;
|
||||
|
||||
use super::{
|
||||
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
|
||||
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
|
||||
};
|
||||
|
||||
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] =
|
||||
[const { AtomicWaker::new() }; NUM_PINS_PORT_A];
|
||||
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] =
|
||||
[const { AtomicWaker::new() }; NUM_PINS_PORT_B];
|
||||
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
|
||||
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A];
|
||||
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
|
||||
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
|
||||
|
||||
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
|
||||
///
|
||||
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
|
||||
/// matching the [Port] argument.
|
||||
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
|
||||
/// as well as update the static edge detection structures. This allows the pin future tocomplete
|
||||
/// complete async operations.
|
||||
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
|
||||
let periphs = unsafe { pac::Peripherals::steal() };
|
||||
|
||||
let (irq_enb, edge_status, wakers, edge_detection) = match port {
|
||||
Port::A => (
|
||||
periphs.porta.irq_enb().read().bits(),
|
||||
periphs.porta.edge_status().read().bits(),
|
||||
WAKERS_FOR_PORT_A.as_ref(),
|
||||
EDGE_DETECTION_PORT_A.as_ref(),
|
||||
),
|
||||
Port::B => (
|
||||
periphs.portb.irq_enb().read().bits(),
|
||||
periphs.portb.edge_status().read().bits(),
|
||||
WAKERS_FOR_PORT_B.as_ref(),
|
||||
EDGE_DETECTION_PORT_B.as_ref(),
|
||||
),
|
||||
};
|
||||
|
||||
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn on_interrupt_for_port(
|
||||
mut irq_enb: u32,
|
||||
edge_status: u32,
|
||||
wakers: &'static [AtomicWaker],
|
||||
edge_detection: &'static [AtomicBool],
|
||||
) {
|
||||
while irq_enb != 0 {
|
||||
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||
let bit_mask = 1 << bit_pos;
|
||||
|
||||
wakers[bit_pos].wake();
|
||||
|
||||
if edge_status & bit_mask != 0 {
|
||||
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Clear the processed bit
|
||||
irq_enb &= !bit_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Input pin future which implements the [Future] trait.
|
||||
///
|
||||
/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this
|
||||
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
|
||||
/// struture is granted to allow writing custom async structures.
|
||||
pub struct InputPinFuture {
|
||||
pin_id: DynPinId,
|
||||
waker_group: &'static [AtomicWaker],
|
||||
edge_detection_group: &'static [AtomicBool],
|
||||
}
|
||||
|
||||
impl InputPinFuture {
|
||||
#[inline]
|
||||
pub fn pin_group_to_waker_and_edge_detection_group(
|
||||
group: Port,
|
||||
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
||||
match group {
|
||||
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
|
||||
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_dyn_pin(
|
||||
pin: &mut DynPin,
|
||||
irq: pac::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
) -> Result<Self, InvalidPinTypeError> {
|
||||
if !pin.is_input_pin() {
|
||||
return Err(InvalidPinTypeError(pin.mode()));
|
||||
}
|
||||
|
||||
let (waker_group, edge_detection_group) =
|
||||
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
edge_detection_group[pin.id().num() as usize]
|
||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.configure_edge_interrupt(edge).unwrap();
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
Ok(Self {
|
||||
pin_id: pin.id(),
|
||||
waker_group,
|
||||
edge_detection_group,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
||||
pin: &mut Pin<I, pin::Input<C>>,
|
||||
irq: pac::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
) -> Self {
|
||||
let (waker_group, edge_detection_group) =
|
||||
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
edge_detection_group[pin.id().num() as usize]
|
||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.configure_edge_interrupt(edge);
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
Self {
|
||||
pin_id: pin.id(),
|
||||
edge_detection_group,
|
||||
waker_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InputPinFuture {
|
||||
fn drop(&mut self) {
|
||||
// The API ensures that we actually own the pin, so stealing it here is okay.
|
||||
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for InputPinFuture {
|
||||
type Output = ();
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
let idx = self.pin_id.num() as usize;
|
||||
self.waker_group[idx].register(cx.waker());
|
||||
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||
return core::task::Poll::Ready(());
|
||||
}
|
||||
core::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputDynPinAsync {
|
||||
pin: DynPin,
|
||||
irq: pac::Interrupt,
|
||||
}
|
||||
|
||||
impl InputDynPinAsync {
|
||||
/// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be
|
||||
/// passed as well and is used to route and enable the interrupt.
|
||||
///
|
||||
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
|
||||
if !pin.is_input_pin() {
|
||||
return Err(InvalidPinTypeError(pin.mode()));
|
||||
}
|
||||
Ok(Self { pin, irq })
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is high.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
let fut =
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||
.unwrap();
|
||||
if self.pin.is_high().unwrap() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is low.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
let fut =
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||
.unwrap();
|
||||
if self.pin.is_low().unwrap() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees a falling edge.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees a rising edge.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> DynPin {
|
||||
self.pin
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for InputDynPinAsync {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl Wait for InputDynPinAsync {
|
||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_high().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_low().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_rising_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_falling_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_any_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputPinAsync<I: PinId, C: InputConfig> {
|
||||
pin: Pin<I, pin::Input<C>>,
|
||||
irq: pac::Interrupt,
|
||||
}
|
||||
|
||||
impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
||||
/// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be
|
||||
/// passed as well and is used to route and enable the interrupt.
|
||||
///
|
||||
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
||||
Self { pin, irq }
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is high.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
||||
if self.pin.is_high() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is low.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
||||
if self.pin.is_low() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees falling edge.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees rising edge.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
||||
self.pin
|
||||
}
|
||||
}
|
||||
impl<I: PinId, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl<I: PinId, C: InputConfig> Wait for InputPinAsync<I, C> {
|
||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_high().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_low().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_rising_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_falling_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_any_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
7
va108xx-hal/src/gpio/mod.rs
Normal file
7
va108xx-hal/src/gpio/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! GPIO support module.
|
||||
pub use vorago_shared_periphs::gpio::*;
|
||||
|
||||
/// Low-level GPIO access.
|
||||
pub use vorago_shared_periphs::gpio::ll;
|
||||
|
||||
pub mod asynch;
|
@ -1,366 +0,0 @@
|
||||
//! # Async GPIO functionality for the VA108xx family.
|
||||
//!
|
||||
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
||||
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
|
||||
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
|
||||
//! 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;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use portable_atomic::AtomicBool;
|
||||
use va108xx::{self as pac};
|
||||
|
||||
use crate::InterruptConfig;
|
||||
|
||||
use super::{
|
||||
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
|
||||
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
|
||||
};
|
||||
|
||||
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] =
|
||||
[const { AtomicWaker::new() }; NUM_PINS_PORT_A];
|
||||
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] =
|
||||
[const { AtomicWaker::new() }; NUM_PINS_PORT_B];
|
||||
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
|
||||
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A];
|
||||
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
|
||||
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
|
||||
|
||||
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
|
||||
///
|
||||
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
|
||||
/// matching the [Port] argument.
|
||||
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
|
||||
/// as well as update the static edge detection structures. This allows the pin future tocomplete
|
||||
/// complete async operations.
|
||||
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
|
||||
let periphs = unsafe { pac::Peripherals::steal() };
|
||||
|
||||
let (irq_enb, edge_status, wakers, edge_detection) = match port {
|
||||
Port::A => (
|
||||
periphs.porta.irq_enb().read().bits(),
|
||||
periphs.porta.edge_status().read().bits(),
|
||||
WAKERS_FOR_PORT_A.as_ref(),
|
||||
EDGE_DETECTION_PORT_A.as_ref(),
|
||||
),
|
||||
Port::B => (
|
||||
periphs.portb.irq_enb().read().bits(),
|
||||
periphs.portb.edge_status().read().bits(),
|
||||
WAKERS_FOR_PORT_B.as_ref(),
|
||||
EDGE_DETECTION_PORT_B.as_ref(),
|
||||
),
|
||||
};
|
||||
|
||||
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn on_interrupt_for_port(
|
||||
mut irq_enb: u32,
|
||||
edge_status: u32,
|
||||
wakers: &'static [AtomicWaker],
|
||||
edge_detection: &'static [AtomicBool],
|
||||
) {
|
||||
while irq_enb != 0 {
|
||||
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||
let bit_mask = 1 << bit_pos;
|
||||
|
||||
wakers[bit_pos].wake();
|
||||
|
||||
if edge_status & bit_mask != 0 {
|
||||
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Clear the processed bit
|
||||
irq_enb &= !bit_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Input pin future which implements the [Future] trait.
|
||||
///
|
||||
/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this
|
||||
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
|
||||
/// struture is granted to allow writing custom async structures.
|
||||
pub struct InputPinFuture {
|
||||
pin_id: DynPinId,
|
||||
waker_group: &'static [AtomicWaker],
|
||||
edge_detection_group: &'static [AtomicBool],
|
||||
}
|
||||
|
||||
impl InputPinFuture {
|
||||
#[inline]
|
||||
pub fn pin_group_to_waker_and_edge_detection_group(
|
||||
group: Port,
|
||||
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
||||
match group {
|
||||
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
|
||||
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_dyn_pin(
|
||||
pin: &mut DynPin,
|
||||
irq: pac::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
) -> Result<Self, InvalidPinTypeError> {
|
||||
if !pin.is_input_pin() {
|
||||
return Err(InvalidPinTypeError(pin.mode()));
|
||||
}
|
||||
|
||||
let (waker_group, edge_detection_group) =
|
||||
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
edge_detection_group[pin.id().num() as usize]
|
||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.configure_edge_interrupt(edge).unwrap();
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
Ok(Self {
|
||||
pin_id: pin.id(),
|
||||
waker_group,
|
||||
edge_detection_group,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
||||
pin: &mut Pin<I, pin::Input<C>>,
|
||||
irq: pac::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
) -> Self {
|
||||
let (waker_group, edge_detection_group) =
|
||||
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
edge_detection_group[pin.id().num() as usize]
|
||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.configure_edge_interrupt(edge);
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
Self {
|
||||
pin_id: pin.id(),
|
||||
edge_detection_group,
|
||||
waker_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InputPinFuture {
|
||||
fn drop(&mut self) {
|
||||
// The API ensures that we actually own the pin, so stealing it here is okay.
|
||||
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for InputPinFuture {
|
||||
type Output = ();
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
let idx = self.pin_id.num() as usize;
|
||||
self.waker_group[idx].register(cx.waker());
|
||||
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||
return core::task::Poll::Ready(());
|
||||
}
|
||||
core::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputDynPinAsync {
|
||||
pin: DynPin,
|
||||
irq: pac::Interrupt,
|
||||
}
|
||||
|
||||
impl InputDynPinAsync {
|
||||
/// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be
|
||||
/// passed as well and is used to route and enable the interrupt.
|
||||
///
|
||||
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
|
||||
if !pin.is_input_pin() {
|
||||
return Err(InvalidPinTypeError(pin.mode()));
|
||||
}
|
||||
Ok(Self { pin, irq })
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is high.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
let fut =
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||
.unwrap();
|
||||
if self.pin.is_high().unwrap() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is low.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
let fut =
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||
.unwrap();
|
||||
if self.pin.is_low().unwrap() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees a falling edge.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees a rising edge.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> DynPin {
|
||||
self.pin
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for InputDynPinAsync {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl Wait for InputDynPinAsync {
|
||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_high().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_low().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_rising_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_falling_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_any_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputPinAsync<I: PinId, C: InputConfig> {
|
||||
pin: Pin<I, pin::Input<C>>,
|
||||
irq: pac::Interrupt,
|
||||
}
|
||||
|
||||
impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
||||
/// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be
|
||||
/// passed as well and is used to route and enable the interrupt.
|
||||
///
|
||||
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
||||
Self { pin, irq }
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is high.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
||||
if self.pin.is_high() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is low.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
||||
if self.pin.is_low() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees falling edge.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees rising edge.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
||||
self.pin
|
||||
}
|
||||
}
|
||||
impl<I: PinId, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl<I: PinId, C: InputConfig> Wait for InputPinAsync<I, C> {
|
||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_high().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_low().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_rising_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_falling_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_any_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -19,6 +19,9 @@ pub mod uart;
|
||||
|
||||
pub use vorago_shared_periphs::FunSel;
|
||||
|
||||
/// This is the NONE destination reigster value for the IRQSEL peripheral.
|
||||
pub const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PeripheralSelect {
|
||||
@ -37,32 +40,6 @@ pub enum PeripheralSelect {
|
||||
Gpio = 24,
|
||||
}
|
||||
|
||||
/// Generic interrupt config which can be used to specify whether the HAL driver will
|
||||
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
||||
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
|
||||
/// perform those steps themselves.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct InterruptConfig {
|
||||
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
||||
pub id: pac::Interrupt,
|
||||
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral.
|
||||
pub route: bool,
|
||||
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for
|
||||
/// multiple purposes, the user can enable the interrupts themselves.
|
||||
pub enable_in_nvic: bool,
|
||||
}
|
||||
|
||||
impl InterruptConfig {
|
||||
pub fn new(id: pac::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
|
||||
InterruptConfig {
|
||||
id,
|
||||
route,
|
||||
enable_in_nvic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type IrqCfg = InterruptConfig;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use vorago_shared_periphs::gpio::{Pin, PinId, Port};
|
||||
pub use vorago_shared_periphs::gpio::{Pin, PinIdProvider, Port};
|
||||
|
||||
use crate::{sysconfig::reset_peripheral_for_cycles, PeripheralSelect};
|
||||
|
||||
@ -19,7 +19,7 @@ macro_rules! pin_id {
|
||||
};
|
||||
}
|
||||
|
||||
impl<I: PinId + crate::sealed::Sealed> crate::sealed::Sealed for Pin<I> {}
|
||||
impl<I: PinIdProvider + crate::sealed::Sealed> crate::sealed::Sealed for Pin<I> {}
|
||||
|
||||
pin_id!(Pa0, Port::A, 0);
|
||||
pin_id!(Pa1, Port::A, 1);
|
||||
|
@ -11,191 +11,39 @@ use core::marker::PhantomData;
|
||||
use crate::clock::enable_peripheral_clock;
|
||||
use crate::pac;
|
||||
use crate::time::Hertz;
|
||||
use crate::timer::{Tim, TimPeripheralMarker, TimPin, TimRegInterface};
|
||||
use crate::timer::{TimId, TimPeripheralMarker, TimPin, TimRegInterface};
|
||||
|
||||
const DUTY_MAX: u16 = u16::MAX;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub(crate) struct PwmCommon {}
|
||||
|
||||
enum StatusSelPwm {
|
||||
PwmA = 3,
|
||||
PwmB = 4,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PwmA {}
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PwmB {}
|
||||
|
||||
//==================================================================================================
|
||||
// Strongly typed PWM pin
|
||||
//==================================================================================================
|
||||
|
||||
/*
|
||||
pub struct PwmPin<Pin: TimPin, Tim: TimMarker, Mode = PwmA> {
|
||||
pin_and_tim: (Pin, Tim),
|
||||
inner: ReducedPwmPin<Mode>,
|
||||
mode: PhantomData<Mode>,
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[error("pin tim ID {pin_tim:?} and timer tim id {tim_id:?} do not match")]
|
||||
pub struct TimMissmatchError {
|
||||
pin_tim: TimId,
|
||||
tim_id: TimId,
|
||||
}
|
||||
|
||||
impl<Pin: TimPin, Tim: ValidTim, Mode> PwmPin<Pin, Tim, Mode>
|
||||
where
|
||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||
{
|
||||
/// Create a new stronlgy typed PWM pin
|
||||
pub fn new(
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
pin_and_tim: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
) -> Self {
|
||||
let mut pin = PwmPin {
|
||||
pin_and_tim,
|
||||
inner: ReducedPwmPin::<Mode>::new(
|
||||
Tim::TIM_ID,
|
||||
Pin::DYN,
|
||||
PwmCommon {
|
||||
current_duty: 0,
|
||||
current_lower_limit: 0,
|
||||
current_period: initial_period.into(),
|
||||
current_rst_val: 0,
|
||||
sys_clk: sys_clk.into(),
|
||||
},
|
||||
),
|
||||
//unsafe { TimAndPin::new(tim_and_pin.0, tim_and_pin.1) },
|
||||
mode: PhantomData,
|
||||
};
|
||||
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
|
||||
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig);
|
||||
sys_cfg
|
||||
.tim_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | pin.pin_and_tim.1.mask_32()) });
|
||||
pin.enable_pwm_a();
|
||||
pin.set_period(initial_period);
|
||||
pin
|
||||
}
|
||||
|
||||
pub fn downgrade(self) -> ReducedPwmPin<Mode> {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn release(self) -> (Pin, Tim) {
|
||||
self.pin_and_tim
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn enable_pwm_a(&mut self) {
|
||||
self.inner.enable_pwm_a();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn enable_pwm_b(&mut self) {
|
||||
self.inner.enable_pwm_b();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_period(&self) -> Hertz {
|
||||
self.inner.get_period()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_period(&mut self, period: impl Into<Hertz>) {
|
||||
self.inner.set_period(period);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.inner.disable();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.inner.enable();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn period(&self) -> Hertz {
|
||||
self.inner.period()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn duty(&self) -> u16 {
|
||||
self.inner.duty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB>
|
||||
where
|
||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||
{
|
||||
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
|
||||
let mut pwmb = Self {
|
||||
mode: PhantomData,
|
||||
pin_and_tim: other.pin_and_tim,
|
||||
inner: other.inner.into(),
|
||||
};
|
||||
pwmb.enable_pwm_b();
|
||||
pwmb
|
||||
}
|
||||
}
|
||||
|
||||
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM, PwmB>> for PwmPin<PIN, TIM, PwmA>
|
||||
where
|
||||
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||
{
|
||||
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
|
||||
let mut pwma = Self {
|
||||
mode: PhantomData,
|
||||
pin_and_tim: other.pin_and_tim,
|
||||
inner: other.inner.into(),
|
||||
};
|
||||
pwma.enable_pwm_a();
|
||||
pwma
|
||||
}
|
||||
}
|
||||
|
||||
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmA>
|
||||
where
|
||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||
{
|
||||
pub fn pwma(
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
pin_and_tim: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
) -> Self {
|
||||
let mut pin: PwmPin<Pin, Tim, PwmA> =
|
||||
Self::new(sys_cfg, sys_clk, pin_and_tim, initial_period);
|
||||
pin.enable_pwm_a();
|
||||
pin
|
||||
}
|
||||
}
|
||||
|
||||
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
|
||||
where
|
||||
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||
{
|
||||
pub fn pwmb(
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
pin_and_tim: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
) -> Self {
|
||||
let mut pin: PwmPin<Pin, Tim, PwmB> =
|
||||
Self::new(sys_cfg, sys_clk, pin_and_tim, initial_period);
|
||||
pin.enable_pwm_b();
|
||||
pin
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//==================================================================================================
|
||||
// PWM pin
|
||||
//==================================================================================================
|
||||
|
||||
/// Reduced version where type information is deleted
|
||||
pub struct PwmPin<Mode = PwmA> {
|
||||
tim: Tim,
|
||||
tim_id: TimId,
|
||||
sys_clk: Hertz,
|
||||
/// For PWMB, this is the upper limit
|
||||
current_duty: u16,
|
||||
@ -208,14 +56,20 @@ pub struct PwmPin<Mode = PwmA> {
|
||||
|
||||
impl<Mode> PwmPin<Mode> {
|
||||
/// Create a new strongly typed PWM pin
|
||||
pub fn new<Pin: TimPin, Tim: TimPeripheralMarker>(
|
||||
pub fn new<Pin: TimPin, Tim: TimPeripheralMarker + TimRegInterface>(
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
pin_and_tim: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
) -> Self {
|
||||
) -> Result<Self, TimMissmatchError> {
|
||||
if Pin::TIM_ID != Tim::ID {
|
||||
return Err(TimMissmatchError {
|
||||
pin_tim: Pin::TIM_ID,
|
||||
tim_id: Tim::ID,
|
||||
});
|
||||
}
|
||||
let mut pin = PwmPin {
|
||||
tim: Tim::TIM,
|
||||
tim_id: Tim::ID,
|
||||
current_duty: 0,
|
||||
current_lower_limit: 0,
|
||||
current_period: initial_period.into(),
|
||||
@ -227,15 +81,15 @@ impl<Mode> PwmPin<Mode> {
|
||||
enable_peripheral_clock(crate::clock::PeripheralClocks::Ioconfig);
|
||||
sys_cfg
|
||||
.tim_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | pin.pin_and_tim.1.mask_32()) });
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | pin_and_tim.1.mask_32()) });
|
||||
pin.enable_pwm_a();
|
||||
pin.set_period(initial_period);
|
||||
pin
|
||||
Ok(pin)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn enable_pwm_a(&mut self) {
|
||||
self.tim
|
||||
self.tim_id
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
|
||||
@ -243,7 +97,7 @@ impl<Mode> PwmPin<Mode> {
|
||||
|
||||
#[inline]
|
||||
fn enable_pwm_b(&mut self) {
|
||||
self.tim
|
||||
self.tim_id
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
|
||||
@ -261,8 +115,8 @@ impl<Mode> PwmPin<Mode> {
|
||||
if self.current_period.raw() == 0 {
|
||||
return;
|
||||
}
|
||||
self.current_rst_val = self.common.sys_clk.raw() / self.common.current_period.raw();
|
||||
self.tim
|
||||
self.current_rst_val = self.sys_clk.raw() / self.current_period.raw();
|
||||
self.tim_id
|
||||
.reg_block()
|
||||
.rst_value()
|
||||
.write(|w| unsafe { w.bits(self.current_rst_val) });
|
||||
@ -270,7 +124,7 @@ impl<Mode> PwmPin<Mode> {
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.tim
|
||||
self.tim_id
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.enable().clear_bit());
|
||||
@ -278,7 +132,7 @@ impl<Mode> PwmPin<Mode> {
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.tim
|
||||
self.tim_id
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.enable().set_bit());
|
||||
@ -299,7 +153,7 @@ impl From<PwmPin<PwmA>> for PwmPin<PwmB> {
|
||||
fn from(other: PwmPin<PwmA>) -> Self {
|
||||
let mut pwmb = Self {
|
||||
mode: PhantomData,
|
||||
tim: other.tim,
|
||||
tim_id: other.tim_id,
|
||||
sys_clk: other.sys_clk,
|
||||
current_duty: other.current_duty,
|
||||
current_lower_limit: other.current_lower_limit,
|
||||
@ -315,7 +169,7 @@ impl From<PwmPin<PwmB>> for PwmPin<PwmA> {
|
||||
fn from(other: PwmPin<PwmB>) -> Self {
|
||||
let mut pwmb = Self {
|
||||
mode: PhantomData,
|
||||
tim: other.tim,
|
||||
tim_id: other.tim_id,
|
||||
sys_clk: other.sys_clk,
|
||||
current_duty: other.current_duty,
|
||||
current_lower_limit: other.current_lower_limit,
|
||||
@ -353,7 +207,7 @@ impl PwmPin<PwmB> {
|
||||
self.current_lower_limit = duty;
|
||||
let pwmb_val: u64 =
|
||||
(self.current_rst_val as u64 * self.current_lower_limit as u64) / DUTY_MAX as u64;
|
||||
self.tim
|
||||
self.tim_id
|
||||
.reg_block()
|
||||
.pwmb_value()
|
||||
.write(|w| unsafe { w.bits(pwmb_val as u32) });
|
||||
@ -369,7 +223,7 @@ impl PwmPin<PwmB> {
|
||||
self.current_duty = duty;
|
||||
let pwma_val: u64 =
|
||||
(self.current_rst_val as u64 * self.current_duty as u64) / DUTY_MAX as u64;
|
||||
self.tim
|
||||
self.tim_id
|
||||
.reg_block()
|
||||
.pwma_value()
|
||||
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||
@ -396,7 +250,7 @@ impl embedded_hal::pwm::SetDutyCycle for PwmPin {
|
||||
let pwma_val: u64 = (self.current_rst_val as u64
|
||||
* (DUTY_MAX as u64 - self.current_duty as u64))
|
||||
/ DUTY_MAX as u64;
|
||||
self.tim
|
||||
self.tim_id
|
||||
.reg_block()
|
||||
.pwma_value()
|
||||
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||
|
@ -50,6 +50,12 @@ pub enum SpiId {
|
||||
}
|
||||
|
||||
impl SpiId {
|
||||
/// Unsafely steal a peripheral MMIO block for the given UART.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents ownership and safety guarantees by the HAL which can lead to data races
|
||||
/// on cuncurrent usage.
|
||||
pub unsafe fn reg_block(&self) -> &'static SpiRegBlock {
|
||||
unsafe {
|
||||
match self {
|
||||
|
@ -57,10 +57,10 @@ macro_rules! hw_cs_multi_pin {
|
||||
$cs_id:path
|
||||
) => {
|
||||
#[doc = concat!(
|
||||
"Newtype wrapper to use [Pin] [`", stringify!($pin_ty),
|
||||
"`] as a HW CS pin for [`", stringify!($spi_id),
|
||||
"`] with [`", stringify!($cs_id), "`]."
|
||||
)]
|
||||
"Newtype wrapper to use [Pin] [`", stringify!($pin_ty),
|
||||
"`] as a HW CS pin for [`", stringify!($spi_id),
|
||||
"`] with [`", stringify!($cs_id), "`]."
|
||||
)]
|
||||
pub struct $name(Pin<$pin_id>);
|
||||
|
||||
impl $name {
|
||||
|
@ -6,7 +6,6 @@
|
||||
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
|
||||
pub use crate::InterruptConfig;
|
||||
use crate::{
|
||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||
enable_nvic_interrupt,
|
||||
pac::{self, tim0},
|
||||
pins::{
|
||||
@ -26,7 +25,6 @@ use vorago_shared_periphs::{
|
||||
Port,
|
||||
};
|
||||
|
||||
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||
|
||||
/// Get the peripheral block of a TIM peripheral given the index.
|
||||
@ -165,23 +163,25 @@ impl CascadeSource {
|
||||
// Valid TIM and PIN combinations
|
||||
//==================================================================================================
|
||||
|
||||
pub trait TimPin {
|
||||
pub trait TimPin: Sealed {
|
||||
const PORT: Port;
|
||||
const OFFSET: usize;
|
||||
const FUN_SEL: FunSel;
|
||||
const TIM: Tim;
|
||||
const TIM_ID: TimId;
|
||||
}
|
||||
|
||||
pub trait TimPeripheralMarker {
|
||||
pub trait TimPeripheralMarker: Sealed {
|
||||
// TIM ID ranging from 0 to 23 for 24 TIM peripherals
|
||||
const TIM: Tim;
|
||||
const ID: TimId;
|
||||
}
|
||||
|
||||
macro_rules! tim_marker {
|
||||
($TIMX:path, $ID:expr) => {
|
||||
impl TimPeripheralMarker for $TIMX {
|
||||
const TIM: Tim = Tim($ID);
|
||||
const ID: TimId = TimId($ID);
|
||||
}
|
||||
|
||||
impl Sealed for $TIMX {}
|
||||
};
|
||||
}
|
||||
|
||||
@ -212,77 +212,6 @@ tim_marker!(pac::Tim23, 23);
|
||||
|
||||
pub trait ValidTimAndPin<Pin: TimPin, Tim: TimPeripheralMarker>: Sealed {}
|
||||
|
||||
/*
|
||||
macro_rules! pin_and_tim {
|
||||
($PAX:ident, $ALTFUNC:ident, $ID:expr, $TIMX:path) => {
|
||||
impl TimPin for Pin<$PAX, $ALTFUNC>
|
||||
where
|
||||
$PAX: PinId,
|
||||
{
|
||||
const DYN: DynPinId = $PAX::DYN;
|
||||
}
|
||||
|
||||
impl<PIN: TimPin, TIM: ValidTim> ValidTimAndPin<PIN, TIM> for (Pin<$PAX, $ALTFUNC>, $TIMX)
|
||||
where
|
||||
Pin<$PAX, $ALTFUNC>: TimPin,
|
||||
$PAX: PinId,
|
||||
{
|
||||
}
|
||||
|
||||
impl Sealed for (Pin<$PAX, $ALTFUNC>, $TIMX) {}
|
||||
};
|
||||
}
|
||||
|
||||
pin_and_tim!(PA31, AltFunc2, 23, pac::Tim23);
|
||||
pin_and_tim!(PA30, AltFunc2, 22, pac::Tim22);
|
||||
pin_and_tim!(PA29, AltFunc2, 21, pac::Tim21);
|
||||
pin_and_tim!(PA28, AltFunc2, 20, pac::Tim20);
|
||||
pin_and_tim!(PA27, AltFunc2, 19, pac::Tim19);
|
||||
pin_and_tim!(PA26, AltFunc2, 18, pac::Tim18);
|
||||
pin_and_tim!(PA25, AltFunc2, 17, pac::Tim17);
|
||||
pin_and_tim!(PA24, AltFunc2, 16, pac::Tim16);
|
||||
|
||||
pin_and_tim!(PA15, AltFunc1, 15, pac::Tim15);
|
||||
pin_and_tim!(PA14, AltFunc1, 14, pac::Tim14);
|
||||
pin_and_tim!(PA13, AltFunc1, 13, pac::Tim13);
|
||||
pin_and_tim!(PA12, AltFunc1, 12, pac::Tim12);
|
||||
pin_and_tim!(PA11, AltFunc1, 11, pac::Tim11);
|
||||
pin_and_tim!(PA10, AltFunc1, 10, pac::Tim10);
|
||||
pin_and_tim!(PA9, AltFunc1, 9, pac::Tim9);
|
||||
pin_and_tim!(PA8, AltFunc1, 8, pac::Tim8);
|
||||
pin_and_tim!(PA7, AltFunc1, 7, pac::Tim7);
|
||||
pin_and_tim!(PA6, AltFunc1, 6, pac::Tim6);
|
||||
pin_and_tim!(PA5, AltFunc1, 5, pac::Tim5);
|
||||
pin_and_tim!(PA4, AltFunc1, 4, pac::Tim4);
|
||||
pin_and_tim!(PA3, AltFunc1, 3, pac::Tim3);
|
||||
pin_and_tim!(PA2, AltFunc1, 2, pac::Tim2);
|
||||
pin_and_tim!(PA1, AltFunc1, 1, pac::Tim1);
|
||||
pin_and_tim!(PA0, AltFunc1, 0, pac::Tim0);
|
||||
|
||||
pin_and_tim!(PB23, AltFunc3, 23, pac::Tim23);
|
||||
pin_and_tim!(PB22, AltFunc3, 22, pac::Tim22);
|
||||
pin_and_tim!(PB21, AltFunc3, 21, pac::Tim21);
|
||||
pin_and_tim!(PB20, AltFunc3, 20, pac::Tim20);
|
||||
pin_and_tim!(PB19, AltFunc3, 19, pac::Tim19);
|
||||
pin_and_tim!(PB18, AltFunc3, 18, pac::Tim18);
|
||||
pin_and_tim!(PB17, AltFunc3, 17, pac::Tim17);
|
||||
pin_and_tim!(PB16, AltFunc3, 16, pac::Tim16);
|
||||
pin_and_tim!(PB15, AltFunc3, 15, pac::Tim15);
|
||||
pin_and_tim!(PB14, AltFunc3, 14, pac::Tim14);
|
||||
pin_and_tim!(PB13, AltFunc3, 13, pac::Tim13);
|
||||
pin_and_tim!(PB12, AltFunc3, 12, pac::Tim12);
|
||||
pin_and_tim!(PB11, AltFunc3, 11, pac::Tim11);
|
||||
pin_and_tim!(PB10, AltFunc3, 10, pac::Tim10);
|
||||
|
||||
pin_and_tim!(PB6, AltFunc3, 6, pac::Tim6);
|
||||
pin_and_tim!(PB5, AltFunc3, 5, pac::Tim5);
|
||||
pin_and_tim!(PB4, AltFunc3, 4, pac::Tim4);
|
||||
pin_and_tim!(PB3, AltFunc3, 3, pac::Tim3);
|
||||
pin_and_tim!(PB2, AltFunc3, 2, pac::Tim2);
|
||||
pin_and_tim!(PB1, AltFunc3, 1, pac::Tim1);
|
||||
pin_and_tim!(PB0, AltFunc3, 0, pac::Tim0);
|
||||
*/
|
||||
|
||||
macro_rules! pin_and_tim {
|
||||
($Px:ident, $FunSel:path, $ID:expr) => {
|
||||
impl TimPin for Pin<$Px>
|
||||
@ -292,19 +221,8 @@ macro_rules! pin_and_tim {
|
||||
const PORT: Port = $Px::PORT;
|
||||
const OFFSET: usize = $Px::OFFSET;
|
||||
const FUN_SEL: FunSel = $FunSel;
|
||||
const TIM: Tim = Tim($ID);
|
||||
const TIM_ID: TimId = TimId($ID);
|
||||
}
|
||||
|
||||
/*
|
||||
impl<Pin: TimPin, Tim: ValidTim> ValidTimAndPin<Pin, Tim> for (Pin<$Px>, $TIMX)
|
||||
where
|
||||
Pin<$PAX, $ALTFUNC>: TimPin,
|
||||
$PAX: PinId,
|
||||
{
|
||||
}
|
||||
|
||||
impl Sealed for (Pin<$PAX, $ALTFUNC>, $TIMX) {}
|
||||
*/
|
||||
};
|
||||
}
|
||||
|
||||
@ -375,7 +293,7 @@ pub type TimRegBlock = tim0::RegisterBlock;
|
||||
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||
/// pin ID is a singleton.
|
||||
pub unsafe trait TimRegInterface {
|
||||
fn tim_id(&self) -> u8;
|
||||
fn raw_id(&self) -> u8;
|
||||
|
||||
const PORT_BASE: *const tim0::RegisterBlock = pac::Tim0::ptr() as *const _;
|
||||
|
||||
@ -383,12 +301,12 @@ pub unsafe trait TimRegInterface {
|
||||
/// memory mapped peripheral depending on the TIM ID.
|
||||
#[inline(always)]
|
||||
fn reg_block(&self) -> &TimRegBlock {
|
||||
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
||||
unsafe { &*Self::PORT_BASE.offset(self.raw_id() as isize) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn mask_32(&self) -> u32 {
|
||||
1 << self.tim_id()
|
||||
1 << self.raw_id()
|
||||
}
|
||||
|
||||
/// Clear the reset bit of the TIM, holding it in reset
|
||||
@ -419,62 +337,44 @@ pub unsafe trait TimRegInterface {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tim(u8);
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct TimId(u8);
|
||||
|
||||
impl Tim {
|
||||
pub const fn raw_id(&self) -> u8 {
|
||||
unsafe impl TimRegInterface for TimId {
|
||||
fn raw_id(&self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl TimRegInterface for Tim {
|
||||
fn tim_id(&self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TimDynRegister {
|
||||
pub(crate) tim_id: u8,
|
||||
}
|
||||
|
||||
unsafe impl TimRegInterface for TimDynRegister {
|
||||
#[inline(always)]
|
||||
fn tim_id(&self) -> u8 {
|
||||
self.tim_id
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Timers
|
||||
//==================================================================================================
|
||||
|
||||
/// Hardware timers
|
||||
pub struct CountdownTimer {
|
||||
tim: Tim,
|
||||
tim: TimId,
|
||||
curr_freq: Hertz,
|
||||
sys_clk: Hertz,
|
||||
rst_val: u32,
|
||||
last_cnt: u32,
|
||||
listening: bool,
|
||||
}
|
||||
|
||||
unsafe impl TimRegInterface for CountdownTimer {
|
||||
fn tim_id(&self) -> u8 {
|
||||
fn raw_id(&self) -> u8 {
|
||||
self.tim.0
|
||||
}
|
||||
}
|
||||
|
||||
impl CountdownTimer {
|
||||
/// Configures a TIM peripheral as a periodic count down timer
|
||||
pub fn new(sys_clk: impl Into<Hertz>, tim_id: u8) -> Self {
|
||||
enable_tim_clk(tim_id);
|
||||
pub fn new<Tim: TimPeripheralMarker>(sys_clk: Hertz, _tim: Tim) -> Self {
|
||||
enable_tim_clk(Tim::ID.raw_id());
|
||||
let cd_timer = CountdownTimer {
|
||||
tim: Tim(tim_id),
|
||||
sys_clk: sys_clk.into(),
|
||||
tim: Tim::ID,
|
||||
sys_clk,
|
||||
rst_val: 0,
|
||||
curr_freq: 0.Hz(),
|
||||
listening: false,
|
||||
last_cnt: 0,
|
||||
};
|
||||
cd_timer
|
||||
@ -485,52 +385,33 @@ impl CountdownTimer {
|
||||
cd_timer
|
||||
}
|
||||
|
||||
/// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the
|
||||
/// IRQSEL peripheral for the provided interrupt and unmasks the interrupt
|
||||
pub fn listen(&mut self, event: Event, irq_cfg: InterruptConfig) {
|
||||
enable_peripheral_clock(PeripheralClocks::Irqsel);
|
||||
pub fn enable_interupt(&mut self, irq_cfg: InterruptConfig) {
|
||||
if irq_cfg.route {
|
||||
let irqsel = unsafe { pac::Irqsel::steal() };
|
||||
irqsel
|
||||
.tim0(self.tim_id() as usize)
|
||||
.tim0(self.raw_id() as usize)
|
||||
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||
}
|
||||
if irq_cfg.enable_in_nvic {
|
||||
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
match event {
|
||||
Event::TimeOut => {
|
||||
cortex_m::peripheral::NVIC::mask(irq_cfg.id);
|
||||
self.irq_cfg = Some(irq_cfg);
|
||||
self.listening = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlisten(&mut self, event: Event, unroute_irq: bool) {
|
||||
match event {
|
||||
Event::TimeOut => {
|
||||
enable_peripheral_clock(PeripheralClocks::Irqsel);
|
||||
if unroute_irq {
|
||||
let irqsel = unsafe { pac::Irqsel::steal() };
|
||||
irqsel
|
||||
.tim0(self.tim_id() as usize)
|
||||
.write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
||||
}
|
||||
self.disable_interrupt();
|
||||
self.listening = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable_interrupt(&mut self) {
|
||||
self.tim
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.irq_enb().set_bit());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable(&mut self) {
|
||||
self.tim
|
||||
.reg_block()
|
||||
.enable()
|
||||
.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
/// This function only clears the interrupt enable bit.
|
||||
///
|
||||
/// It does not mask the interrupt in the NVIC or un-route the IRQ.
|
||||
#[inline(always)]
|
||||
pub fn disable_interrupt(&mut self) {
|
||||
self.tim
|
||||
@ -539,15 +420,16 @@ impl CountdownTimer {
|
||||
.modify(|_, w| w.irq_enb().clear_bit());
|
||||
}
|
||||
|
||||
pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
|
||||
/// Disables the TIM and the dedicated TIM clock.
|
||||
pub fn stop(self) {
|
||||
self.tim
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.write(|w| w.enable().clear_bit());
|
||||
let syscfg = unsafe { va108xx::Sysconfig::steal() };
|
||||
syscfg
|
||||
.tim_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.tim_id())) });
|
||||
self.tim
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.raw_id())) });
|
||||
}
|
||||
|
||||
/// Load the count down timer with a timeout but do not start it.
|
||||
@ -583,20 +465,6 @@ impl CountdownTimer {
|
||||
self.tim.reg_block().cnt_value().read().bits()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable(&mut self) {
|
||||
if let Some(irq_cfg) = self.irq_cfg {
|
||||
self.enable_interrupt();
|
||||
if irq_cfg.enable_in_nvic {
|
||||
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
}
|
||||
self.tim
|
||||
.reg_block()
|
||||
.enable()
|
||||
.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn disable(&mut self) {
|
||||
self.tim
|
||||
@ -686,10 +554,6 @@ impl CountdownTimer {
|
||||
pub fn curr_freq(&self) -> Hertz {
|
||||
self.curr_freq
|
||||
}
|
||||
|
||||
pub fn listening(&self) -> bool {
|
||||
self.listening
|
||||
}
|
||||
}
|
||||
|
||||
/// CountDown implementation for TIMx
|
||||
@ -783,10 +647,7 @@ impl embedded_hal::delay::DelayNs for CountdownTimer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_up_ms_delay_provider(
|
||||
sys_clk: impl Into<Hertz>,
|
||||
tim: impl TimPeripheralMarker,
|
||||
) -> CountdownTimer {
|
||||
pub fn set_up_ms_delay_provider(sys_clk: Hertz, tim: impl TimPeripheralMarker) -> CountdownTimer {
|
||||
let mut provider = CountdownTimer::new(sys_clk, tim);
|
||||
provider.start(1000.Hz());
|
||||
provider
|
||||
|
@ -30,7 +30,7 @@ use crate::{
|
||||
};
|
||||
use embedded_hal_nb::serial::Read;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum UartId {
|
||||
A = 0,
|
||||
@ -38,7 +38,12 @@ pub enum UartId {
|
||||
}
|
||||
|
||||
impl UartId {
|
||||
// TODO: Safety docs.
|
||||
/// Unsafely steal a peripheral MMIO block for the given UART.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents ownership and safety guarantees by the HAL which can lead to data races
|
||||
/// on cuncurrent usage.
|
||||
pub const unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock {
|
||||
match self {
|
||||
UartId::A => unsafe { &*pac::Uarta::PTR },
|
||||
@ -494,6 +499,11 @@ impl UartPeripheralMarker for pac::Uartb {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[error("UART ID missmatch between peripheral and pins.")]
|
||||
pub struct UartIdMissmatchError;
|
||||
|
||||
//==================================================================================================
|
||||
// UART implementation
|
||||
//==================================================================================================
|
||||
@ -512,7 +522,7 @@ impl Uart {
|
||||
pins: (Tx, Rx),
|
||||
config: Config,
|
||||
irq_cfg: InterruptConfig,
|
||||
) -> Self {
|
||||
) -> Result<Uart, UartIdMissmatchError> {
|
||||
Self::new(sys_clk, uart, pins, config, Some(irq_cfg))
|
||||
}
|
||||
|
||||
@ -522,7 +532,7 @@ impl Uart {
|
||||
uart: UartI,
|
||||
pins: (Tx, Rx),
|
||||
config: Config,
|
||||
) -> Self {
|
||||
) -> Result<Self, UartIdMissmatchError> {
|
||||
Self::new(sys_clk, uart, pins, config, None)
|
||||
}
|
||||
|
||||
@ -540,11 +550,14 @@ impl Uart {
|
||||
/// any interrupt support is used, this can be [None].
|
||||
pub fn new<UartI: UartPeripheralMarker, TxPinI: TxPin, RxPinI: RxPin>(
|
||||
sys_clk: Hertz,
|
||||
uart: UartI,
|
||||
pins: (TxPinI, RxPinI),
|
||||
_uart: UartI,
|
||||
_pins: (TxPinI, RxPinI),
|
||||
config: Config,
|
||||
opt_irq_cfg: Option<InterruptConfig>,
|
||||
) -> Self {
|
||||
) -> Result<Self, UartIdMissmatchError> {
|
||||
if UartI::ID != TxPinI::UART_ID || UartI::ID != RxPinI::UART_ID {
|
||||
return Err(UartIdMissmatchError);
|
||||
}
|
||||
crate::clock::enable_peripheral_clock(UartI::PERIPH_SEL);
|
||||
|
||||
let reg_block = unsafe { UartI::reg_block() };
|
||||
@ -608,11 +621,10 @@ impl Uart {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Validity checks.
|
||||
Uart {
|
||||
Ok(Uart {
|
||||
tx: Tx::new(UartI::ID),
|
||||
rx: Rx::new(UartI::ID),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -973,7 +985,7 @@ impl Tx {
|
||||
/// You must ensure that only registers related to the operation of the TX side are used.
|
||||
#[inline(always)]
|
||||
pub unsafe fn regs(&self) -> &'static uart_base::RegisterBlock {
|
||||
&self.0.reg_block()
|
||||
self.0.reg_block()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -26,7 +26,7 @@ use embedded_io::ErrorType;
|
||||
use portable_atomic::AtomicBool;
|
||||
use va108xx::uarta as uart_base;
|
||||
|
||||
use super::{Rx, UartErrors, UartId, UartPeripheralMarker};
|
||||
use super::{Rx, UartErrors, UartId};
|
||||
|
||||
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||
|
@ -4,18 +4,27 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7" }
|
||||
cfg-if = "1"
|
||||
derive-mmio = { path = "../../../ROMEO/derive-mmio" }
|
||||
bitbybit = "1.3"
|
||||
arbitrary-int = "1.3"
|
||||
static_assertions = "1.1"
|
||||
embedded-hal = { version = "1.0" }
|
||||
embedded-hal-async = "1"
|
||||
thiserror = { version = "2", default-features = false }
|
||||
paste = "1"
|
||||
embassy-sync = "0.6"
|
||||
defmt = { version = "1", optional = true }
|
||||
va108xx = { version = "0.5", default-features = false, optional = true }
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
||||
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
|
||||
portable-atomic = "1"
|
||||
|
||||
[features]
|
||||
vor1x = ["_family-selected"]
|
||||
vor1x = ["_family-selected", "va108xx"]
|
||||
vor4x = ["_family-selected"]
|
||||
|
||||
_family-selected = []
|
||||
|
332
vorago-shared-periphs/src/gpio/asynch.rs
Normal file
332
vorago-shared-periphs/src/gpio/asynch.rs
Normal file
@ -0,0 +1,332 @@
|
||||
//! # Async GPIO functionality for the Vorago GPIO peripherals.
|
||||
//!
|
||||
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
||||
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
|
||||
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
|
||||
//! 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;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use portable_atomic::AtomicBool;
|
||||
|
||||
use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
|
||||
|
||||
pub use super::ll::InterruptEdge;
|
||||
use super::{Input, Pin, PinIdProvider, Port, ll::PinId};
|
||||
|
||||
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PORT_A] = [const { AtomicWaker::new() }; NUM_PORT_A];
|
||||
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PORT_B] = [const { AtomicWaker::new() }; NUM_PORT_B];
|
||||
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PORT_A] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_A];
|
||||
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_B] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_B];
|
||||
|
||||
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
|
||||
///
|
||||
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
|
||||
/// matching the [Port] argument.
|
||||
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
|
||||
/// as well as update the static edge detection structures. This allows the pin future tocomplete
|
||||
/// complete async operations.
|
||||
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
|
||||
let gpio = unsafe { port.steal_gpio() };
|
||||
|
||||
let irq_enb = gpio.read_irq_enb();
|
||||
let edge_status = gpio.read_edge_status();
|
||||
let (wakers, edge_detection) = match port {
|
||||
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
|
||||
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
|
||||
};
|
||||
|
||||
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn on_interrupt_for_port(
|
||||
mut irq_enb: u32,
|
||||
edge_status: u32,
|
||||
wakers: &'static [AtomicWaker],
|
||||
edge_detection: &'static [AtomicBool],
|
||||
) {
|
||||
while irq_enb != 0 {
|
||||
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||
let bit_mask = 1 << bit_pos;
|
||||
|
||||
wakers[bit_pos].wake();
|
||||
|
||||
if edge_status & bit_mask != 0 {
|
||||
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Clear the processed bit
|
||||
irq_enb &= !bit_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Input pin future which implements the [Future] trait.
|
||||
///
|
||||
/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this
|
||||
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
|
||||
/// struture is granted to allow writing custom async structures.
|
||||
pub struct InputPinFuture {
|
||||
id: PinId,
|
||||
waker_group: &'static [AtomicWaker],
|
||||
edge_detection_group: &'static [AtomicBool],
|
||||
}
|
||||
|
||||
impl InputPinFuture {
|
||||
#[inline]
|
||||
pub fn pin_group_to_waker_and_edge_detection_group(
|
||||
group: Port,
|
||||
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
||||
match group {
|
||||
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
|
||||
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_input_pin(
|
||||
pin: &mut Input,
|
||||
irq: va108xx::Interrupt,
|
||||
edge: InterruptEdge,
|
||||
) -> Self {
|
||||
let (waker_group, edge_detection_group) =
|
||||
Self::pin_group_to_waker_and_edge_detection_group(pin.port());
|
||||
edge_detection_group[pin.offset() as usize]
|
||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.configure_edge_interrupt(edge).unwrap();
|
||||
#[cfg(feature = "vor1x")]
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
Self {
|
||||
port: pin.port(),
|
||||
offset: pin.offset(),
|
||||
waker_group,
|
||||
edge_detection_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InputPinFuture {
|
||||
fn drop(&mut self) {
|
||||
// The API ensures that we actually own the pin, so stealing it here is okay.
|
||||
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for InputPinFuture {
|
||||
type Output = ();
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
let idx = self.pin_id.num() as usize;
|
||||
self.waker_group[idx].register(cx.waker());
|
||||
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||
return core::task::Poll::Ready(());
|
||||
}
|
||||
core::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputPinAsync {
|
||||
pin: Input,
|
||||
#[cfg(feature = "vor1x")]
|
||||
irq: va108xx::Interrupt,
|
||||
}
|
||||
|
||||
impl InputPinAsync {
|
||||
/// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be
|
||||
/// passed as well and is used to route and enable the interrupt.
|
||||
///
|
||||
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn new(pin: Input, irq: va108xx::Interrupt) -> Self {
|
||||
Self { pin, irq }
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is high.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||
.unwrap();
|
||||
if self.pin.is_high().unwrap() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is low.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||
.unwrap();
|
||||
if self.pin.is_low().unwrap() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees a falling edge.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees a rising edge.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> Input {
|
||||
self.pin
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for InputPinAsync {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl Wait for InputPinAsync {
|
||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_high().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_low().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_rising_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_falling_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_any_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputPinAsync<I: PinIdProvider, C: InputConfig> {
|
||||
pin: Pin<I, pin::Input<C>>,
|
||||
irq: pac::Interrupt,
|
||||
}
|
||||
|
||||
impl<I: PinIdProvider, C: InputConfig> InputPinAsync<I, C> {
|
||||
/// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be
|
||||
/// passed as well and is used to route and enable the interrupt.
|
||||
///
|
||||
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
||||
Self { pin, irq }
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is high.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
||||
if self.pin.is_high() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin is low.
|
||||
///
|
||||
/// This returns immediately if the pin is already high.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
||||
if self.pin.is_low() {
|
||||
return;
|
||||
}
|
||||
fut.await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees falling edge.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees rising edge.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
||||
self.pin
|
||||
}
|
||||
}
|
||||
impl<I: PinIdProvider, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl<I: PinIdProvider, C: InputConfig> Wait for InputPinAsync<I, C> {
|
||||
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_high().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_low().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_rising_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_falling_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||
self.wait_for_any_edge().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -5,37 +5,63 @@ pub use crate::Port;
|
||||
pub use crate::ioconfig::regs::Pull;
|
||||
use crate::ioconfig::regs::{FunSel, IoConfig, MmioIoConfig};
|
||||
|
||||
pub struct LowLevelGpio {
|
||||
gpio: super::regs::MmioGpio<'static>,
|
||||
ioconfig: MmioIoConfig<'static>,
|
||||
#[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 struct PinId {
|
||||
port: Port,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl LowLevelGpio {
|
||||
pub fn new(port: Port, offset: usize) -> Result<Self, InvalidOffsetError> {
|
||||
impl PinId {
|
||||
pub const fn new(port: Port, offset: usize) -> Result<Self, InvalidOffsetError> {
|
||||
if offset >= port.max_offset() {
|
||||
return Err(InvalidOffsetError {
|
||||
offset,
|
||||
port: Port::A,
|
||||
});
|
||||
return Err(InvalidOffsetError { offset, port });
|
||||
}
|
||||
Ok(LowLevelGpio {
|
||||
gpio: super::regs::Gpio::new_mmio(port),
|
||||
ioconfig: IoConfig::new_mmio(),
|
||||
port,
|
||||
offset,
|
||||
})
|
||||
Ok(PinId { port, offset })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn port(&self) -> Port {
|
||||
pub const fn port(&self) -> Port {
|
||||
self.port
|
||||
}
|
||||
|
||||
pub const fn offset(&self) -> usize {
|
||||
self.port
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LowLevelGpio {
|
||||
gpio: super::regs::MmioGpio<'static>,
|
||||
ioconfig: MmioIoConfig<'static>,
|
||||
id: PinId,
|
||||
}
|
||||
|
||||
impl LowLevelGpio {
|
||||
pub fn new(id: PinId) -> Self {
|
||||
LowLevelGpio {
|
||||
gpio: super::regs::Gpio::new_mmio(id.port),
|
||||
ioconfig: IoConfig::new_mmio(),
|
||||
id,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn offset(&self) -> usize {
|
||||
self.offset
|
||||
pub fn id(&self) -> PinId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn configure_as_input_floating(&mut self) {
|
||||
@ -54,7 +80,7 @@ impl LowLevelGpio {
|
||||
});
|
||||
}
|
||||
self.gpio.modify_dir(|mut dir| {
|
||||
dir &= !(1 << self.offset);
|
||||
dir &= !self.mask_32();
|
||||
dir
|
||||
});
|
||||
}
|
||||
@ -76,7 +102,7 @@ impl LowLevelGpio {
|
||||
});
|
||||
}
|
||||
self.gpio.modify_dir(|mut dir| {
|
||||
dir &= !(1 << self.offset);
|
||||
dir &= !self.mask_32();
|
||||
dir
|
||||
});
|
||||
}
|
||||
@ -101,7 +127,7 @@ impl LowLevelGpio {
|
||||
PinState::High => self.gpio.write_set_out(1 << self.offset),
|
||||
}
|
||||
self.gpio.modify_dir(|mut dir| {
|
||||
dir |= 1 << self.offset;
|
||||
dir |= self.mask_32();
|
||||
dir
|
||||
});
|
||||
}
|
||||
@ -160,12 +186,12 @@ impl LowLevelGpio {
|
||||
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
self.gpio.write_set_out(1 << self.offset);
|
||||
self.gpio.write_set_out(self.mask_32());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low(&mut self) {
|
||||
self.gpio.write_clr_out(1 << self.offset);
|
||||
self.gpio.write_clr_out(self.mask_32());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -180,6 +206,57 @@ impl LowLevelGpio {
|
||||
|
||||
#[inline]
|
||||
pub fn toggle(&mut self) {
|
||||
self.gpio.write_tog_out(1 << self.offset);
|
||||
self.gpio.write_tog_out(self.mask_32());
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
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 { crate::enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
self.gpio.modify_irq_enb(|mut value| {
|
||||
value |= self.mask_32();
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
/// 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]
|
||||
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
||||
unsafe {
|
||||
self.gpio.modify_irq_sen(|mut value| {
|
||||
value &= !self.mask_32();
|
||||
value
|
||||
});
|
||||
match edge_type {
|
||||
InterruptEdge::HighToLow => {
|
||||
self.gpio.modify_irq_evt(|mut value| {
|
||||
value &= !self.mask_32();
|
||||
value
|
||||
});
|
||||
}
|
||||
InterruptEdge::LowToHigh => {
|
||||
self.gpio.modify_irq_evt(|mut value| {
|
||||
value |= self.mask_32();
|
||||
value
|
||||
});
|
||||
}
|
||||
InterruptEdge::BothEdges => {
|
||||
self.gpio.modify_irq_edge(|mut value| {
|
||||
value |= self.mask_32();
|
||||
value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn mask_32(&self) -> u32 {
|
||||
1 << self.offset
|
||||
}
|
||||
}
|
||||
|
@ -5,19 +5,19 @@ pub use ll::{Port, Pull};
|
||||
|
||||
use crate::ioconfig::regs::FunSel;
|
||||
|
||||
pub mod asynch;
|
||||
pub mod ll;
|
||||
pub mod regs;
|
||||
|
||||
pub trait PinId {
|
||||
const PORT: Port;
|
||||
const OFFSET: usize;
|
||||
pub trait PinIdProvider {
|
||||
const ID: ll::PinId;
|
||||
}
|
||||
|
||||
pub struct Pin<I: PinId> {
|
||||
pub struct Pin<I: PinIdProvider> {
|
||||
phantom: core::marker::PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I: PinId> Pin<I> {
|
||||
impl<I: PinIdProvider> Pin<I> {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
@ -29,8 +29,8 @@ impl<I: PinId> Pin<I> {
|
||||
pub struct Output(ll::LowLevelGpio);
|
||||
|
||||
impl Output {
|
||||
pub fn new<I: PinId>(_pin: Pin<I>, init_level: PinState) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
|
||||
pub fn new<I: PinIdProvider>(_pin: Pin<I>, init_level: PinState) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::ID);
|
||||
ll.configure_as_output_push_pull(init_level);
|
||||
Output(ll)
|
||||
}
|
||||
@ -101,14 +101,14 @@ impl embedded_hal::digital::StatefulOutputPin for Output {
|
||||
pub struct Input(ll::LowLevelGpio);
|
||||
|
||||
impl Input {
|
||||
pub fn new_floating<I: PinId>(_pin: Pin<I>) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
|
||||
pub fn new_floating<I: PinIdProvider>(_pin: Pin<I>) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::ID);
|
||||
ll.configure_as_input_floating();
|
||||
Input(ll)
|
||||
}
|
||||
|
||||
pub fn new_with_pull<I: PinId>(_pin: Pin<I>, pull: Pull) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
|
||||
pub fn new_with_pull<I: PinIdProvider>(_pin: Pin<I>, pull: Pull) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::ID);
|
||||
ll.configure_as_input_with_pull(pull);
|
||||
Input(ll)
|
||||
}
|
||||
@ -122,6 +122,14 @@ impl Input {
|
||||
pub fn offset(&self) -> usize {
|
||||
self.0.offset()
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||
self.0.enable_interrupt(irq_cfg);
|
||||
}
|
||||
pub fn configure_edge_interrupt(&mut self, edge: ll::InterruptEdge) {
|
||||
self.0.configure_edge_interrupt(edge);
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for Input {
|
||||
@ -161,8 +169,8 @@ pub struct Flex {
|
||||
}
|
||||
|
||||
impl Flex {
|
||||
pub fn new<I: PinId>(_pin: Pin<I>) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
|
||||
pub fn new<I: PinIdProvider>(_pin: Pin<I>) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::ID);
|
||||
ll.configure_as_input_floating();
|
||||
Flex {
|
||||
ll,
|
||||
@ -270,8 +278,8 @@ pub struct IoPeriphPin {
|
||||
}
|
||||
|
||||
impl IoPeriphPin {
|
||||
pub fn new<I: PinId>(_pin: Pin<I>, fun_sel: FunSel, pull: Option<Pull>) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
|
||||
pub fn new<I: PinIdProvider>(_pin: Pin<I>, fun_sel: FunSel, pull: Option<Pull>) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::ID);
|
||||
ll.configure_as_peripheral_pin(fun_sel, pull);
|
||||
IoPeriphPin { ll, fun_sel }
|
||||
}
|
||||
|
@ -51,10 +51,13 @@ pub struct Gpio {
|
||||
irq_edge: u32,
|
||||
irq_evt: u32,
|
||||
irq_enb: u32,
|
||||
/// Raw interrupt status. This register is not latched and may not indicated edge sensitive
|
||||
/// events.
|
||||
#[mmio(PureRead)]
|
||||
irq_raw: u32,
|
||||
/// Read only register which shows the AND of IRQ_RAW and IRQ_ENB. Called IRQ_END by Vorago.
|
||||
#[mmio(PureRead)]
|
||||
irq_end: u32,
|
||||
irq_active: u32,
|
||||
#[mmio(PureRead)]
|
||||
edge_status: u32,
|
||||
|
||||
|
@ -53,6 +53,15 @@ impl Port {
|
||||
Port::G => NUM_PORT_G,
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsafely steal the GPIO peripheral block for the given port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents ownership and safety guarantees by the HAL.
|
||||
pub unsafe fn steal_gpio(&self) -> gpio::regs::MmioGpio<'static> {
|
||||
gpio::regs::Gpio::new_mmio(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@ -63,6 +72,54 @@ pub struct InvalidOffsetError {
|
||||
port: Port,
|
||||
}
|
||||
|
||||
/// Generic interrupt config which can be used to specify whether the HAL driver will
|
||||
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
||||
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
|
||||
/// perform those steps themselves.
|
||||
#[cfg(feature = "vor1x")]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct InterruptConfig {
|
||||
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
||||
pub id: va108xx::Interrupt,
|
||||
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral.
|
||||
pub route: bool,
|
||||
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for
|
||||
/// multiple purposes, the user can enable the interrupts themselves.
|
||||
pub enable_in_nvic: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
impl InterruptConfig {
|
||||
pub fn new(id: va108xx::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
|
||||
InterruptConfig {
|
||||
id,
|
||||
route,
|
||||
enable_in_nvic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable a specific interrupt using the NVIC peripheral.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||
#[cfg(feature = "vor1x")]
|
||||
#[inline]
|
||||
pub unsafe fn enable_nvic_interrupt(irq: va108xx::Interrupt) {
|
||||
unsafe {
|
||||
cortex_m::peripheral::NVIC::unmask(irq);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable a specific interrupt using the NVIC peripheral.
|
||||
#[cfg(feature = "vor1x")]
|
||||
#[inline]
|
||||
pub fn disable_nvic_interrupt(irq: va108xx::Interrupt) {
|
||||
cortex_m::peripheral::NVIC::mask(irq);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) mod sealed {
|
||||
pub trait Sealed {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user