Async GPIO implementation #53
@ -9,31 +9,24 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
|||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
|
embedded-hal-async = "1"
|
||||||
|
embedded-io-async = "0.6"
|
||||||
|
|
||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
panic-rtt-target = { version = "0.1" }
|
panic-rtt-target = { version = "0.1" }
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
|
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
||||||
|
ringbuf = { version = "0.4", default-features = false }
|
||||||
|
|
||||||
embassy-sync = { version = "0.6" }
|
embassy-sync = "0.6"
|
||||||
embassy-time = { version = "0.4" }
|
embassy-time = "0.4"
|
||||||
va416xx-embassy = { path = "../../va416xx-embassy", default-features = false }
|
embassy-executor = { version = "0.7", features = [
|
||||||
|
|
||||||
[dependencies.ringbuf]
|
|
||||||
version = "0.4"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.once_cell]
|
|
||||||
version = "1"
|
|
||||||
default-features = false
|
|
||||||
features = ["critical-section"]
|
|
||||||
|
|
||||||
[dependencies.embassy-executor]
|
|
||||||
version = "0.7"
|
|
||||||
features = [
|
|
||||||
"arch-cortex-m",
|
"arch-cortex-m",
|
||||||
"executor-thread",
|
"executor-thread",
|
||||||
"executor-interrupt",
|
"executor-interrupt"
|
||||||
]
|
]}
|
||||||
|
|
||||||
|
va416xx-embassy = { path = "../../va416xx-embassy", default-features = false }
|
||||||
|
|
||||||
[dependencies.va416xx-hal]
|
[dependencies.va416xx-hal]
|
||||||
path = "../../va416xx-hal"
|
path = "../../va416xx-hal"
|
||||||
|
371
examples/embassy/src/bin/async-gpio.rs
Normal file
371
examples/embassy/src/bin/async-gpio.rs
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
//! This example demonstrates the usage of async GPIO operations on VA416xx.
|
||||||
|
//!
|
||||||
|
//! You need to tie the PA0 to the PA1 pin for this example to work. You can optionally also tie
|
||||||
|
//! more pin combinations together and test other ports by setting the appropriate
|
||||||
|
//! [CHECK_XXX_TO_XXX] constants to true.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use embassy_example::EXTCLK_FREQ;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_sync::channel::{Receiver, Sender};
|
||||||
|
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use embedded_hal_async::digital::Wait;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va416xx_hal::clock::ClkgenExt;
|
||||||
|
use va416xx_hal::gpio::{
|
||||||
|
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, PinsC, PinsD,
|
||||||
|
PinsE, PinsF, PinsG, Port,
|
||||||
|
};
|
||||||
|
use va416xx_hal::time::Hertz;
|
||||||
|
use va416xx_hal::{
|
||||||
|
gpio::{DynPin, PinsA},
|
||||||
|
pac::{self, interrupt},
|
||||||
|
};
|
||||||
|
|
||||||
|
const CHECK_PA0_TO_PA1: bool = true;
|
||||||
|
const CHECK_PB0_TO_PB1: bool = true;
|
||||||
|
const CHECK_PC14_TO_PC15: bool = true;
|
||||||
|
const CHECK_PD2_TO_PD3: bool = true;
|
||||||
|
const CHECK_PE0_TO_PE1: bool = true;
|
||||||
|
const CHECK_PF0_TO_PF1: bool = true;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct GpioCmd {
|
||||||
|
cmd_type: GpioCmdType,
|
||||||
|
after_delay: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpioCmd {
|
||||||
|
pub fn new(cmd_type: GpioCmdType, after_delay: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
cmd_type,
|
||||||
|
after_delay,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum GpioCmdType {
|
||||||
|
SetHigh,
|
||||||
|
SetLow,
|
||||||
|
RisingEdge,
|
||||||
|
FallingEdge,
|
||||||
|
CloseTask,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a bounded channel of 3 u32s.
|
||||||
|
static CHANNEL_PA0_TO_PA1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
static CHANNEL_PB0_TO_PB1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
static CHANNEL_PC14_TO_PC15: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
static CHANNEL_PD2_TO_PD3: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
static CHANNEL_PE0_TO_PE1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
static CHANNEL_PF0_TO_PF1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA416xx Async GPIO Demo --");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||||
|
// Use the external clock connected to XTAL_N.
|
||||||
|
let clocks = dp
|
||||||
|
.clkgen
|
||||||
|
.constrain()
|
||||||
|
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
|
||||||
|
.freeze(&mut dp.sysconfig)
|
||||||
|
.unwrap();
|
||||||
|
// Safety: Only called once here.
|
||||||
|
unsafe {
|
||||||
|
va416xx_embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irq_router,
|
||||||
|
dp.tim15,
|
||||||
|
dp.tim14,
|
||||||
|
&clocks,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let portb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
||||||
|
let portc = PinsC::new(&mut dp.sysconfig, dp.portc);
|
||||||
|
let portd = PinsD::new(&mut dp.sysconfig, dp.portd);
|
||||||
|
let porte = PinsE::new(&mut dp.sysconfig, dp.porte);
|
||||||
|
let portf = PinsF::new(&mut dp.sysconfig, dp.portf);
|
||||||
|
|
||||||
|
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
||||||
|
let mut led = portg.pg5.into_readable_push_pull_output();
|
||||||
|
|
||||||
|
if CHECK_PA0_TO_PA1 {
|
||||||
|
let out_pin = porta.pa0.into_readable_push_pull_output();
|
||||||
|
let in_pin = porta.pa1.into_floating_input();
|
||||||
|
let out_pin = out_pin.downgrade();
|
||||||
|
let in_pin = InputPinAsync::new(in_pin).unwrap();
|
||||||
|
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PA0 to PA1",
|
||||||
|
out_pin,
|
||||||
|
CHANNEL_PA0_TO_PA1.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_TO_PA1.sender(), in_pin).await;
|
||||||
|
rprintln!("Example PA0 to PA1 done");
|
||||||
|
}
|
||||||
|
|
||||||
|
if CHECK_PB0_TO_PB1 {
|
||||||
|
let out_pin = portb.pb0.into_readable_push_pull_output();
|
||||||
|
let in_pin = portb.pb1.into_floating_input();
|
||||||
|
let out_pin = out_pin.downgrade();
|
||||||
|
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
||||||
|
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PB0 to PB1",
|
||||||
|
out_pin,
|
||||||
|
CHANNEL_PB0_TO_PB1.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
check_pin_to_pin_async_ops("PB0 to PB1", CHANNEL_PB0_TO_PB1.sender(), in_pin).await;
|
||||||
|
rprintln!("Example PB0 to PB1 done");
|
||||||
|
}
|
||||||
|
|
||||||
|
if CHECK_PC14_TO_PC15 {
|
||||||
|
let out_pin = portc.pc14.into_readable_push_pull_output();
|
||||||
|
let in_pin = portc.pc15.into_floating_input();
|
||||||
|
let out_pin = out_pin.downgrade();
|
||||||
|
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PC14 to PC15",
|
||||||
|
out_pin,
|
||||||
|
CHANNEL_PC14_TO_PC15.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
check_pin_to_pin_async_ops("PC14 to PC15", CHANNEL_PC14_TO_PC15.sender(), in_pin).await;
|
||||||
|
rprintln!("Example PC14 to PC15 done");
|
||||||
|
}
|
||||||
|
|
||||||
|
if CHECK_PD2_TO_PD3 {
|
||||||
|
let out_pin = portd.pd2.into_readable_push_pull_output();
|
||||||
|
let in_pin = portd.pd3.into_floating_input();
|
||||||
|
let out_pin = out_pin.downgrade();
|
||||||
|
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PD2 to PD3",
|
||||||
|
out_pin,
|
||||||
|
CHANNEL_PD2_TO_PD3.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
check_pin_to_pin_async_ops("PD2 to PD3", CHANNEL_PD2_TO_PD3.sender(), in_pin).await;
|
||||||
|
rprintln!("Example PD2 to PD3 done");
|
||||||
|
}
|
||||||
|
|
||||||
|
if CHECK_PE0_TO_PE1 {
|
||||||
|
let out_pin = porte.pe0.into_readable_push_pull_output();
|
||||||
|
let in_pin = porte.pe1.into_floating_input();
|
||||||
|
let out_pin = out_pin.downgrade();
|
||||||
|
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PE0 to PE1",
|
||||||
|
out_pin,
|
||||||
|
CHANNEL_PE0_TO_PE1.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
check_pin_to_pin_async_ops("PE0 to PE1", CHANNEL_PE0_TO_PE1.sender(), in_pin).await;
|
||||||
|
rprintln!("Example PE0 to PE1 done");
|
||||||
|
}
|
||||||
|
|
||||||
|
if CHECK_PF0_TO_PF1 {
|
||||||
|
let out_pin = portf.pf0.into_readable_push_pull_output();
|
||||||
|
let in_pin = portf.pf1.into_floating_input();
|
||||||
|
let out_pin = out_pin.downgrade();
|
||||||
|
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PF0 to PF1",
|
||||||
|
out_pin,
|
||||||
|
CHANNEL_PF0_TO_PF1.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
check_pin_to_pin_async_ops("PF0 to PF1", CHANNEL_PF0_TO_PF1.sender(), in_pin).await;
|
||||||
|
rprintln!("Example PF0 to PF1 done");
|
||||||
|
}
|
||||||
|
|
||||||
|
rprintln!("Example done, toggling LED0");
|
||||||
|
loop {
|
||||||
|
led.toggle();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_pin_to_pin_async_ops(
|
||||||
|
ctx: &'static str,
|
||||||
|
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
||||||
|
mut async_input: impl Wait,
|
||||||
|
) {
|
||||||
|
rprintln!(
|
||||||
|
"{}: sending SetHigh command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
|
||||||
|
async_input.wait_for_high().await.unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"{}: Input pin is high now ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"{}: sending SetLow command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
|
||||||
|
async_input.wait_for_low().await.unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"{}: Input pin is low now ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"{}: sending RisingEdge command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
||||||
|
async_input.wait_for_rising_edge().await.unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"{}: input pin had rising edge ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"{}: sending Falling command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender
|
||||||
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
||||||
|
.await;
|
||||||
|
async_input.wait_for_falling_edge().await.unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"{}: input pin had a falling edge ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"{}: sending Falling command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender
|
||||||
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
||||||
|
.await;
|
||||||
|
async_input.wait_for_any_edge().await.unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"{}: input pin had a falling (any) edge ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"{}: sending Falling command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
||||||
|
async_input.wait_for_any_edge().await.unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"{}: input pin had a rising (any) edge ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::CloseTask, 0)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task(pool_size = 8)]
|
||||||
|
async fn output_task(
|
||||||
|
ctx: &'static str,
|
||||||
|
mut out: DynPin,
|
||||||
|
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
||||||
|
) {
|
||||||
|
loop {
|
||||||
|
let next_cmd = receiver.receive().await;
|
||||||
|
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
|
||||||
|
match next_cmd.cmd_type {
|
||||||
|
GpioCmdType::SetHigh => {
|
||||||
|
rprintln!("{}: Set output high", ctx);
|
||||||
|
out.set_high().unwrap();
|
||||||
|
}
|
||||||
|
GpioCmdType::SetLow => {
|
||||||
|
rprintln!("{}: Set output low", ctx);
|
||||||
|
out.set_low().unwrap();
|
||||||
|
}
|
||||||
|
GpioCmdType::RisingEdge => {
|
||||||
|
rprintln!("{}: Rising edge", ctx);
|
||||||
|
if !out.is_low().unwrap() {
|
||||||
|
out.set_low().unwrap();
|
||||||
|
}
|
||||||
|
out.set_high().unwrap();
|
||||||
|
}
|
||||||
|
GpioCmdType::FallingEdge => {
|
||||||
|
rprintln!("{}: Falling edge", ctx);
|
||||||
|
if !out.is_high().unwrap() {
|
||||||
|
out.set_high().unwrap();
|
||||||
|
}
|
||||||
|
out.set_low().unwrap();
|
||||||
|
}
|
||||||
|
GpioCmdType::CloseTask => {
|
||||||
|
rprintln!("{}: Closing task", ctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn PORTA1() {
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::A).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn PORTB1() {
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::B).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn PORTC15() {
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::C).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn PORTD3() {
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::D).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn PORTE1() {
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::E).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn PORTF1() {
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::F).unwrap();
|
||||||
|
}
|
@ -16,7 +16,6 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embassy_time::{Duration, Ticker};
|
use embassy_time::{Duration, Ticker};
|
||||||
use embedded_hal::digital::StatefulOutputPin;
|
|
||||||
use embedded_io::Write;
|
use embedded_io::Write;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use ringbuf::{
|
use ringbuf::{
|
||||||
@ -119,7 +118,7 @@ async fn main(spawner: Spawner) {
|
|||||||
async fn blinky(mut led: Pin<PG5, OutputReadablePushPull>) {
|
async fn blinky(mut led: Pin<PG5, OutputReadablePushPull>) {
|
||||||
let mut ticker = Ticker::every(Duration::from_millis(500));
|
let mut ticker = Ticker::every(Duration::from_millis(500));
|
||||||
loop {
|
loop {
|
||||||
led.toggle().ok();
|
led.toggle();
|
||||||
ticker.next().await;
|
ticker.next().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
use embassy_example::EXTCLK_FREQ;
|
use embassy_example::EXTCLK_FREQ;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_time::{Duration, Instant, Ticker};
|
use embassy_time::{Duration, Instant, Ticker};
|
||||||
use embedded_hal::digital::StatefulOutputPin;
|
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va416xx_hal::{gpio::PinsG, pac, prelude::*, time::Hertz};
|
use va416xx_hal::{gpio::PinsG, pac, prelude::*, time::Hertz};
|
||||||
@ -59,6 +58,6 @@ async fn main(_spawner: Spawner) {
|
|||||||
loop {
|
loop {
|
||||||
ticker.next().await;
|
ticker.next().await;
|
||||||
rprintln!("Current time: {}", Instant::now().as_secs());
|
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||||
led.toggle().ok();
|
led.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ const EXTCLK_FREQ: Hertz = Hertz::from_raw(40_000_000);
|
|||||||
mod app {
|
mod app {
|
||||||
use super::*;
|
use super::*;
|
||||||
use cortex_m::asm;
|
use cortex_m::asm;
|
||||||
use embedded_hal::digital::StatefulOutputPin;
|
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
use rtic_monotonics::Monotonic;
|
use rtic_monotonics::Monotonic;
|
||||||
@ -64,7 +63,7 @@ mod app {
|
|||||||
)]
|
)]
|
||||||
async fn blinky(cx: blinky::Context) {
|
async fn blinky(cx: blinky::Context) {
|
||||||
loop {
|
loop {
|
||||||
cx.local.led.toggle().ok();
|
cx.local.led.toggle();
|
||||||
Mono::delay(200.millis()).await;
|
Mono::delay(200.millis()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::digital::StatefulOutputPin;
|
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va416xx_hal::{gpio::PinsG, pac};
|
use va416xx_hal::{gpio::PinsG, pac};
|
||||||
@ -18,6 +17,6 @@ fn main() -> ! {
|
|||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = portg.pg5.into_readable_push_pull_output();
|
||||||
loop {
|
loop {
|
||||||
cortex_m::asm::delay(2_000_000);
|
cortex_m::asm::delay(2_000_000);
|
||||||
led.toggle().ok();
|
led.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ use embassy_time_queue_utils::Queue;
|
|||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
clock::Clocks,
|
clock::Clocks,
|
||||||
enable_interrupt,
|
enable_nvic_interrupt,
|
||||||
irq_router::enable_and_init_irq_router,
|
irq_router::enable_and_init_irq_router,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
pwm::ValidTim,
|
pwm::ValidTim,
|
||||||
@ -207,7 +207,7 @@ impl TimerDriver {
|
|||||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
// Switch on. Timekeeping should always be done.
|
// Switch on. Timekeeping should always be done.
|
||||||
unsafe {
|
unsafe {
|
||||||
enable_interrupt(TimekeeperTim::IRQ);
|
enable_nvic_interrupt(TimekeeperTim::IRQ);
|
||||||
}
|
}
|
||||||
timekeeper_tim_regs
|
timekeeper_tim_regs
|
||||||
.ctrl()
|
.ctrl()
|
||||||
@ -224,7 +224,7 @@ impl TimerDriver {
|
|||||||
});
|
});
|
||||||
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
|
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
|
||||||
unsafe {
|
unsafe {
|
||||||
enable_interrupt(AlarmTim::IRQ);
|
enable_nvic_interrupt(AlarmTim::IRQ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,21 +16,26 @@ critical-section = "1"
|
|||||||
nb = "1"
|
nb = "1"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
|
embedded-hal-async = "1"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
typenum = "1"
|
typenum = "1"
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
bitfield = "0.17"
|
bitfield = "0.17"
|
||||||
defmt = { version = "0.3", optional = true }
|
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
delegate = "0.12"
|
delegate = "0.12"
|
||||||
void = { version = "1", default-features = false }
|
void = { version = "1", default-features = false }
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
portable-atomic = "1"
|
||||||
|
embassy-sync = "0.6"
|
||||||
|
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
[dependencies.va416xx]
|
[dependencies.va416xx]
|
||||||
default-features = false
|
default-features = false
|
||||||
version = "0.3"
|
path = "../va416xx"
|
||||||
|
version = "0.4"
|
||||||
features = ["critical-section"]
|
features = ["critical-section"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs)
|
//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs)
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{PeripheralClock, PeripheralSelect},
|
clock::{PeripheralClock, PeripheralSelect},
|
||||||
enable_interrupt, pac,
|
enable_nvic_interrupt, pac,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -266,7 +266,7 @@ impl DmaChannel {
|
|||||||
///
|
///
|
||||||
/// This function is `unsafe` because it can break mask-based critical sections.
|
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||||
pub unsafe fn enable_done_interrupt(&mut self) {
|
pub unsafe fn enable_done_interrupt(&mut self) {
|
||||||
enable_interrupt(self.done_interrupt);
|
enable_nvic_interrupt(self.done_interrupt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables the DMA_ACTIVE interrupt for the DMA channel.
|
/// Enables the DMA_ACTIVE interrupt for the DMA channel.
|
||||||
@ -275,7 +275,7 @@ impl DmaChannel {
|
|||||||
///
|
///
|
||||||
/// This function is `unsafe` because it can break mask-based critical sections.
|
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||||
pub unsafe fn enable_active_interrupt(&mut self) {
|
pub unsafe fn enable_active_interrupt(&mut self) {
|
||||||
enable_interrupt(self.active_interrupt);
|
enable_nvic_interrupt(self.active_interrupt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepares a 8-bit DMA transfer from memory to memory.
|
/// Prepares a 8-bit DMA transfer from memory to memory.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{enable_interrupt, pac};
|
use crate::{enable_nvic_interrupt, pac};
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable_rom_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
|
pub fn enable_rom_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
|
||||||
@ -26,7 +26,7 @@ pub fn enable_ram1_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable_sbe_irq() {
|
pub fn enable_sbe_irq() {
|
||||||
unsafe {
|
unsafe {
|
||||||
enable_interrupt(pac::Interrupt::EDAC_SBE);
|
enable_nvic_interrupt(pac::Interrupt::EDAC_SBE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ pub fn enable_sbe_irq() {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable_mbe_irq() {
|
pub fn enable_mbe_irq() {
|
||||||
unsafe {
|
unsafe {
|
||||||
enable_interrupt(pac::Interrupt::EDAC_MBE);
|
enable_nvic_interrupt(pac::Interrupt::EDAC_MBE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
448
va416xx-hal/src/gpio/asynch.rs
Normal file
448
va416xx-hal/src/gpio/asynch.rs
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
//! # Async GPIO functionality for the VA416xx 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 va416xx::{self as pac};
|
||||||
|
|
||||||
|
use crate::enable_nvic_interrupt;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
|
||||||
|
NUM_PINS_PORT_A_TO_F,
|
||||||
|
};
|
||||||
|
|
||||||
|
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_C: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_D: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_E: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_F: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
|
||||||
|
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_C: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_D: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_E: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_F: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("port G does not support async functionality")]
|
||||||
|
pub struct PortGDoesNotSupportAsyncError;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum AsyncDynPinError {
|
||||||
|
#[error("invalid pin type: {0}")]
|
||||||
|
InvalidPinType(#[from] InvalidPinTypeError),
|
||||||
|
#[error("port g does not support async functionality: {0}")]
|
||||||
|
PortGDoesNotSupportAsync(#[from] PortGDoesNotSupportAsyncError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
) -> Result<(), PortGDoesNotSupportAsyncError> {
|
||||||
|
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,
|
||||||
|
&EDGE_DETECTION_PORT_A,
|
||||||
|
),
|
||||||
|
Port::B => (
|
||||||
|
periphs.portb.irq_enb().read().bits(),
|
||||||
|
periphs.portb.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_B,
|
||||||
|
&EDGE_DETECTION_PORT_B,
|
||||||
|
),
|
||||||
|
Port::C => (
|
||||||
|
periphs.portc.irq_enb().read().bits(),
|
||||||
|
periphs.portc.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_C,
|
||||||
|
&EDGE_DETECTION_PORT_C,
|
||||||
|
),
|
||||||
|
Port::D => (
|
||||||
|
periphs.portd.irq_enb().read().bits(),
|
||||||
|
periphs.portd.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_D,
|
||||||
|
&EDGE_DETECTION_PORT_D,
|
||||||
|
),
|
||||||
|
Port::E => (
|
||||||
|
periphs.porte.irq_enb().read().bits(),
|
||||||
|
periphs.porte.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_E,
|
||||||
|
&EDGE_DETECTION_PORT_E,
|
||||||
|
),
|
||||||
|
Port::F => (
|
||||||
|
periphs.portf.irq_enb().read().bits(),
|
||||||
|
periphs.portf.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_F,
|
||||||
|
&EDGE_DETECTION_PORT_F,
|
||||||
|
),
|
||||||
|
Port::G => return Err(PortGDoesNotSupportAsyncError),
|
||||||
|
};
|
||||||
|
|
||||||
|
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
pub fn new_with_dyn_pin(
|
||||||
|
pin: &mut DynPin,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Result<Self, AsyncDynPinError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError(pin.mode()).into());
|
||||||
|
}
|
||||||
|
if pin.id().port() == Port::G {
|
||||||
|
return Err(PortGDoesNotSupportAsyncError.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
// Unwraps okay, checked for PORT G previously
|
||||||
|
pin.configure_edge_interrupt(edge).unwrap();
|
||||||
|
unsafe { enable_nvic_interrupt(pin.irq_id().unwrap()) };
|
||||||
|
pin.enable_interrupt();
|
||||||
|
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>>,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Result<Self, PortGDoesNotSupportAsyncError> {
|
||||||
|
if pin.id().port() == Port::G {
|
||||||
|
return Err(PortGDoesNotSupportAsyncError);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
// Unwraps okay, checked for PORT G previously
|
||||||
|
pin.configure_edge_interrupt(edge);
|
||||||
|
unsafe { enable_nvic_interrupt(I::IRQ.unwrap()) };
|
||||||
|
pin.enable_interrupt();
|
||||||
|
Ok(Self {
|
||||||
|
pin_id: pin.id(),
|
||||||
|
waker_group,
|
||||||
|
edge_detection_group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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()),
|
||||||
|
Port::C => (WAKERS_FOR_PORT_C.as_ref(), EDGE_DETECTION_PORT_C.as_ref()),
|
||||||
|
Port::D => (WAKERS_FOR_PORT_D.as_ref(), EDGE_DETECTION_PORT_D.as_ref()),
|
||||||
|
Port::E => (WAKERS_FOR_PORT_E.as_ref(), EDGE_DETECTION_PORT_E.as_ref()),
|
||||||
|
Port::F => (WAKERS_FOR_PORT_F.as_ref(), EDGE_DETECTION_PORT_F.as_ref()),
|
||||||
|
_ => panic!("unexpected pin group G"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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) -> Result<Self, AsyncDynPinError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError(pin.mode()).into());
|
||||||
|
}
|
||||||
|
if pin.id().port() == Port::G {
|
||||||
|
return Err(PortGDoesNotSupportAsyncError.into());
|
||||||
|
}
|
||||||
|
Ok(Self { pin })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, 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, 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, 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, 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, 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>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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>>) -> Result<Self, PortGDoesNotSupportAsyncError> {
|
||||||
|
if pin.id().port() == Port::G {
|
||||||
|
return Err(PortGDoesNotSupportAsyncError);
|
||||||
|
}
|
||||||
|
Ok(Self { pin })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
|
||||||
|
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, InterruptEdge::HighToLow).unwrap();
|
||||||
|
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, InterruptEdge::HighToLow)
|
||||||
|
.unwrap()
|
||||||
|
.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, 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_pin(&mut self.pin, InterruptEdge::BothEdges)
|
||||||
|
.unwrap()
|
||||||
|
.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(())
|
||||||
|
}
|
||||||
|
}
|
@ -55,11 +55,14 @@
|
|||||||
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
|
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
|
||||||
//! operation, the trait functions will return
|
//! operation, the trait functions will return
|
||||||
//! [InvalidPinTypeError].
|
//! [InvalidPinTypeError].
|
||||||
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
|
|
||||||
|
use crate::FunSel;
|
||||||
|
|
||||||
|
use va416xx as pac;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId,
|
AsyncDynPinError, FilterClkSel, FilterType, InputDynPinAsync, InterruptEdge, InterruptLevel,
|
||||||
PinMode, PinState,
|
IsMaskedError, Pin, PinId, PinMode, PinState, Port,
|
||||||
};
|
};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -155,61 +158,213 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
|
|||||||
// DynGroup & DynPinId
|
// DynGroup & DynPinId
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Value-level `enum` for pin groups
|
pub type DynGroup = Port;
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[inline]
|
||||||
pub enum DynGroup {
|
pub const fn irq_id(port: Port, num: u8) -> Option<va416xx::Interrupt> {
|
||||||
A,
|
match port {
|
||||||
B,
|
Port::A => match num {
|
||||||
C,
|
0 => Some(va416xx::Interrupt::PORTA0),
|
||||||
D,
|
1 => Some(va416xx::Interrupt::PORTA1),
|
||||||
E,
|
2 => Some(va416xx::Interrupt::PORTA2),
|
||||||
F,
|
3 => Some(va416xx::Interrupt::PORTA3),
|
||||||
G,
|
4 => Some(va416xx::Interrupt::PORTA4),
|
||||||
|
5 => Some(va416xx::Interrupt::PORTA5),
|
||||||
|
6 => Some(va416xx::Interrupt::PORTA6),
|
||||||
|
7 => Some(va416xx::Interrupt::PORTA7),
|
||||||
|
8 => Some(va416xx::Interrupt::PORTA8),
|
||||||
|
9 => Some(va416xx::Interrupt::PORTA9),
|
||||||
|
10 => Some(va416xx::Interrupt::PORTA10),
|
||||||
|
11 => Some(va416xx::Interrupt::PORTA11),
|
||||||
|
12 => Some(va416xx::Interrupt::PORTA12),
|
||||||
|
13 => Some(va416xx::Interrupt::PORTA13),
|
||||||
|
14 => Some(va416xx::Interrupt::PORTA14),
|
||||||
|
15 => Some(va416xx::Interrupt::PORTA15),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Port::B => match num {
|
||||||
|
0 => Some(va416xx::Interrupt::PORTB0),
|
||||||
|
1 => Some(va416xx::Interrupt::PORTB1),
|
||||||
|
2 => Some(va416xx::Interrupt::PORTB2),
|
||||||
|
3 => Some(va416xx::Interrupt::PORTB3),
|
||||||
|
4 => Some(va416xx::Interrupt::PORTB4),
|
||||||
|
5 => Some(va416xx::Interrupt::PORTB5),
|
||||||
|
6 => Some(va416xx::Interrupt::PORTB6),
|
||||||
|
7 => Some(va416xx::Interrupt::PORTB7),
|
||||||
|
8 => Some(va416xx::Interrupt::PORTB8),
|
||||||
|
9 => Some(va416xx::Interrupt::PORTB9),
|
||||||
|
10 => Some(va416xx::Interrupt::PORTB10),
|
||||||
|
11 => Some(va416xx::Interrupt::PORTB11),
|
||||||
|
12 => Some(va416xx::Interrupt::PORTB12),
|
||||||
|
13 => Some(va416xx::Interrupt::PORTB13),
|
||||||
|
14 => Some(va416xx::Interrupt::PORTB14),
|
||||||
|
15 => Some(va416xx::Interrupt::PORTB15),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Port::C => match num {
|
||||||
|
0 => Some(va416xx::Interrupt::PORTC0),
|
||||||
|
1 => Some(va416xx::Interrupt::PORTC1),
|
||||||
|
2 => Some(va416xx::Interrupt::PORTC2),
|
||||||
|
3 => Some(va416xx::Interrupt::PORTC3),
|
||||||
|
4 => Some(va416xx::Interrupt::PORTC4),
|
||||||
|
5 => Some(va416xx::Interrupt::PORTC5),
|
||||||
|
6 => Some(va416xx::Interrupt::PORTC6),
|
||||||
|
7 => Some(va416xx::Interrupt::PORTC7),
|
||||||
|
8 => Some(va416xx::Interrupt::PORTC8),
|
||||||
|
9 => Some(va416xx::Interrupt::PORTC9),
|
||||||
|
10 => Some(va416xx::Interrupt::PORTC10),
|
||||||
|
11 => Some(va416xx::Interrupt::PORTC11),
|
||||||
|
12 => Some(va416xx::Interrupt::PORTC12),
|
||||||
|
13 => Some(va416xx::Interrupt::PORTC13),
|
||||||
|
14 => Some(va416xx::Interrupt::PORTC14),
|
||||||
|
15 => Some(va416xx::Interrupt::PORTC15),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Port::D => match num {
|
||||||
|
0 => Some(va416xx::Interrupt::PORTD0),
|
||||||
|
1 => Some(va416xx::Interrupt::PORTD1),
|
||||||
|
2 => Some(va416xx::Interrupt::PORTD2),
|
||||||
|
3 => Some(va416xx::Interrupt::PORTD3),
|
||||||
|
4 => Some(va416xx::Interrupt::PORTD4),
|
||||||
|
5 => Some(va416xx::Interrupt::PORTD5),
|
||||||
|
6 => Some(va416xx::Interrupt::PORTD6),
|
||||||
|
7 => Some(va416xx::Interrupt::PORTD7),
|
||||||
|
8 => Some(va416xx::Interrupt::PORTD8),
|
||||||
|
9 => Some(va416xx::Interrupt::PORTD9),
|
||||||
|
10 => Some(va416xx::Interrupt::PORTD10),
|
||||||
|
11 => Some(va416xx::Interrupt::PORTD11),
|
||||||
|
12 => Some(va416xx::Interrupt::PORTD12),
|
||||||
|
13 => Some(va416xx::Interrupt::PORTD13),
|
||||||
|
14 => Some(va416xx::Interrupt::PORTD14),
|
||||||
|
15 => Some(va416xx::Interrupt::PORTD15),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Port::E => match num {
|
||||||
|
0 => Some(va416xx::Interrupt::PORTE0),
|
||||||
|
1 => Some(va416xx::Interrupt::PORTE1),
|
||||||
|
2 => Some(va416xx::Interrupt::PORTE2),
|
||||||
|
3 => Some(va416xx::Interrupt::PORTE3),
|
||||||
|
4 => Some(va416xx::Interrupt::PORTE4),
|
||||||
|
5 => Some(va416xx::Interrupt::PORTE5),
|
||||||
|
6 => Some(va416xx::Interrupt::PORTE6),
|
||||||
|
7 => Some(va416xx::Interrupt::PORTE7),
|
||||||
|
8 => Some(va416xx::Interrupt::PORTE8),
|
||||||
|
9 => Some(va416xx::Interrupt::PORTE9),
|
||||||
|
10 => Some(va416xx::Interrupt::PORTE10),
|
||||||
|
11 => Some(va416xx::Interrupt::PORTE11),
|
||||||
|
12 => Some(va416xx::Interrupt::PORTE12),
|
||||||
|
13 => Some(va416xx::Interrupt::PORTE13),
|
||||||
|
14 => Some(va416xx::Interrupt::PORTE14),
|
||||||
|
15 => Some(va416xx::Interrupt::PORTE15),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Port::F => match num {
|
||||||
|
0 => Some(va416xx::Interrupt::PORTF0),
|
||||||
|
1 => Some(va416xx::Interrupt::PORTF1),
|
||||||
|
2 => Some(va416xx::Interrupt::PORTF2),
|
||||||
|
3 => Some(va416xx::Interrupt::PORTF3),
|
||||||
|
4 => Some(va416xx::Interrupt::PORTF4),
|
||||||
|
5 => Some(va416xx::Interrupt::PORTF5),
|
||||||
|
6 => Some(va416xx::Interrupt::PORTF6),
|
||||||
|
7 => Some(va416xx::Interrupt::PORTF7),
|
||||||
|
8 => Some(va416xx::Interrupt::PORTF8),
|
||||||
|
9 => Some(va416xx::Interrupt::PORTF9),
|
||||||
|
10 => Some(va416xx::Interrupt::PORTF10),
|
||||||
|
11 => Some(va416xx::Interrupt::PORTF11),
|
||||||
|
12 => Some(va416xx::Interrupt::PORTF12),
|
||||||
|
13 => Some(va416xx::Interrupt::PORTF13),
|
||||||
|
14 => Some(va416xx::Interrupt::PORTF14),
|
||||||
|
15 => Some(va416xx::Interrupt::PORTF15),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Port::G => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Value-level `struct` representing pin IDs
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct DynPinId {
|
pub struct DynPinId {
|
||||||
pub group: DynGroup,
|
port: Port,
|
||||||
pub num: u8,
|
num: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
impl DynPinId {
|
||||||
// DynRegisters
|
pub const fn new(port: Port, num: u8) -> Self {
|
||||||
//==============================================================================
|
DynPinId { port, num }
|
||||||
|
}
|
||||||
|
|
||||||
/// Provide a safe register interface for [`DynPin`]s
|
pub const fn port(&self) -> Port {
|
||||||
///
|
self.port
|
||||||
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
}
|
||||||
/// access the corresponding regsiters.
|
pub const fn num(&self) -> u8 {
|
||||||
#[derive(Debug)]
|
self.num
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub(crate) struct DynRegisters(DynPinId);
|
|
||||||
|
|
||||||
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
|
||||||
// guarantees that each pin is a singleton, so this implementation is safe.
|
|
||||||
unsafe impl RegisterInterface for DynRegisters {
|
|
||||||
#[inline]
|
|
||||||
fn id(&self) -> DynPinId {
|
|
||||||
self.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynRegisters {
|
//==================================================================================================
|
||||||
/// Create a new instance of [`DynRegisters`]
|
// ModeFields
|
||||||
///
|
//==================================================================================================
|
||||||
/// # Safety
|
|
||||||
///
|
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
||||||
/// Users must never create two simultaneous instances of this `struct` with
|
#[derive(Default)]
|
||||||
/// the same [`DynPinId`]
|
struct ModeFields {
|
||||||
|
dir: bool,
|
||||||
|
opendrn: bool,
|
||||||
|
pull_en: bool,
|
||||||
|
/// true for pullup, false for pulldown
|
||||||
|
pull_dir: bool,
|
||||||
|
funsel: u8,
|
||||||
|
enb_input: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DynPinMode> for ModeFields {
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn new(id: DynPinId) -> Self {
|
fn from(mode: DynPinMode) -> Self {
|
||||||
DynRegisters(id)
|
let mut fields = Self::default();
|
||||||
|
match mode {
|
||||||
|
DynPinMode::Input(config) => {
|
||||||
|
fields.dir = false;
|
||||||
|
fields.funsel = FunSel::Sel0 as u8;
|
||||||
|
match config {
|
||||||
|
DynInput::Floating => (),
|
||||||
|
DynInput::PullUp => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
fields.pull_dir = true;
|
||||||
|
}
|
||||||
|
DynInput::PullDown => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DynPinMode::Output(config) => {
|
||||||
|
fields.dir = true;
|
||||||
|
fields.funsel = FunSel::Sel0 as u8;
|
||||||
|
match config {
|
||||||
|
DynOutput::PushPull => (),
|
||||||
|
DynOutput::OpenDrain => {
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
DynOutput::ReadableOpenDrain => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
DynOutput::ReadablePushPull => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DynPinMode::Alternate(config) => {
|
||||||
|
fields.funsel = config as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type definition to avoid confusion: These register blocks are identical
|
||||||
|
type PortRegisterBlock = pac::porta::RegisterBlock;
|
||||||
|
pub type PortReg = pac::ioconfig::Porta;
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// DynPin
|
// DynPin
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -221,58 +376,89 @@ impl DynRegisters {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct DynPin {
|
pub struct DynPin {
|
||||||
pub(crate) regs: DynRegisters,
|
id: DynPinId,
|
||||||
mode: DynPinMode,
|
mode: DynPinMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynPin {
|
impl DynPin {
|
||||||
/// Create a new [`DynPin`]
|
/// Create a new [DynPin]
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there
|
/// Each [DynPin] must be a singleton. For a given [DynPinId], there
|
||||||
/// must be at most one corresponding [`DynPin`] in existence at any given
|
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||||
/// time. Violating this requirement is `unsafe`.
|
/// time. Violating this requirement is `unsafe`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||||
|
DynPin { id, mode }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Steals a new [DynPin].
|
||||||
|
///
|
||||||
|
/// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without
|
||||||
|
/// modifying any registers related to the behaviour of the pin. The user should call
|
||||||
|
/// [Self::into_mode] to ensure the correct mode of the pin.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not
|
||||||
|
/// used cocurrently somewhere else. The caller might also want to call [Self::into_mode]
|
||||||
|
/// to ensure the correct desired state of the pin. It is recommended to create the pin using
|
||||||
|
/// [Pin::downgrade] instead.
|
||||||
|
pub const unsafe fn steal(id: DynPinId) -> Self {
|
||||||
DynPin {
|
DynPin {
|
||||||
regs: DynRegisters::new(id),
|
id,
|
||||||
mode,
|
mode: DYN_FLOATING_INPUT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin ID
|
/// Return a copy of the pin ID
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> DynPinId {
|
pub const fn id(&self) -> DynPinId {
|
||||||
self.regs.0
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn irq_id(&self) -> Option<va416xx::Interrupt> {
|
||||||
|
irq_id(self.id.port(), self.id.num())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin mode
|
/// Return a copy of the pin mode
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mode(&self) -> DynPinMode {
|
pub const fn mode(&self) -> DynPinMode {
|
||||||
self.mode
|
self.mode
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the pin to the requested [`DynPinMode`]
|
/// Convert the pin to the requested [`DynPinMode`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_mode(&mut self, mode: DynPinMode) {
|
pub fn into_mode(&mut self, mode: DynPinMode) {
|
||||||
// Only modify registers if we are actually changing pin mode
|
self.change_mode(mode);
|
||||||
if mode != self.mode {
|
|
||||||
self.regs.change_mode(mode);
|
|
||||||
self.mode = mode;
|
self.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_input_pin(&self) -> bool {
|
||||||
|
matches!(self.mode, DynPinMode::Input(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_output_pin(&self) -> bool {
|
||||||
|
matches!(self.mode, DynPinMode::Output(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 1. See Programmer Guide p.286 for the function table
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_funsel_1(&mut self) {
|
pub fn into_funsel_1(&mut self) {
|
||||||
self.into_mode(DYN_ALT_FUNC_1);
|
self.into_mode(DYN_ALT_FUNC_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 2. See Programmer Guide p.286 for the function table
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_funsel_2(&mut self) {
|
pub fn into_funsel_2(&mut self) {
|
||||||
self.into_mode(DYN_ALT_FUNC_2);
|
self.into_mode(DYN_ALT_FUNC_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 3. See Programmer Guide p.286 for the function table
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_funsel_3(&mut self) {
|
pub fn into_funsel_3(&mut self) {
|
||||||
self.into_mode(DYN_ALT_FUNC_3);
|
self.into_mode(DYN_ALT_FUNC_3);
|
||||||
@ -320,47 +506,118 @@ impl DynPin {
|
|||||||
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self.read_internal().map(|v| !v)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self.read_internal()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
self.write_internal(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
self.write_internal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the logic level of an output pin
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn toggle(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
if !self.is_output_pin() {
|
||||||
|
return Err(InvalidPinTypeError(self.mode));
|
||||||
|
}
|
||||||
|
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable_interrupt(&self) {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn disable_interrupt(&self) {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||||
|
///
|
||||||
|
/// There is no way for the compiler to know if the conversion will be
|
||||||
|
/// successful at compile-time. We must verify the conversion at run-time
|
||||||
|
/// or refuse to perform it.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
||||||
|
if self.id == I::DYN && self.mode == M::DYN {
|
||||||
|
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `Pin`
|
||||||
|
return Ok(unsafe { Pin::new() });
|
||||||
|
}
|
||||||
|
Err(InvalidPinTypeError(self.mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin into an async pin. The pin can be converted back by calling
|
||||||
|
/// [InputDynPinAsync::release]
|
||||||
|
pub fn into_async_input(self) -> Result<InputDynPinAsync, AsyncDynPinError> {
|
||||||
|
InputDynPinAsync::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get DATAMASK bit for this particular pin
|
||||||
|
#[inline(always)]
|
||||||
pub fn datamask(&self) -> bool {
|
pub fn datamask(&self) -> bool {
|
||||||
self.regs.datamask()
|
(self.port_reg().datamask().read().bits() >> self.id().num) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Clear DATAMASK bit for this particular pin. This prevents access
|
||||||
pub fn clear_datamask(&mut self) {
|
/// of the corresponding bit for output and input operations
|
||||||
self.regs.clear_datamask();
|
#[inline(always)]
|
||||||
|
pub fn clear_datamask(&self) {
|
||||||
|
self.port_reg()
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Set DATAMASK bit for this particular pin. 1 is the default
|
||||||
pub fn set_datamask(&mut self) {
|
/// state of the bit and allows access of the corresponding bit
|
||||||
self.regs.set_datamask();
|
#[inline(always)]
|
||||||
|
pub fn set_datamask(&self) {
|
||||||
|
self.port_reg()
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
self.regs.read_pin_masked()
|
self.read_pin_masked()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
self.regs.read_pin_masked().map(|v| !v)
|
self.read_pin_masked().map(|v| !v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
self.regs.write_pin_masked(true)
|
self.write_pin_masked(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
self.regs.write_pin_masked(false)
|
self.write_pin_masked(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// See p.293 of the programmers guide for more information.
|
||||||
pub fn irq_enb(&mut self) {
|
|
||||||
self.regs.enable_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See p.53 of the programmers guide for more information.
|
|
||||||
/// Possible delays in clock cycles:
|
/// Possible delays in clock cycles:
|
||||||
/// - Delay 1: 1
|
/// - Delay 1: 1
|
||||||
/// - Delay 2: 2
|
/// - Delay 2: 2
|
||||||
@ -373,14 +630,14 @@ impl DynPin {
|
|||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.regs.configure_delay(delay_1, delay_2);
|
self.configure_delay_internal(delay_1, delay_2);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See p.52 of the programmers guide for more information.
|
/// See p.293 of the programmers guide for more information.
|
||||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
/// one clock cycle before returning to the configured default state
|
/// one clock cycle before returning to the configured default state
|
||||||
pub fn configure_pulse_mode(
|
pub fn configure_pulse_mode(
|
||||||
@ -390,14 +647,14 @@ impl DynPin {
|
|||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.regs.configure_pulse_mode(enable, default_state);
|
self.configure_pulse_mode_internal(enable, default_state);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See p.37 and p.38 of the programmers guide for more information.
|
/// See p.284 of the programmers guide for more information.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_filter_type(
|
pub fn configure_filter_type(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -406,7 +663,7 @@ impl DynPin {
|
|||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) => {
|
DynPinMode::Input(_) => {
|
||||||
self.regs.configure_filter_type(filter, clksel);
|
self.configure_filter_type_internal(filter, clksel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
@ -419,8 +676,7 @@ impl DynPin {
|
|||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
self.regs.configure_edge_interrupt(edge_type);
|
self.configure_edge_interrupt_internal(edge_type);
|
||||||
self.irq_enb();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
@ -433,64 +689,273 @@ impl DynPin {
|
|||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
self.regs.configure_level_interrupt(level_type);
|
self.configure_level_interrupt_internal(level_type);
|
||||||
self.irq_enb();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
fn _read(&self) -> Result<bool, InvalidPinTypeError> {
|
fn read_internal(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||||
Ok(self.regs.read_pin())
|
Ok(self.read_pin())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
#[inline(always)]
|
||||||
|
fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.regs.write_pin(bit);
|
self.write_pin(bit);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change the pin mode
|
||||||
#[inline]
|
#[inline]
|
||||||
fn _is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
pub(crate) fn change_mode(&mut self, mode: DynPinMode) {
|
||||||
self._read().map(|v| !v)
|
let ModeFields {
|
||||||
|
dir,
|
||||||
|
funsel,
|
||||||
|
opendrn,
|
||||||
|
pull_dir,
|
||||||
|
pull_en,
|
||||||
|
enb_input,
|
||||||
|
} = mode.into();
|
||||||
|
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
||||||
|
iocfg.write(|w| {
|
||||||
|
w.opendrn().bit(opendrn);
|
||||||
|
w.pen().bit(pull_en);
|
||||||
|
w.plevel().bit(pull_dir);
|
||||||
|
w.iewo().bit(enb_input);
|
||||||
|
unsafe { w.funsel().bits(funsel) }
|
||||||
|
});
|
||||||
|
let mask = self.mask_32();
|
||||||
|
unsafe {
|
||||||
|
if dir {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||||
|
// Clear output
|
||||||
|
portreg.clrout().write(|w| w.bits(mask));
|
||||||
|
} else {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn _is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
|
||||||
self._read()
|
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn _set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
|
||||||
self._write(false)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
|
||||||
self._write(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
#[inline(always)]
|
||||||
///
|
const fn port_reg(&self) -> &PortRegisterBlock {
|
||||||
/// There is no way for the compiler to know if the conversion will be
|
match self.id().port() {
|
||||||
/// successful at compile-time. We must verify the conversion at run-time
|
Port::A => unsafe { &(*pac::Porta::ptr()) },
|
||||||
/// or refuse to perform it.
|
Port::B => unsafe { &(*pac::Portb::ptr()) },
|
||||||
#[inline]
|
Port::C => unsafe { &(*pac::Portc::ptr()) },
|
||||||
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
Port::D => unsafe { &(*pac::Portd::ptr()) },
|
||||||
if self.regs.0 == I::DYN && self.mode == M::DYN {
|
Port::E => unsafe { &(*pac::Porte::ptr()) },
|
||||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
Port::F => unsafe { &(*pac::Portf::ptr()) },
|
||||||
// corresponding `Pin`
|
Port::G => unsafe { &(*pac::Portg::ptr()) },
|
||||||
return Ok(unsafe { Pin::new() });
|
|
||||||
}
|
}
|
||||||
Err(InvalidPinTypeError(self.mode))
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn iocfg_port(&self) -> &PortReg {
|
||||||
|
let ioconfig = unsafe { pac::Ioconfig::ptr().as_ref().unwrap() };
|
||||||
|
match self.id().port() {
|
||||||
|
Port::A => ioconfig.porta(self.id().num() as usize),
|
||||||
|
Port::B => ioconfig.portb0(self.id().num() as usize),
|
||||||
|
Port::C => ioconfig.portc0(self.id().num() as usize),
|
||||||
|
Port::D => ioconfig.portd0(self.id().num() as usize),
|
||||||
|
Port::E => ioconfig.porte0(self.id().num() as usize),
|
||||||
|
Port::F => ioconfig.portf0(self.id().num() as usize),
|
||||||
|
Port::G => ioconfig.portg0(self.id().num() as usize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.id().num()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Read the logic level of an output pin
|
||||||
|
pub(crate) fn read_pin(&self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(IsMaskedError)
|
||||||
|
} else {
|
||||||
|
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn write_pin(&mut self, bit: bool) {
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
#[inline]
|
||||||
|
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(IsMaskedError)
|
||||||
|
} else {
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
||||||
|
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
||||||
|
#[inline]
|
||||||
|
fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) {
|
||||||
|
unsafe {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_sen()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
match edge_type {
|
||||||
|
InterruptEdge::HighToLow => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
InterruptEdge::LowToHigh => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
InterruptEdge::BothEdges => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_edge()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure which edge or level type triggers an interrupt
|
||||||
|
#[inline]
|
||||||
|
fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) {
|
||||||
|
unsafe {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_sen()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
if level == InterruptLevel::Low {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for input pins
|
||||||
|
#[inline]
|
||||||
|
fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
|
self.iocfg_port().modify(|_, w| {
|
||||||
|
// Safety: Only write to register for this Pin ID
|
||||||
|
unsafe {
|
||||||
|
w.flttype().bits(filter as u8);
|
||||||
|
w.fltclk().bits(clksel as u8)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for output pins
|
||||||
|
/// See p.293 of the programmers guide for more information.
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
#[inline]
|
||||||
|
fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
if enable {
|
||||||
|
portreg
|
||||||
|
.pulse()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.pulse()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
if default_state == PinState::Low {
|
||||||
|
portreg
|
||||||
|
.pulsebase()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.pulsebase()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for output pins
|
||||||
|
#[inline]
|
||||||
|
fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
if delay_1 {
|
||||||
|
portreg
|
||||||
|
.delay1()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.delay1()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
if delay_2 {
|
||||||
|
portreg
|
||||||
|
.delay2()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.delay2()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only serves disambiguation purposes for the Embedded HAL impl
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_low_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self.is_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only serves disambiguation purposes for the Embedded HAL impl
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_high_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self.is_high()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,39 +998,44 @@ where
|
|||||||
// Embedded HAL v1 traits
|
// Embedded HAL v1 traits
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
impl ErrorType for DynPin {
|
impl embedded_hal::digital::ErrorType for DynPin {
|
||||||
type Error = InvalidPinTypeError;
|
type Error = InvalidPinTypeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputPin for DynPin {
|
impl embedded_hal::digital::OutputPin for DynPin {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
self._set_high()
|
self.set_high()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
self._set_low()
|
self.set_low()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputPin for DynPin {
|
impl embedded_hal::digital::InputPin for DynPin {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
self._is_high()
|
self.is_high_mut()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
self._is_low()
|
self.is_low_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatefulOutputPin for DynPin {
|
impl embedded_hal::digital::StatefulOutputPin for DynPin {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
self._is_high()
|
self.is_high_mut()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
self._is_low()
|
self.is_low_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.toggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,15 +21,60 @@
|
|||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Errors, Definitions and Constants
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub const NUM_PINS_PORT_A_TO_F: usize = 16;
|
||||||
|
pub const NUM_PINS_PORT_G: usize = 8;
|
||||||
|
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A_TO_F * 6 + NUM_PINS_PORT_G;
|
||||||
|
pub const NUM_GPIO_PINS_WITH_IRQ: usize = NUM_GPIO_PINS - NUM_PINS_PORT_G;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("pin is masked")]
|
#[error("pin is masked")]
|
||||||
pub struct IsMaskedError;
|
pub struct IsMaskedError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Port {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
D,
|
||||||
|
E,
|
||||||
|
F,
|
||||||
|
G,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum InterruptEdge {
|
||||||
|
HighToLow,
|
||||||
|
LowToHigh,
|
||||||
|
BothEdges,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum InterruptLevel {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum PinState {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
pub mod pin;
|
pub mod pin;
|
||||||
pub use pin::*;
|
pub use pin::*;
|
||||||
|
|
||||||
pub mod dynpin;
|
pub mod dynpin;
|
||||||
pub use dynpin::*;
|
pub use dynpin::*;
|
||||||
|
|
||||||
mod reg;
|
pub mod asynch;
|
||||||
|
pub use asynch::*;
|
||||||
|
@ -68,46 +68,19 @@
|
|||||||
//! # Embedded HAL traits
|
//! # Embedded HAL traits
|
||||||
//!
|
//!
|
||||||
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
||||||
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
|
//! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin],
|
||||||
//! and [`StatefulOutputPin`].
|
//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
|
||||||
use core::{convert::Infallible, marker::PhantomData, mem::transmute};
|
use core::{convert::Infallible, marker::PhantomData, mem::transmute};
|
||||||
|
|
||||||
pub use crate::clock::FilterClkSel;
|
pub use crate::clock::FilterClkSel;
|
||||||
use crate::typelevel::Sealed;
|
use crate::typelevel::Sealed;
|
||||||
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
|
use va416xx::{self as pac, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
||||||
use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPin, DynPinId,
|
DynAlternate, DynInput, DynOutput, DynPin, DynPinId, DynPinMode, InputPinAsync, InterruptEdge,
|
||||||
DynPinMode,
|
InterruptLevel, PinState, Port, PortGDoesNotSupportAsyncError,
|
||||||
};
|
};
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// Errors and Definitions
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum InterruptEdge {
|
|
||||||
HighToLow,
|
|
||||||
LowToHigh,
|
|
||||||
BothEdges,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum InterruptLevel {
|
|
||||||
Low = 0,
|
|
||||||
High = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum PinState {
|
|
||||||
Low = 0,
|
|
||||||
High = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Input configuration
|
// Input configuration
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -296,10 +269,11 @@ impl<C: AlternateConfig> PinMode for Alternate<C> {
|
|||||||
pub trait PinId: Sealed {
|
pub trait PinId: Sealed {
|
||||||
/// Corresponding [DynPinId]
|
/// Corresponding [DynPinId]
|
||||||
const DYN: DynPinId;
|
const DYN: DynPinId;
|
||||||
|
const IRQ: Option<pac::Interrupt>;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! pin_id {
|
macro_rules! pin_id {
|
||||||
($Group:ident, $Id:ident, $NUM:literal $(, $meta: meta)?) => {
|
($Port:ident, $Id:ident, $NUM:literal, $Irq:expr, $(, $meta: meta)?) => {
|
||||||
// Need paste macro to use ident in doc attribute
|
// Need paste macro to use ident in doc attribute
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
$(#[$meta])?
|
$(#[$meta])?
|
||||||
@ -311,10 +285,8 @@ macro_rules! pin_id {
|
|||||||
|
|
||||||
$(#[$meta])?
|
$(#[$meta])?
|
||||||
impl PinId for $Id {
|
impl PinId for $Id {
|
||||||
const DYN: DynPinId = DynPinId {
|
const DYN: DynPinId = DynPinId::new(Port::$Port, $NUM);
|
||||||
group: DynGroup::$Group,
|
const IRQ: Option<pac::Interrupt> = $Irq;
|
||||||
num: $NUM,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -340,7 +312,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
/// at most one corresponding [`Pin`] in existence at any given time.
|
/// at most one corresponding [`Pin`] in existence at any given time.
|
||||||
/// Violating this requirement is `unsafe`.
|
/// Violating this requirement is `unsafe`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn new() -> Pin<I, M> {
|
pub(crate) const unsafe fn new() -> Pin<I, M> {
|
||||||
Pin {
|
Pin {
|
||||||
inner: DynPin::new(I::DYN, M::DYN),
|
inner: DynPin::new(I::DYN, M::DYN),
|
||||||
mode: PhantomData,
|
mode: PhantomData,
|
||||||
@ -348,35 +320,40 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> DynPinId {
|
pub const fn id(&self) -> DynPinId {
|
||||||
self.inner.id()
|
self.inner.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn irq_id(&self) -> Option<pac::Interrupt> {
|
||||||
|
I::IRQ
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert the pin to the requested [`PinMode`]
|
/// Convert the pin to the requested [`PinMode`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
||||||
// Only modify registers if we are actually changing pin mode
|
// Only modify registers if we are actually changing pin mode
|
||||||
// This check should compile away
|
// This check should compile away
|
||||||
if N::DYN != M::DYN {
|
if N::DYN != M::DYN {
|
||||||
self.inner.regs.change_mode(N::DYN);
|
self.inner.change_mode(N::DYN);
|
||||||
}
|
}
|
||||||
// Safe because we drop the existing Pin
|
// Safe because we drop the existing Pin
|
||||||
unsafe { Pin::new() }
|
unsafe { Pin::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the pin for function select 1. See Programmer Guide p.40 for the function table
|
/// Configure the pin for function select 1. See Programmer Guide p. 286 for the function table
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_funsel_1(self) -> Pin<I, AltFunc1> {
|
pub fn into_funsel_1(self) -> Pin<I, AltFunc1> {
|
||||||
self.into_mode()
|
self.into_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the pin for function select 2. See Programmer Guide p.40 for the function table
|
/// Configure the pin for function select 2. See Programmer Guide p. 286 for the function table
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_funsel_2(self) -> Pin<I, AltFunc2> {
|
pub fn into_funsel_2(self) -> Pin<I, AltFunc2> {
|
||||||
self.into_mode()
|
self.into_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the pin for function select 3. See Programmer Guide p.40 for the function table
|
/// Configure the pin for function select 3. See Programmer Guide p. 286 for the function table
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_funsel_3(self) -> Pin<I, AltFunc3> {
|
pub fn into_funsel_3(self) -> Pin<I, AltFunc3> {
|
||||||
self.into_mode()
|
self.into_mode()
|
||||||
@ -418,6 +395,16 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
self.into_mode()
|
self.into_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low(&self) -> bool {
|
||||||
|
!self.inner.read_pin()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_high(&self) -> bool {
|
||||||
|
self.inner.read_pin()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn datamask(&self) -> bool {
|
pub fn datamask(&self) -> bool {
|
||||||
self.inner.datamask()
|
self.inner.datamask()
|
||||||
@ -443,43 +430,41 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
self.inner.is_low_masked()
|
self.inner.is_low_masked()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
|
||||||
self.inner.set_high_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
|
||||||
self.inner.set_low_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn downgrade(self) -> DynPin {
|
pub fn downgrade(self) -> DynPin {
|
||||||
self.inner
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irq_enb(&mut self) {
|
// Those only serve for the embedded HAL implementations which have different mutability.
|
||||||
self.inner.irq_enb();
|
|
||||||
|
#[inline]
|
||||||
|
fn is_low_mut(&mut self) -> bool {
|
||||||
|
self.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn _set_high(&mut self) {
|
fn is_high_mut(&mut self) -> bool {
|
||||||
self.inner.regs.write_pin(true)
|
self.is_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn _set_low(&mut self) {
|
pub fn enable_interrupt(&mut self) {
|
||||||
self.inner.regs.write_pin(false)
|
self.inner.enable_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn _is_low(&self) -> bool {
|
pub fn disable_interrupt(&mut self) {
|
||||||
!self.inner.regs.read_pin()
|
self.inner.disable_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Configure the pin for an edge interrupt but does not enable the interrupt.
|
||||||
pub(crate) fn _is_high(&self) -> bool {
|
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
||||||
self.inner.regs.read_pin()
|
self.inner.configure_edge_interrupt(edge_type).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for a level interrupt but does not enable the interrupt.
|
||||||
|
pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) {
|
||||||
|
self.inner.configure_level_interrupt(level_type).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,56 +556,61 @@ impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
/// Convert the pin into an async pin. The pin can be converted back by calling
|
||||||
self.inner.regs.configure_edge_interrupt(edge_type);
|
/// [InputPinAsync::release]
|
||||||
self.irq_enb();
|
pub fn into_async_input(self) -> Result<InputPinAsync<I, C>, PortGDoesNotSupportAsyncError> {
|
||||||
}
|
InputPinAsync::new(self)
|
||||||
|
|
||||||
pub fn configure_interrupt_level(&mut self, level_type: InterruptLevel) {
|
|
||||||
self.inner.regs.configure_level_interrupt(level_type);
|
|
||||||
self.irq_enb();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||||
/// See p.53 of the programmers guide for more information.
|
#[inline]
|
||||||
|
pub fn set_high(&mut self) {
|
||||||
|
self.inner.write_pin(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low(&mut self) {
|
||||||
|
self.inner.write_pin(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn toggle(&mut self) {
|
||||||
|
self.inner.toggle().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.inner.set_high_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.inner.set_low_masked()
|
||||||
|
}
|
||||||
|
|
||||||
/// Possible delays in clock cycles:
|
/// Possible delays in clock cycles:
|
||||||
/// - Delay 1: 1
|
/// - Delay 1: 1
|
||||||
/// - Delay 2: 2
|
/// - Delay 2: 2
|
||||||
/// - Delay 1 + Delay 2: 3
|
/// - Delay 1 + Delay 2: 3
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
||||||
self.inner.regs.configure_delay(delay_1, delay_2);
|
self.inner.configure_delay(delay_1, delay_2).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn toggle_with_toggle_reg(&mut self) {
|
|
||||||
self.inner.regs.toggle()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See p.52 of the programmers guide for more information.
|
|
||||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
/// one clock cycle before returning to the configured default state
|
/// one clock cycle before returning to the configured default state
|
||||||
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||||
self.inner.regs.configure_pulse_mode(enable, default_state);
|
self.inner
|
||||||
}
|
.configure_pulse_mode(enable, default_state)
|
||||||
|
.unwrap();
|
||||||
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
|
||||||
self.inner.regs.configure_edge_interrupt(edge_type);
|
|
||||||
self.irq_enb();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) {
|
|
||||||
self.inner.regs.configure_level_interrupt(level_type);
|
|
||||||
self.irq_enb();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
/// See p.37 and p.38 of the programmers guide for more information.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
self.inner.regs.configure_filter_type(filter, clksel);
|
self.inner.configure_filter_type(filter, clksel).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,7 +618,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
|||||||
// Embedded HAL traits
|
// Embedded HAL traits
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
impl<I, M> ErrorType for Pin<I, M>
|
impl<I, M> embedded_hal::digital::ErrorType for Pin<I, M>
|
||||||
where
|
where
|
||||||
I: PinId,
|
I: PinId,
|
||||||
M: PinMode,
|
M: PinMode,
|
||||||
@ -636,63 +626,69 @@ where
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, C: OutputConfig> OutputPin for Pin<I, Output<C>> {
|
impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
self._set_high();
|
self.set_high();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
self._set_low();
|
self.set_low();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, C> InputPin for Pin<I, Input<C>>
|
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Input<C>>
|
||||||
where
|
where
|
||||||
I: PinId,
|
I: PinId,
|
||||||
C: InputConfig,
|
C: InputConfig,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self._is_high())
|
Ok(self.is_high_mut())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self._is_low())
|
Ok(self.is_low_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, C> StatefulOutputPin for Pin<I, Output<C>>
|
impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
|
||||||
where
|
where
|
||||||
I: PinId,
|
I: PinId,
|
||||||
C: OutputConfig + ReadableOutput,
|
C: OutputConfig + ReadableOutput,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self._is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self._is_low())
|
Ok(self.is_low())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.toggle();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, C> InputPin for Pin<I, Output<C>>
|
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Output<C>>
|
||||||
where
|
where
|
||||||
I: PinId,
|
I: PinId,
|
||||||
C: OutputConfig + ReadableOutput,
|
C: OutputConfig + ReadableOutput,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self._is_high())
|
Ok(self.is_high_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self._is_low())
|
Ok(self.is_low_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -754,18 +750,31 @@ macro_rules! pins {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! declare_pins_with_irq {
|
||||||
|
(
|
||||||
|
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]
|
||||||
|
) => {
|
||||||
|
pins!($Port, $PinsName, $($Id $(, $meta)?)+,);
|
||||||
|
$(
|
||||||
|
paste::paste! {
|
||||||
|
pin_id!($Group, $Id, $NUM, Some(pac::Interrupt::[<$Port:upper $NUM>]), $(, $meta)?);
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! declare_pins {
|
macro_rules! declare_pins {
|
||||||
(
|
(
|
||||||
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]
|
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]
|
||||||
) => {
|
) => {
|
||||||
pins!($Port, $PinsName, $($Id $(, $meta)?)+,);
|
pins!($Port, $PinsName, $($Id $(, $meta)?)+,);
|
||||||
$(
|
$(
|
||||||
pin_id!($Group, $Id, $NUM $(, $meta)?);
|
pin_id!($Group, $Id, $NUM, None, $(, $meta)?);
|
||||||
)+
|
)+
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_pins!(
|
declare_pins_with_irq!(
|
||||||
A,
|
A,
|
||||||
PinsA,
|
PinsA,
|
||||||
Porta,
|
Porta,
|
||||||
@ -789,7 +798,7 @@ declare_pins!(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_pins!(
|
declare_pins_with_irq!(
|
||||||
B,
|
B,
|
||||||
PinsB,
|
PinsB,
|
||||||
Portb,
|
Portb,
|
||||||
@ -813,7 +822,7 @@ declare_pins!(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_pins!(
|
declare_pins_with_irq!(
|
||||||
C,
|
C,
|
||||||
PinsC,
|
PinsC,
|
||||||
Portc,
|
Portc,
|
||||||
@ -837,7 +846,7 @@ declare_pins!(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_pins!(
|
declare_pins_with_irq!(
|
||||||
D,
|
D,
|
||||||
PinsD,
|
PinsD,
|
||||||
Portd,
|
Portd,
|
||||||
@ -861,7 +870,7 @@ declare_pins!(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_pins!(
|
declare_pins_with_irq!(
|
||||||
E,
|
E,
|
||||||
PinsE,
|
PinsE,
|
||||||
Porte,
|
Porte,
|
||||||
@ -885,7 +894,7 @@ declare_pins!(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_pins!(
|
declare_pins_with_irq!(
|
||||||
F,
|
F,
|
||||||
PinsF,
|
PinsF,
|
||||||
Portf,
|
Portf,
|
||||||
|
@ -1,388 +0,0 @@
|
|||||||
use crate::FunSel;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
dynpin::{self, DynGroup, DynPinId},
|
|
||||||
DynPinMode, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, IsMaskedError, PinState,
|
|
||||||
};
|
|
||||||
use va416xx::{ioconfig, porta, Ioconfig, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
|
||||||
|
|
||||||
/// Type definition to avoid confusion: These register blocks are identical
|
|
||||||
type PortRegisterBlock = porta::RegisterBlock;
|
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// ModeFields
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ModeFields {
|
|
||||||
dir: bool,
|
|
||||||
opendrn: bool,
|
|
||||||
pull_en: bool,
|
|
||||||
/// true for pullup, false for pulldown
|
|
||||||
pull_dir: bool,
|
|
||||||
funsel: u8,
|
|
||||||
enb_input: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DynPinMode> for ModeFields {
|
|
||||||
#[inline]
|
|
||||||
fn from(mode: DynPinMode) -> Self {
|
|
||||||
let mut fields = Self::default();
|
|
||||||
use DynPinMode::*;
|
|
||||||
match mode {
|
|
||||||
Input(config) => {
|
|
||||||
use dynpin::DynInput::*;
|
|
||||||
fields.dir = false;
|
|
||||||
fields.funsel = FunSel::Sel0 as u8;
|
|
||||||
match config {
|
|
||||||
Floating => (),
|
|
||||||
PullUp => {
|
|
||||||
fields.pull_en = true;
|
|
||||||
fields.pull_dir = true;
|
|
||||||
}
|
|
||||||
PullDown => {
|
|
||||||
fields.pull_en = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Output(config) => {
|
|
||||||
use dynpin::DynOutput::*;
|
|
||||||
fields.dir = true;
|
|
||||||
fields.funsel = FunSel::Sel0 as u8;
|
|
||||||
match config {
|
|
||||||
PushPull => (),
|
|
||||||
OpenDrain => {
|
|
||||||
fields.opendrn = true;
|
|
||||||
}
|
|
||||||
ReadableOpenDrain => {
|
|
||||||
fields.enb_input = true;
|
|
||||||
fields.opendrn = true;
|
|
||||||
}
|
|
||||||
ReadablePushPull => {
|
|
||||||
fields.enb_input = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Alternate(config) => {
|
|
||||||
fields.funsel = config as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fields
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// RegisterInterface
|
|
||||||
//==============================================================================
|
|
||||||
|
|
||||||
pub type PortReg = ioconfig::Porta;
|
|
||||||
|
|
||||||
/// Provide a safe register interface for pin objects
|
|
||||||
///
|
|
||||||
/// [`PORT`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
|
|
||||||
/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
|
|
||||||
/// interface is quite restrictive. Instead, it would be ideal if we could split
|
|
||||||
/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`].
|
|
||||||
///
|
|
||||||
/// [`PORT`] is a single, zero-sized marker `struct` that provides access to
|
|
||||||
/// every [`PORT`] register. Instead, we would like to create zero-sized marker
|
|
||||||
/// `struct`s for every pin, where each pin is only allowed to control its own
|
|
||||||
/// registers. Furthermore, each pin `struct` should be a singleton, so that
|
|
||||||
/// exclusive access to the `struct` also guarantees exclusive access to the
|
|
||||||
/// corresponding registers. Finally, the pin `struct`s should not have any
|
|
||||||
/// interior mutability. Together, these requirements would allow the pin
|
|
||||||
/// `struct`s to be both [`Send`] and [`Sync`].
|
|
||||||
///
|
|
||||||
/// This trait creates a safe API for accomplishing these goals. Implementers
|
|
||||||
/// supply a pin ID through the [`id`] function. The remaining functions provide
|
|
||||||
/// a safe API for accessing the registers associated with that pin ID. Any
|
|
||||||
/// modification of the registers requires `&mut self`, which destroys interior
|
|
||||||
/// mutability.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Users should only implement the [`id`] function. No default function
|
|
||||||
/// implementations should be overridden. The implementing type must also have
|
|
||||||
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
|
||||||
/// pin ID is a singleton.
|
|
||||||
///
|
|
||||||
/// [`id`]: Self::id
|
|
||||||
pub(super) unsafe trait RegisterInterface {
|
|
||||||
/// Provide a [`DynPinId`] identifying the set of registers controlled by
|
|
||||||
/// this type.
|
|
||||||
fn id(&self) -> DynPinId;
|
|
||||||
|
|
||||||
/// Change the pin mode
|
|
||||||
#[inline]
|
|
||||||
fn change_mode(&mut self, mode: DynPinMode) {
|
|
||||||
let ModeFields {
|
|
||||||
dir,
|
|
||||||
funsel,
|
|
||||||
opendrn,
|
|
||||||
pull_dir,
|
|
||||||
pull_en,
|
|
||||||
enb_input,
|
|
||||||
} = mode.into();
|
|
||||||
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
|
||||||
iocfg.write(|w| {
|
|
||||||
w.opendrn().bit(opendrn);
|
|
||||||
w.pen().bit(pull_en);
|
|
||||||
w.plevel().bit(pull_dir);
|
|
||||||
w.iewo().bit(enb_input);
|
|
||||||
unsafe { w.funsel().bits(funsel) }
|
|
||||||
});
|
|
||||||
let mask = self.mask_32();
|
|
||||||
unsafe {
|
|
||||||
if dir {
|
|
||||||
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
|
||||||
// Clear output
|
|
||||||
portreg.clrout().write(|w| w.bits(mask));
|
|
||||||
} else {
|
|
||||||
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn port_reg(&self) -> &PortRegisterBlock {
|
|
||||||
match self.id().group {
|
|
||||||
DynGroup::A => unsafe { &(*Porta::ptr()) },
|
|
||||||
DynGroup::B => unsafe { &(*Portb::ptr()) },
|
|
||||||
DynGroup::C => unsafe { &(*Portc::ptr()) },
|
|
||||||
DynGroup::D => unsafe { &(*Portd::ptr()) },
|
|
||||||
DynGroup::E => unsafe { &(*Porte::ptr()) },
|
|
||||||
DynGroup::F => unsafe { &(*Portf::ptr()) },
|
|
||||||
DynGroup::G => unsafe { &(*Portg::ptr()) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn iocfg_port(&self) -> &PortReg {
|
|
||||||
let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() };
|
|
||||||
match self.id().group {
|
|
||||||
DynGroup::A => ioconfig.porta(self.id().num as usize),
|
|
||||||
DynGroup::B => ioconfig.portb0(self.id().num as usize),
|
|
||||||
DynGroup::C => ioconfig.portc0(self.id().num as usize),
|
|
||||||
DynGroup::D => ioconfig.portd0(self.id().num as usize),
|
|
||||||
DynGroup::E => ioconfig.porte0(self.id().num as usize),
|
|
||||||
DynGroup::F => ioconfig.portf0(self.id().num as usize),
|
|
||||||
DynGroup::G => ioconfig.portg0(self.id().num as usize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn mask_32(&self) -> u32 {
|
|
||||||
1 << self.id().num
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn enable_irq(&self) {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_enb()
|
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Read the logic level of an output pin
|
|
||||||
fn read_pin(&self) -> bool {
|
|
||||||
let portreg = self.port_reg();
|
|
||||||
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get DATAMASK bit for this particular pin
|
|
||||||
#[inline(always)]
|
|
||||||
fn datamask(&self) -> bool {
|
|
||||||
let portreg = self.port_reg();
|
|
||||||
(portreg.datamask().read().bits() >> self.id().num) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
|
||||||
/// cleared as well
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
|
||||||
if !self.datamask() {
|
|
||||||
Err(IsMaskedError)
|
|
||||||
} else {
|
|
||||||
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the logic level of an output pin
|
|
||||||
#[inline(always)]
|
|
||||||
fn write_pin(&mut self, bit: bool) {
|
|
||||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
|
||||||
// this pin ID
|
|
||||||
unsafe {
|
|
||||||
if bit {
|
|
||||||
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
|
||||||
} else {
|
|
||||||
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
|
||||||
/// cleared as well
|
|
||||||
#[inline]
|
|
||||||
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
|
||||||
if !self.datamask() {
|
|
||||||
Err(IsMaskedError)
|
|
||||||
} else {
|
|
||||||
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
|
||||||
// this pin ID
|
|
||||||
unsafe {
|
|
||||||
if bit {
|
|
||||||
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
|
||||||
} else {
|
|
||||||
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Toggle the logic level of an output pin
|
|
||||||
#[inline(always)]
|
|
||||||
fn toggle(&mut self) {
|
|
||||||
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
|
||||||
// this pin ID
|
|
||||||
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
|
||||||
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
|
||||||
#[inline]
|
|
||||||
fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
|
||||||
unsafe {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_sen()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
match edge_type {
|
|
||||||
InterruptEdge::HighToLow => {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_evt()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
}
|
|
||||||
InterruptEdge::LowToHigh => {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_evt()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
}
|
|
||||||
InterruptEdge::BothEdges => {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_edge()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure which edge or level type triggers an interrupt
|
|
||||||
#[inline]
|
|
||||||
fn configure_level_interrupt(&mut self, level: InterruptLevel) {
|
|
||||||
unsafe {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_sen()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
if level == InterruptLevel::Low {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_evt()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
} else {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_evt()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only useful for input pins
|
|
||||||
#[inline]
|
|
||||||
fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
|
||||||
self.iocfg_port().modify(|_, w| {
|
|
||||||
// Safety: Only write to register for this Pin ID
|
|
||||||
unsafe {
|
|
||||||
w.flttype().bits(filter as u8);
|
|
||||||
w.fltclk().bits(clksel as u8)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set DATAMASK bit for this particular pin. 1 is the default
|
|
||||||
/// state of the bit and allows access of the corresponding bit
|
|
||||||
#[inline(always)]
|
|
||||||
fn set_datamask(&self) {
|
|
||||||
let portreg = self.port_reg();
|
|
||||||
unsafe {
|
|
||||||
portreg
|
|
||||||
.datamask()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear DATAMASK bit for this particular pin. This prevents access
|
|
||||||
/// of the corresponding bit for output and input operations
|
|
||||||
#[inline(always)]
|
|
||||||
fn clear_datamask(&self) {
|
|
||||||
let portreg = self.port_reg();
|
|
||||||
unsafe {
|
|
||||||
portreg
|
|
||||||
.datamask()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only useful for output pins
|
|
||||||
/// See p.52 of the programmers guide for more information.
|
|
||||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
|
||||||
/// one clock cycle before returning to the configured default state
|
|
||||||
fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
|
||||||
let portreg = self.port_reg();
|
|
||||||
unsafe {
|
|
||||||
if enable {
|
|
||||||
portreg
|
|
||||||
.pulse()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
} else {
|
|
||||||
portreg
|
|
||||||
.pulse()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
}
|
|
||||||
if default_state == PinState::Low {
|
|
||||||
portreg
|
|
||||||
.pulsebase()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
} else {
|
|
||||||
portreg
|
|
||||||
.pulsebase()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only useful for output pins
|
|
||||||
fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
|
||||||
let portreg = self.port_reg();
|
|
||||||
unsafe {
|
|
||||||
if delay_1 {
|
|
||||||
portreg
|
|
||||||
.delay1()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
} else {
|
|
||||||
portreg
|
|
||||||
.delay1()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
}
|
|
||||||
if delay_2 {
|
|
||||||
portreg
|
|
||||||
.delay2()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
} else {
|
|
||||||
portreg
|
|
||||||
.delay2()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -35,6 +35,7 @@ compile_error!(
|
|||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
use gpio::Port;
|
||||||
pub use va416xx as device;
|
pub use va416xx as device;
|
||||||
pub use va416xx as pac;
|
pub use va416xx as pac;
|
||||||
|
|
||||||
@ -71,20 +72,52 @@ pub enum FunSel {
|
|||||||
Sel3 = 0b11,
|
Sel3 = 0b11,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("invalid pin with number {0}")]
|
||||||
|
pub struct InvalidPinError(u8);
|
||||||
|
|
||||||
|
/// Can be used to manually manipulate the function select of port pins.
|
||||||
|
///
|
||||||
|
/// The function selection table can be found on p.286 of the programmers guide. Please note
|
||||||
|
/// that most of the structures and APIs in this library will automatically correctly configure
|
||||||
|
/// the pin or statically expect the correct pin type.
|
||||||
|
#[inline]
|
||||||
|
pub fn port_function_select(
|
||||||
|
ioconfig: &mut pac::Ioconfig,
|
||||||
|
port: Port,
|
||||||
|
pin: u8,
|
||||||
|
funsel: FunSel,
|
||||||
|
) -> Result<(), InvalidPinError> {
|
||||||
|
if (port == Port::G && pin >= 8) || pin >= 16 {
|
||||||
|
return Err(InvalidPinError(pin));
|
||||||
|
}
|
||||||
|
let reg_block = match port {
|
||||||
|
Port::A => ioconfig.porta(pin as usize),
|
||||||
|
Port::B => ioconfig.portb0(pin as usize),
|
||||||
|
Port::C => ioconfig.portc0(pin as usize),
|
||||||
|
Port::D => ioconfig.portd0(pin as usize),
|
||||||
|
Port::E => ioconfig.porte0(pin as usize),
|
||||||
|
Port::F => ioconfig.portf0(pin as usize),
|
||||||
|
Port::G => ioconfig.portg0(pin as usize),
|
||||||
|
};
|
||||||
|
|
||||||
|
reg_block.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Enable a specific interrupt using the NVIC peripheral.
|
/// Enable a specific interrupt using the NVIC peripheral.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This function is `unsafe` because it can break mask-based critical sections.
|
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
|
pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
|
||||||
unsafe {
|
|
||||||
cortex_m::peripheral::NVIC::unmask(irq);
|
cortex_m::peripheral::NVIC::unmask(irq);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable a specific interrupt using the NVIC peripheral.
|
/// Disable a specific interrupt using the NVIC peripheral.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable_interrupt(irq: pac::Interrupt) {
|
pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
|
||||||
cortex_m::peripheral::NVIC::mask(irq);
|
cortex_m::peripheral::NVIC::mask(irq);
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,7 @@ use crate::gpio::{
|
|||||||
|
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::typelevel::Sealed;
|
use crate::typelevel::Sealed;
|
||||||
use crate::{disable_interrupt, prelude::*};
|
use crate::{disable_nvic_interrupt, enable_nvic_interrupt, pac, prelude::*};
|
||||||
use crate::{enable_interrupt, pac};
|
|
||||||
|
|
||||||
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
@ -406,7 +405,7 @@ pub type TimRegBlock = pac::tim0::RegisterBlock;
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Users should only implement the [`tim_id`] function. No default function
|
/// Users should only implement the [Self::tim_id] function. No default function
|
||||||
/// implementations should be overridden. The implementing type must also have
|
/// implementations should be overridden. The implementing type must also have
|
||||||
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
/// pin ID is a singleton.
|
/// pin ID is a singleton.
|
||||||
@ -590,7 +589,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn listen(&mut self) {
|
pub fn listen(&mut self) {
|
||||||
self.listening = true;
|
self.listening = true;
|
||||||
self.enable_interrupt();
|
self.enable_interrupt();
|
||||||
unsafe { enable_interrupt(Tim::IRQ) }
|
unsafe { enable_nvic_interrupt(Tim::IRQ) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
||||||
@ -618,7 +617,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn unlisten(&mut self) {
|
pub fn unlisten(&mut self) {
|
||||||
self.listening = true;
|
self.listening = true;
|
||||||
self.disable_interrupt();
|
self.disable_interrupt();
|
||||||
disable_interrupt(Tim::IRQ);
|
disable_nvic_interrupt(Tim::IRQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! # API for the UART peripheral
|
//! # API for the UART peripheral
|
||||||
//!
|
//!
|
||||||
//! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures.
|
//! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures.
|
||||||
//! The RX structure also has a dedicated [RxWithIrq] variant which allows reading the receiver
|
//! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver
|
||||||
//! using interrupts.
|
//! using interrupts.
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
@ -18,7 +18,7 @@ use fugit::RateExtU32;
|
|||||||
use crate::clock::{Clocks, PeripheralSelect, SyscfgExt};
|
use crate::clock::{Clocks, PeripheralSelect, SyscfgExt};
|
||||||
use crate::gpio::PF13;
|
use crate::gpio::PF13;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{disable_interrupt, enable_interrupt};
|
use crate::{disable_nvic_interrupt, enable_nvic_interrupt};
|
||||||
use crate::{
|
use crate::{
|
||||||
gpio::{
|
gpio::{
|
||||||
AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2,
|
AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2,
|
||||||
@ -580,7 +580,7 @@ impl<Uart: Instance> UartBase<Uart> {
|
|||||||
w.rxenable().clear_bit();
|
w.rxenable().clear_bit();
|
||||||
w.txenable().clear_bit()
|
w.txenable().clear_bit()
|
||||||
});
|
});
|
||||||
disable_interrupt(Uart::IRQ_RX);
|
disable_nvic_interrupt(Uart::IRQ_RX);
|
||||||
self.uart
|
self.uart
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -975,7 +975,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
|||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
self.0.enable();
|
self.0.enable();
|
||||||
self.enable_rx_irq_sources(true);
|
self.enable_rx_irq_sources(true);
|
||||||
unsafe { enable_interrupt(Uart::IRQ_RX) };
|
unsafe { enable_nvic_interrupt(Uart::IRQ_RX) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
pac,
|
pac,
|
||||||
prelude::SyscfgExt,
|
prelude::SyscfgExt,
|
||||||
};
|
};
|
||||||
use crate::{disable_interrupt, enable_interrupt};
|
use crate::{disable_nvic_interrupt, enable_nvic_interrupt};
|
||||||
|
|
||||||
pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551;
|
pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551;
|
||||||
|
|
||||||
@ -30,12 +30,12 @@ pub type WdtController = Wdt;
|
|||||||
/// This function is `unsafe` because it can break mask-based critical sections.
|
/// This function is `unsafe` because it can break mask-based critical sections.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn enable_wdt_interrupts() {
|
pub unsafe fn enable_wdt_interrupts() {
|
||||||
enable_interrupt(pac::Interrupt::WATCHDOG)
|
enable_nvic_interrupt(pac::Interrupt::WATCHDOG)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable_wdt_interrupts() {
|
pub fn disable_wdt_interrupts() {
|
||||||
disable_interrupt(pac::Interrupt::WATCHDOG)
|
disable_nvic_interrupt(pac::Interrupt::WATCHDOG)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wdt {
|
impl Wdt {
|
||||||
|
@ -16,6 +16,7 @@ cortex-m-rt = "0.7"
|
|||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
|
|
||||||
[dependencies.va416xx-hal]
|
[dependencies.va416xx-hal]
|
||||||
|
path = "../va416xx-hal"
|
||||||
features = ["va41630"]
|
features = ["va41630"]
|
||||||
version = ">=0.3, <0.4"
|
version = ">=0.3, <0.4"
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ pub mod accelerometer {
|
|||||||
use lis2dh12::{self, detect_i2c_addr, AddrDetectionError, Lis2dh12};
|
use lis2dh12::{self, detect_i2c_addr, AddrDetectionError, Lis2dh12};
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
clock::Clocks,
|
clock::Clocks,
|
||||||
i2c::{self, ClockTooSlowForFastI2c, I2cMaster, I2cSpeed, MasterConfig},
|
i2c::{self, ClockTooSlowForFastI2cError, I2cMaster, I2cSpeed, MasterConfig},
|
||||||
pac,
|
pac,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ pub mod accelerometer {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ConstructorError {
|
pub enum ConstructorError {
|
||||||
ClkError(ClockTooSlowForFastI2c),
|
ClkError(ClockTooSlowForFastI2cError),
|
||||||
AddrDetectionError(AddrDetectionError<i2c::Error>),
|
AddrDetectionError(AddrDetectionError<i2c::Error>),
|
||||||
AccelerometerError(lis2dh12::Error<i2c::Error>),
|
AccelerometerError(lis2dh12::Error<i2c::Error>),
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user