Compare commits
1 Commits
va108xx-v0
...
063b307701
Author | SHA1 | Date | |
---|---|---|---|
063b307701 |
@ -6,7 +6,10 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::{
|
||||||
|
delay::DelayNs,
|
||||||
|
digital::{InputPin, OutputPin, 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 va108xx_hal::{
|
use va108xx_hal::{
|
||||||
@ -64,35 +67,35 @@ fn main() -> ! {
|
|||||||
TestCase::TestBasic => {
|
TestCase::TestBasic => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
let input = pinsa.pa1.into_floating_input();
|
let mut input = pinsa.pa1.into_floating_input();
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestPullup => {
|
TestCase::TestPullup => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = pinsa.pa1.into_pull_up_input();
|
let mut input = pinsa.pa1.into_pull_up_input();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
out.into_floating_input();
|
out.into_floating_input();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestPulldown => {
|
TestCase::TestPulldown => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = pinsa.pa1.into_pull_down_input();
|
let mut input = pinsa.pa1.into_pull_down_input();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
let mut out = pinsa.pa0.into_push_pull_output();
|
let mut out = pinsa.pa0.into_push_pull_output();
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
out.into_floating_input();
|
out.into_floating_input();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestMask => {
|
TestCase::TestMask => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
@ -107,11 +110,11 @@ fn main() -> ! {
|
|||||||
TestCase::PortB => {
|
TestCase::PortB => {
|
||||||
// Tie PORTB[22] to PORTB[23] for these tests!
|
// Tie PORTB[22] to PORTB[23] for these tests!
|
||||||
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
||||||
let input = pinsb.pb23.into_floating_input();
|
let mut input = pinsb.pb23.into_floating_input();
|
||||||
out.set_high();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_high());
|
assert!(input.is_high().unwrap());
|
||||||
out.set_low();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low());
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::Perid => {
|
TestCase::Perid => {
|
||||||
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
||||||
@ -121,15 +124,15 @@ fn main() -> ! {
|
|||||||
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
|
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
|
||||||
output_pulsed.configure_pulse_mode(true, PinState::Low);
|
output_pulsed.configure_pulse_mode(true, PinState::Low);
|
||||||
rprintln!("Pulsing high 10 times..");
|
rprintln!("Pulsing high 10 times..");
|
||||||
output_pulsed.set_low();
|
output_pulsed.set_low().unwrap();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_high();
|
output_pulsed.set_high().unwrap();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
output_pulsed.configure_pulse_mode(true, PinState::High);
|
output_pulsed.configure_pulse_mode(true, PinState::High);
|
||||||
rprintln!("Pulsing low 10 times..");
|
rprintln!("Pulsing low 10 times..");
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_low();
|
output_pulsed.set_low().unwrap();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,9 +144,9 @@ fn main() -> ! {
|
|||||||
let mut out_2 = pinsa.pa3.into_readable_push_pull_output();
|
let mut out_2 = pinsa.pa3.into_readable_push_pull_output();
|
||||||
out_2.configure_delay(true, true);
|
out_2.configure_delay(true, true);
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
out_0.toggle();
|
out_0.toggle().unwrap();
|
||||||
out_1.toggle();
|
out_1.toggle().unwrap();
|
||||||
out_2.toggle();
|
out_2.toggle().unwrap();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,18 +159,18 @@ fn main() -> ! {
|
|||||||
dp.tim0,
|
dp.tim0,
|
||||||
);
|
);
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
ms_timer.delay_ms(500);
|
ms_timer.delay_ms(500);
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
ms_timer.delay_ms(500);
|
ms_timer.delay_ms(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||||
let mut pa0 = pinsa.pa0.into_readable_push_pull_output();
|
let mut pa0 = pinsa.pa0.into_readable_push_pull_output();
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
delay_timer.delay_ms(500);
|
delay_timer.delay_ms(500);
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
delay_timer.delay_ms(500);
|
delay_timer.delay_ms(500);
|
||||||
}
|
}
|
||||||
let ahb_freq: Hertz = 50.MHz();
|
let ahb_freq: Hertz = 50.MHz();
|
||||||
@ -175,13 +178,13 @@ fn main() -> ! {
|
|||||||
// Test usecond delay using both TIM peripheral and SYST. Use the release image if you
|
// Test usecond delay using both TIM peripheral and SYST. Use the release image if you
|
||||||
// want to verify the timings!
|
// want to verify the timings!
|
||||||
loop {
|
loop {
|
||||||
pa0.toggle();
|
pa0.toggle().ok();
|
||||||
delay_timer.delay_us(50);
|
delay_timer.delay_us(50);
|
||||||
pa0.toggle();
|
pa0.toggle().ok();
|
||||||
delay_timer.delay_us(50);
|
delay_timer.delay_us(50);
|
||||||
pa0.toggle();
|
pa0.toggle_with_toggle_reg();
|
||||||
syst_delay.delay_us(50);
|
syst_delay.delay_us(50);
|
||||||
pa0.toggle();
|
pa0.toggle_with_toggle_reg();
|
||||||
syst_delay.delay_us(50);
|
syst_delay.delay_us(50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +192,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
rprintln!("Test success");
|
rprintln!("Test success");
|
||||||
loop {
|
loop {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,14 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_sync::channel::{Receiver, Sender};
|
use embassy_sync::channel::{Receiver, Sender};
|
||||||
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
|
||||||
use embedded_hal_async::digital::Wait;
|
use embedded_hal_async::digital::Wait;
|
||||||
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 va108xx_embassy::embassy;
|
use va108xx_embassy::embassy;
|
||||||
use va108xx_hal::gpio::{
|
use va108xx_hal::gpio::{
|
||||||
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port,
|
on_interrupt_for_async_gpio_port_a, on_interrupt_for_async_gpio_port_b, InputDynPinAsync,
|
||||||
|
InputPinAsync, PinsB,
|
||||||
};
|
};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::{DynPin, PinsA},
|
gpio::{DynPin, PinsA},
|
||||||
@ -115,7 +117,7 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
rprintln!("Example done, toggling LED0");
|
rprintln!("Example done, toggling LED0");
|
||||||
loop {
|
loop {
|
||||||
led0.toggle();
|
led0.toggle().unwrap();
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,13 +250,13 @@ async fn output_task(
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn OC10() {
|
fn OC10() {
|
||||||
on_interrupt_for_async_gpio_for_port(Port::A);
|
on_interrupt_for_async_gpio_port_a();
|
||||||
on_interrupt_for_async_gpio_for_port(Port::B);
|
on_interrupt_for_async_gpio_port_b();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This interrupt only handles PORT B interrupts.
|
// This interrupt only handles PORT B interrupts.
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn OC11() {
|
fn OC11() {
|
||||||
on_interrupt_for_async_gpio_for_port(Port::B);
|
on_interrupt_for_async_gpio_port_b();
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ use core::cell::RefCell;
|
|||||||
use critical_section::Mutex;
|
use critical_section::Mutex;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_time::Instant;
|
use embassy_time::Instant;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
use embedded_io::Write;
|
use embedded_io::Write;
|
||||||
use embedded_io_async::Read;
|
use embedded_io_async::Read;
|
||||||
use heapless::spsc::{Consumer, Producer, Queue};
|
use heapless::spsc::{Consumer, Producer, Queue};
|
||||||
@ -29,9 +30,9 @@ use va108xx_hal::{
|
|||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
uart::{
|
uart::{
|
||||||
self, on_interrupt_rx_overwriting,
|
self, on_interrupt_uart_b_overwriting,
|
||||||
rx_asynch::{on_interrupt_rx, RxAsync},
|
rx_asynch::{on_interrupt_uart_a, RxAsync},
|
||||||
Bank, RxAsyncOverwriting, Tx,
|
RxAsyncSharedConsumer, Tx,
|
||||||
},
|
},
|
||||||
InterruptConfig,
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
@ -105,16 +106,16 @@ async fn main(spawner: Spawner) {
|
|||||||
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
|
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
|
||||||
});
|
});
|
||||||
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
||||||
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
|
let async_rx_uart_b = RxAsyncSharedConsumer::new(rx_uart_b, &CONSUMER_UART_B);
|
||||||
spawner
|
spawner
|
||||||
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
|
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut buf = [0u8; 256];
|
let mut buf = [0u8; 256];
|
||||||
loop {
|
loop {
|
||||||
rprintln!("Current time UART A: {}", Instant::now().as_secs());
|
rprintln!("Current time UART A: {}", Instant::now().as_secs());
|
||||||
led0.toggle();
|
led0.toggle().ok();
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
led2.toggle();
|
led2.toggle().ok();
|
||||||
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
|
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
|
||||||
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
||||||
rprintln!(
|
rprintln!(
|
||||||
@ -127,7 +128,7 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
|
async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
|
||||||
let mut buf = [0u8; 256];
|
let mut buf = [0u8; 256];
|
||||||
loop {
|
loop {
|
||||||
rprintln!("Current time UART B: {}", Instant::now().as_secs());
|
rprintln!("Current time UART B: {}", Instant::now().as_secs());
|
||||||
@ -148,7 +149,7 @@ async fn uart_b_task(mut async_rx: RxAsyncOverwriting<pac::Uartb, 256>, mut tx:
|
|||||||
fn OC2() {
|
fn OC2() {
|
||||||
let mut prod =
|
let mut prod =
|
||||||
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
|
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
|
||||||
let errors = on_interrupt_rx(Bank::A, &mut prod);
|
let errors = on_interrupt_uart_a(&mut prod);
|
||||||
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
|
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
|
||||||
// In a production app, we could use a channel to send the errors to the main task.
|
// In a production app, we could use a channel to send the errors to the main task.
|
||||||
if let Err(errors) = errors {
|
if let Err(errors) = errors {
|
||||||
@ -161,7 +162,7 @@ fn OC2() {
|
|||||||
fn OC3() {
|
fn OC3() {
|
||||||
let mut prod =
|
let mut prod =
|
||||||
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
|
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
|
||||||
let errors = on_interrupt_rx_overwriting(Bank::B, &mut prod, &CONSUMER_UART_B);
|
let errors = on_interrupt_uart_b_overwriting(&mut prod, &CONSUMER_UART_B);
|
||||||
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
|
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
|
||||||
// In a production app, we could use a channel to send the errors to the main task.
|
// In a production app, we could use a channel to send the errors to the main task.
|
||||||
if let Err(errors) = errors {
|
if let Err(errors) = errors {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
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 embedded_io_async::Write;
|
use embedded_io_async::Write;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
@ -20,7 +21,7 @@ use va108xx_hal::{
|
|||||||
gpio::PinsA,
|
gpio::PinsA,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
uart::{self, on_interrupt_tx, Bank, TxAsync},
|
uart::{self, on_interrupt_uart_a_tx, TxAsync},
|
||||||
InterruptConfig,
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,9 +75,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
loop {
|
loop {
|
||||||
rprintln!("Current time: {}", Instant::now().as_secs());
|
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||||
led0.toggle();
|
led0.toggle().ok();
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
led2.toggle();
|
led2.toggle().ok();
|
||||||
let _written = async_tx
|
let _written = async_tx
|
||||||
.write(STR_LIST[idx].as_bytes())
|
.write(STR_LIST[idx].as_bytes())
|
||||||
.await
|
.await
|
||||||
@ -92,5 +93,5 @@ async fn main(_spawner: Spawner) {
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn OC2() {
|
fn OC2() {
|
||||||
on_interrupt_tx(Bank::A);
|
on_interrupt_uart_a_tx();
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
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 va108xx_embassy::embassy;
|
use va108xx_embassy::embassy;
|
||||||
@ -59,8 +60,8 @@ 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());
|
||||||
led0.toggle();
|
led0.toggle().ok();
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
led2.toggle();
|
led2.toggle().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,5 +22,5 @@ rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
|||||||
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
|
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
|
||||||
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||||
|
|
||||||
va108xx-hal = { version = "0.9", path = "../../va108xx-hal" }
|
va108xx-hal = "0.9"
|
||||||
vorago-reb1 = { version = "0.7", path = "../../vorago-reb1" }
|
vorago-reb1 = "0.7"
|
||||||
|
@ -69,16 +69,18 @@ mod app {
|
|||||||
|
|
||||||
// Configure an edge interrupt on the button and route it to interrupt vector 15
|
// Configure an edge interrupt on the button and route it to interrupt vector 15
|
||||||
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
||||||
|
button.configure_edge_interrupt(
|
||||||
|
edge_irq,
|
||||||
|
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
);
|
||||||
|
|
||||||
if mode == PressMode::Toggle {
|
if mode == PressMode::Toggle {
|
||||||
// This filter debounces the switch for edge based interrupts
|
// This filter debounces the switch for edge based interrupts
|
||||||
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
||||||
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
||||||
}
|
}
|
||||||
button.configure_and_enable_edge_interrupt(
|
|
||||||
edge_irq,
|
|
||||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
|
||||||
);
|
|
||||||
let mut leds = Leds::new(
|
let mut leds = Leds::new(
|
||||||
pinsa.pa10.into_push_pull_output(),
|
pinsa.pa10.into_push_pull_output(),
|
||||||
pinsa.pa7.into_push_pull_output(),
|
pinsa.pa7.into_push_pull_output(),
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
||||||
mod app {
|
mod app {
|
||||||
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_example::SYSCLK_FREQ;
|
use rtic_example::SYSCLK_FREQ;
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
@ -57,9 +58,9 @@ mod app {
|
|||||||
async fn blinky(cx: blinky::Context) {
|
async fn blinky(cx: blinky::Context) {
|
||||||
loop {
|
loop {
|
||||||
rprintln!("toggling LEDs");
|
rprintln!("toggling LEDs");
|
||||||
cx.local.led0.toggle();
|
cx.local.led0.toggle().ok();
|
||||||
cx.local.led1.toggle();
|
cx.local.led1.toggle().ok();
|
||||||
cx.local.led2.toggle();
|
cx.local.led2.toggle().ok();
|
||||||
Mono::delay(1000.millis()).await;
|
Mono::delay(1000.millis()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,8 @@ embedded-io = "0.6"
|
|||||||
cortex-m-semihosting = "0.5.0"
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
path = "../../va108xx-hal"
|
|
||||||
version = "0.9"
|
version = "0.9"
|
||||||
features = ["rt", "defmt"]
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
[dependencies.vorago-reb1]
|
[dependencies.vorago-reb1]
|
||||||
path = "../../vorago-reb1"
|
|
||||||
version = "0.7"
|
version = "0.7"
|
||||||
|
@ -7,7 +7,10 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::{
|
||||||
|
delay::DelayNs,
|
||||||
|
digital::{OutputPin, StatefulOutputPin},
|
||||||
|
};
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::PinsA,
|
gpio::PinsA,
|
||||||
@ -35,21 +38,21 @@ fn main() -> ! {
|
|||||||
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
||||||
let mut led3 = porta.pa6.into_readable_push_pull_output();
|
let mut led3 = porta.pa6.into_readable_push_pull_output();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
led1.set_low();
|
led1.set_low().ok();
|
||||||
led2.set_low();
|
led2.set_low().ok();
|
||||||
led3.set_low();
|
led3.set_low().ok();
|
||||||
delay_ms.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
led1.set_high();
|
led1.set_high().ok();
|
||||||
led2.set_high();
|
led2.set_high().ok();
|
||||||
led3.set_high();
|
led3.set_high().ok();
|
||||||
delay_tim1.delay_ms(200);
|
delay_tim1.delay_ms(200);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
delay_ms.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
led2.toggle();
|
led2.toggle().ok();
|
||||||
delay_tim1.delay_ms(200);
|
delay_tim1.delay_ms(200);
|
||||||
led3.toggle();
|
led3.toggle().ok();
|
||||||
delay_ms.delay_ms(200);
|
delay_ms.delay_ms(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
- Larger refactoring of GPIO library. The edge and level interrupt configurator functions do not
|
- Missing GPIO API replacements from `x` to `configure_x`
|
||||||
enable interrupts anymore. Instead, there are explicit `enbable_interrupt` and
|
- Split up generic GPIO interrupt handler into `on_interrupt_for_asynch_gpio`
|
||||||
`disable_interrupt` methods
|
into port specific variants.
|
||||||
- Renamed GPIO `DynGroup` to `Port`
|
|
||||||
- Rename generic GPIO interrupt handler into `on_interrupt_for_asynch_gpio`
|
|
||||||
into `on_interrupt_for_async_gpio_for_port` which expects a Port argument
|
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
|
@ -3,9 +3,14 @@
|
|||||||
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
||||||
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
|
//! 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
|
//! 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
|
//! which must be provided for async support to work. However, it provides two generic interrupt
|
||||||
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
|
//! handlers:
|
||||||
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
|
//!
|
||||||
|
//! - [on_interrupt_for_async_gpio_port_a]
|
||||||
|
//! - [on_interrupt_for_async_gpio_port_b]
|
||||||
|
//!
|
||||||
|
//! Those should be called in the interrupt handlers which handle GPIO interrupts for port A
|
||||||
|
//! and/or port B.
|
||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
@ -13,14 +18,15 @@
|
|||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embedded_hal::digital::InputPin;
|
||||||
use embedded_hal_async::digital::Wait;
|
use embedded_hal_async::digital::Wait;
|
||||||
use portable_atomic::AtomicBool;
|
use portable_atomic::AtomicBool;
|
||||||
use va108xx::{self as pac};
|
use va108xx::{self as pac, Irqsel, Sysconfig};
|
||||||
|
|
||||||
use crate::InterruptConfig;
|
use crate::InterruptConfig;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
|
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
|
||||||
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
|
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,36 +39,51 @@ static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
|
|||||||
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
|
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
|
||||||
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
|
||||||
|
|
||||||
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
|
/// Generic interrupt handler for GPIO interrupts on PORT A to support the async functionalities.
|
||||||
///
|
///
|
||||||
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
|
/// This function should be called in all interrupt hanflers which handle any PORT A GPIO
|
||||||
/// matching the [Port] argument.
|
/// interrupts.
|
||||||
/// 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
|
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
|
||||||
/// complete async operations.
|
/// as well as updating the static edge detection structures. This allows the pin future to
|
||||||
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
|
/// complete async operations. The user should call this function in ALL interrupt handlers
|
||||||
|
/// which handle any GPIO interrupts.
|
||||||
|
#[inline]
|
||||||
|
pub fn on_interrupt_for_async_gpio_port_a() {
|
||||||
let periphs = unsafe { pac::Peripherals::steal() };
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
|
||||||
let (irq_enb, edge_status, wakers, edge_detection) = match port {
|
handle_interrupt_for_gpio_and_port(
|
||||||
Port::A => (
|
periphs.porta.irq_enb().read().bits(),
|
||||||
periphs.porta.irq_enb().read().bits(),
|
periphs.porta.edge_status().read().bits(),
|
||||||
periphs.porta.edge_status().read().bits(),
|
&WAKERS_FOR_PORT_A,
|
||||||
WAKERS_FOR_PORT_A.as_ref(),
|
&EDGE_DETECTION_PORT_A,
|
||||||
EDGE_DETECTION_PORT_A.as_ref(),
|
);
|
||||||
),
|
|
||||||
Port::B => (
|
|
||||||
periphs.portb.irq_enb().read().bits(),
|
|
||||||
periphs.portb.edge_status().read().bits(),
|
|
||||||
WAKERS_FOR_PORT_B.as_ref(),
|
|
||||||
EDGE_DETECTION_PORT_B.as_ref(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generic interrupt handler for GPIO interrupts on PORT B to support the async functionalities.
|
||||||
|
///
|
||||||
|
/// This function should be called in all interrupt hanflers which handle any PORT B GPIO
|
||||||
|
/// interrupts.
|
||||||
|
///
|
||||||
|
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
|
||||||
|
/// as well as updating the static edge detection structures. This allows the pin future to
|
||||||
|
/// complete async operations. The user should call this function in ALL interrupt handlers
|
||||||
|
/// which handle any GPIO interrupts.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn on_interrupt_for_port(
|
pub fn on_interrupt_for_async_gpio_port_b() {
|
||||||
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
|
||||||
|
handle_interrupt_for_gpio_and_port(
|
||||||
|
periphs.portb.irq_enb().read().bits(),
|
||||||
|
periphs.portb.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_B,
|
||||||
|
&EDGE_DETECTION_PORT_B,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses the enabled interrupt register and the persistent edge status to capture all GPIO events.
|
||||||
|
#[inline]
|
||||||
|
fn handle_interrupt_for_gpio_and_port(
|
||||||
mut irq_enb: u32,
|
mut irq_enb: u32,
|
||||||
edge_status: u32,
|
edge_status: u32,
|
||||||
wakers: &'static [AtomicWaker],
|
wakers: &'static [AtomicWaker],
|
||||||
@ -95,13 +116,26 @@ pub struct InputPinFuture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InputPinFuture {
|
impl InputPinFuture {
|
||||||
#[inline]
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This calls [Self::new_with_dyn_pin] but uses [pac::Peripherals::steal] to get the system configuration
|
||||||
|
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
|
||||||
|
/// related to this input pin are not being used elsewhere concurrently.
|
||||||
|
pub unsafe fn new_unchecked_with_dyn_pin(
|
||||||
|
pin: &mut DynPin,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
let mut periphs = pac::Peripherals::steal();
|
||||||
|
Self::new_with_dyn_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pin_group_to_waker_and_edge_detection_group(
|
pub fn pin_group_to_waker_and_edge_detection_group(
|
||||||
group: Port,
|
group: DynGroup,
|
||||||
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
||||||
match group {
|
match group {
|
||||||
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
|
DynGroup::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()),
|
DynGroup::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,17 +143,24 @@ impl InputPinFuture {
|
|||||||
pin: &mut DynPin,
|
pin: &mut DynPin,
|
||||||
irq: pac::Interrupt,
|
irq: pac::Interrupt,
|
||||||
edge: InterruptEdge,
|
edge: InterruptEdge,
|
||||||
|
sys_cfg: &mut Sysconfig,
|
||||||
|
irq_sel: &mut Irqsel,
|
||||||
) -> Result<Self, InvalidPinTypeError> {
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
if !pin.is_input_pin() {
|
if !pin.is_input_pin() {
|
||||||
return Err(InvalidPinTypeError(pin.mode()));
|
return Err(InvalidPinTypeError(pin.mode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (waker_group, edge_detection_group) =
|
let (waker_group, edge_detection_group) =
|
||||||
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
Self::pin_group_to_waker_and_edge_detection_group(pin.id().group);
|
||||||
edge_detection_group[pin.id().num() as usize]
|
edge_detection_group[pin.id().num as usize]
|
||||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
pin.configure_edge_interrupt(edge).unwrap();
|
pin.configure_edge_interrupt(
|
||||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
edge,
|
||||||
|
InterruptConfig::new(irq, true, true),
|
||||||
|
Some(sys_cfg),
|
||||||
|
Some(irq_sel),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
pin_id: pin.id(),
|
pin_id: pin.id(),
|
||||||
waker_group,
|
waker_group,
|
||||||
@ -127,17 +168,37 @@ impl InputPinFuture {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This calls [Self::new_with_pin] but uses [pac::Peripherals::steal] to get the system configuration
|
||||||
|
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
|
||||||
|
/// related to this input pin are not being used elsewhere concurrently.
|
||||||
|
pub unsafe fn new_unchecked_with_pin<I: PinId, C: InputConfig>(
|
||||||
pin: &mut Pin<I, pin::Input<C>>,
|
pin: &mut Pin<I, pin::Input<C>>,
|
||||||
irq: pac::Interrupt,
|
irq: pac::Interrupt,
|
||||||
edge: InterruptEdge,
|
edge: InterruptEdge,
|
||||||
|
) -> Self {
|
||||||
|
let mut periphs = pac::Peripherals::steal();
|
||||||
|
Self::new_with_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
||||||
|
pin: &mut Pin<I, pin::Input<C>>,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
sys_cfg: &mut Sysconfig,
|
||||||
|
irq_sel: &mut Irqsel,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (waker_group, edge_detection_group) =
|
let (waker_group, edge_detection_group) =
|
||||||
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
Self::pin_group_to_waker_and_edge_detection_group(pin.id().group);
|
||||||
edge_detection_group[pin.id().num() as usize]
|
edge_detection_group[pin.id().num as usize]
|
||||||
.store(false, core::sync::atomic::Ordering::Relaxed);
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
pin.configure_edge_interrupt(edge);
|
pin.configure_edge_interrupt(
|
||||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
edge,
|
||||||
|
InterruptConfig::new(irq, true, true),
|
||||||
|
Some(sys_cfg),
|
||||||
|
Some(irq_sel),
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
pin_id: pin.id(),
|
pin_id: pin.id(),
|
||||||
edge_detection_group,
|
edge_detection_group,
|
||||||
@ -148,8 +209,18 @@ impl InputPinFuture {
|
|||||||
|
|
||||||
impl Drop for InputPinFuture {
|
impl Drop for InputPinFuture {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// The API ensures that we actually own the pin, so stealing it here is okay.
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false);
|
if self.pin_id.group == DynGroup::A {
|
||||||
|
periphs
|
||||||
|
.porta
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
|
||||||
|
} else {
|
||||||
|
periphs
|
||||||
|
.porta
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +230,7 @@ impl Future for InputPinFuture {
|
|||||||
self: core::pin::Pin<&mut Self>,
|
self: core::pin::Pin<&mut Self>,
|
||||||
cx: &mut core::task::Context<'_>,
|
cx: &mut core::task::Context<'_>,
|
||||||
) -> core::task::Poll<Self::Output> {
|
) -> core::task::Poll<Self::Output> {
|
||||||
let idx = self.pin_id.num() as usize;
|
let idx = self.pin_id.num as usize;
|
||||||
self.waker_group[idx].register(cx.waker());
|
self.waker_group[idx].register(cx.waker());
|
||||||
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||||
return core::task::Poll::Ready(());
|
return core::task::Poll::Ready(());
|
||||||
@ -178,8 +249,8 @@ impl InputDynPinAsync {
|
|||||||
/// passed as well and is used to route and enable the interrupt.
|
/// 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
|
/// 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
|
/// generic [on_interrupt_for_async_gpio_port_a] or [on_interrupt_for_async_gpio_port_b]
|
||||||
/// for the asynchronous functionality to work.
|
/// function must be called inside that function for the asynchronous functionality to work.
|
||||||
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
|
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
|
||||||
if !pin.is_input_pin() {
|
if !pin.is_input_pin() {
|
||||||
return Err(InvalidPinTypeError(pin.mode()));
|
return Err(InvalidPinTypeError(pin.mode()));
|
||||||
@ -191,10 +262,15 @@ impl InputDynPinAsync {
|
|||||||
///
|
///
|
||||||
/// This returns immediately if the pin is already high.
|
/// This returns immediately if the pin is already high.
|
||||||
pub async fn wait_for_high(&mut self) {
|
pub async fn wait_for_high(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
let fut = unsafe {
|
||||||
let fut =
|
// Unwrap okay, checked pin in constructor.
|
||||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
.unwrap();
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::LowToHigh,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
if self.pin.is_high().unwrap() {
|
if self.pin.is_high().unwrap() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -205,10 +281,15 @@ impl InputDynPinAsync {
|
|||||||
///
|
///
|
||||||
/// This returns immediately if the pin is already high.
|
/// This returns immediately if the pin is already high.
|
||||||
pub async fn wait_for_low(&mut self) {
|
pub async fn wait_for_low(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
let fut = unsafe {
|
||||||
let fut =
|
// Unwrap okay, checked pin in constructor.
|
||||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
.unwrap();
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
if self.pin.is_low().unwrap() {
|
if self.pin.is_low().unwrap() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -217,26 +298,44 @@ impl InputDynPinAsync {
|
|||||||
|
|
||||||
/// Asynchronously wait until the pin sees a falling edge.
|
/// Asynchronously wait until the pin sees a falling edge.
|
||||||
pub async fn wait_for_falling_edge(&mut self) {
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
unsafe {
|
||||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.await;
|
}
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees a rising edge.
|
/// Asynchronously wait until the pin sees a rising edge.
|
||||||
pub async fn wait_for_rising_edge(&mut self) {
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
unsafe {
|
||||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::LowToHigh,
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.await;
|
}
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
pub async fn wait_for_any_edge(&mut self) {
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
unsafe {
|
||||||
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::BothEdges,
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.await;
|
}
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release(self) -> DynPin {
|
pub fn release(self) -> DynPin {
|
||||||
@ -285,8 +384,8 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
|||||||
/// passed as well and is used to route and enable the interrupt.
|
/// 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
|
/// 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
|
/// generic [on_interrupt_for_async_gpio_port_a] or [on_interrupt_for_async_gpio_port_b]
|
||||||
/// for the asynchronous functionality to work.
|
/// function must be called inside that function for the asynchronous functionality to work.
|
||||||
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
||||||
Self { pin, irq }
|
Self { pin, irq }
|
||||||
}
|
}
|
||||||
@ -295,8 +394,14 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
|||||||
///
|
///
|
||||||
/// This returns immediately if the pin is already high.
|
/// This returns immediately if the pin is already high.
|
||||||
pub async fn wait_for_high(&mut self) {
|
pub async fn wait_for_high(&mut self) {
|
||||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
let fut = unsafe {
|
||||||
if self.pin.is_high() {
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::LowToHigh,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if self.pin.is_high().unwrap() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fut.await;
|
fut.await;
|
||||||
@ -306,8 +411,14 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
|||||||
///
|
///
|
||||||
/// This returns immediately if the pin is already high.
|
/// This returns immediately if the pin is already high.
|
||||||
pub async fn wait_for_low(&mut self) {
|
pub async fn wait_for_low(&mut self) {
|
||||||
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
let fut = unsafe {
|
||||||
if self.pin.is_low() {
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if self.pin.is_low().unwrap() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fut.await;
|
fut.await;
|
||||||
@ -315,19 +426,40 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
|||||||
|
|
||||||
/// Asynchronously wait until the pin sees falling edge.
|
/// Asynchronously wait until the pin sees falling edge.
|
||||||
pub async fn wait_for_falling_edge(&mut self) {
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
unsafe {
|
||||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees rising edge.
|
/// Asynchronously wait until the pin sees rising edge.
|
||||||
pub async fn wait_for_rising_edge(&mut self) {
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
unsafe {
|
||||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::LowToHigh,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
pub async fn wait_for_any_edge(&mut self) {
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
unsafe {
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::BothEdges,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
||||||
|
@ -57,10 +57,11 @@
|
|||||||
//! [InvalidPinTypeError].
|
//! [InvalidPinTypeError].
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
pin::{FilterType, Pin, PinId, PinMode},
|
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
||||||
InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port,
|
reg::RegisterInterface,
|
||||||
|
InputDynPinAsync,
|
||||||
};
|
};
|
||||||
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel};
|
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// DynPinMode configurations
|
// DynPinMode configurations
|
||||||
@ -155,91 +156,55 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
|
|||||||
// DynGroup & DynPinId
|
// DynGroup & DynPinId
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
pub type DynGroup = Port;
|
/// Value-level `enum` for pin groups
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum DynGroup {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
port: Port,
|
pub group: DynGroup,
|
||||||
num: u8,
|
pub num: u8,
|
||||||
}
|
|
||||||
|
|
||||||
impl DynPinId {
|
|
||||||
pub const fn new(port: Port, num: u8) -> Self {
|
|
||||||
DynPinId { port, num }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn port(&self) -> Port {
|
|
||||||
self.port
|
|
||||||
}
|
|
||||||
pub const fn num(&self) -> u8 {
|
|
||||||
self.num
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// ModeFields
|
// DynRegisters
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
/// Provide a safe register interface for [`DynPin`]s
|
||||||
#[derive(Default)]
|
///
|
||||||
struct ModeFields {
|
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||||
dir: bool,
|
/// access the corresponding regsiters.
|
||||||
opendrn: bool,
|
#[derive(Debug)]
|
||||||
pull_en: bool,
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
/// true for pullup, false for pulldown
|
pub(crate) struct DynRegisters(DynPinId);
|
||||||
pull_dir: bool,
|
|
||||||
funsel: u8,
|
|
||||||
enb_input: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DynPinMode> for ModeFields {
|
// [`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]
|
#[inline]
|
||||||
fn from(mode: DynPinMode) -> Self {
|
fn id(&self) -> DynPinId {
|
||||||
let mut fields = Self::default();
|
self.0
|
||||||
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
|
impl DynRegisters {
|
||||||
type PortRegisterBlock = pac::porta::RegisterBlock;
|
/// Create a new instance of [`DynRegisters`]
|
||||||
pub type PortReg = pac::ioconfig::Porta;
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users must never create two simultaneous instances of this `struct` with
|
||||||
|
/// the same [`DynPinId`]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new(id: DynPinId) -> Self {
|
||||||
|
DynRegisters(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// DynPin
|
// DynPin
|
||||||
@ -251,59 +216,46 @@ pub type PortReg = pac::ioconfig::Porta;
|
|||||||
/// by the same type, and pins are tracked and distinguished at run-time.
|
/// by the same type, and pins are tracked and distinguished at run-time.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DynPin {
|
pub struct DynPin {
|
||||||
id: DynPinId,
|
pub(crate) regs: DynRegisters,
|
||||||
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) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
pub(crate) 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 {
|
||||||
id,
|
regs: DynRegisters::new(id),
|
||||||
mode: DYN_FLOATING_INPUT,
|
mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin ID
|
/// Return a copy of the pin ID
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn id(&self) -> DynPinId {
|
pub fn id(&self) -> DynPinId {
|
||||||
self.id
|
self.regs.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin mode
|
/// Return a copy of the pin mode
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn mode(&self) -> DynPinMode {
|
pub 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) {
|
||||||
self.change_mode(mode);
|
// Only modify registers if we are actually changing pin mode
|
||||||
self.mode = mode;
|
if mode != self.mode {
|
||||||
|
self.regs.change_mode(mode);
|
||||||
|
self.mode = mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -311,11 +263,6 @@ impl DynPin {
|
|||||||
matches!(self.mode, DynPinMode::Input(_))
|
matches!(self.mode, DynPinMode::Input(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_output_pin(&self) -> bool {
|
|
||||||
matches!(self.mode, DynPinMode::Output(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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);
|
||||||
@ -373,170 +320,74 @@ 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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
|
||||||
if irq_cfg.route {
|
|
||||||
self.configure_irqsel(irq_cfg.id);
|
|
||||||
}
|
|
||||||
if irq_cfg.enable_in_nvic {
|
|
||||||
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only manipulate our own bit.
|
|
||||||
self.port_reg()
|
|
||||||
.irq_enb()
|
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
|
||||||
if reset_irqsel {
|
|
||||||
self.reset_irqsel();
|
|
||||||
}
|
|
||||||
// We only manipulate our own bit.
|
|
||||||
self.port_reg()
|
|
||||||
.irq_enb()
|
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
|
||||||
///
|
|
||||||
/// There is no way for the compiler to know if the conversion will be
|
|
||||||
/// successful at compile-time. We must verify the conversion at run-time
|
|
||||||
/// or refuse to perform it.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
|
||||||
if self.id == I::DYN && self.mode == M::DYN {
|
|
||||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
|
||||||
// corresponding `Pin`
|
|
||||||
return Ok(unsafe { Pin::new() });
|
|
||||||
}
|
|
||||||
Err(InvalidPinTypeError(self.mode))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the pin into an async pin. The pin can be converted back by calling
|
|
||||||
/// [InputDynPinAsync::release]
|
|
||||||
pub fn into_async_input(
|
|
||||||
self,
|
|
||||||
irq: crate::pac::Interrupt,
|
|
||||||
) -> Result<InputDynPinAsync, InvalidPinTypeError> {
|
|
||||||
InputDynPinAsync::new(self, irq)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
|
|
||||||
pub fn configure_irqsel(&mut self, id: pac::Interrupt) {
|
|
||||||
let mut syscfg = unsafe { pac::Sysconfig::steal() };
|
|
||||||
let irqsel = unsafe { pac::Irqsel::steal() };
|
|
||||||
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
|
|
||||||
match self.id().port() {
|
|
||||||
// Set the correct interrupt number in the IRQSEL register
|
|
||||||
super::Port::A => {
|
|
||||||
irqsel
|
|
||||||
.porta0(self.id().num() as usize)
|
|
||||||
.write(|w| unsafe { w.bits(id as u32) });
|
|
||||||
}
|
|
||||||
super::Port::B => {
|
|
||||||
irqsel
|
|
||||||
.portb0(self.id().num as usize)
|
|
||||||
.write(|w| unsafe { w.bits(id as u32) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset the IRQSEL peripheral value for this particular pin.
|
|
||||||
pub fn reset_irqsel(&mut self) {
|
|
||||||
let mut syscfg = unsafe { pac::Sysconfig::steal() };
|
|
||||||
let irqsel = unsafe { pac::Irqsel::steal() };
|
|
||||||
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
|
|
||||||
match self.id().port() {
|
|
||||||
// Set the correct interrupt number in the IRQSEL register
|
|
||||||
super::Port::A => {
|
|
||||||
irqsel
|
|
||||||
.porta0(self.id().num() as usize)
|
|
||||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
|
||||||
}
|
|
||||||
super::Port::B => {
|
|
||||||
irqsel
|
|
||||||
.portb0(self.id().num as usize)
|
|
||||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get DATAMASK bit for this particular pin
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn datamask(&self) -> bool {
|
pub fn datamask(&self) -> bool {
|
||||||
(self.port_reg().datamask().read().bits() >> self.id().num) == 1
|
self.regs.datamask()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear DATAMASK bit for this particular pin. This prevents access
|
#[inline]
|
||||||
/// of the corresponding bit for output and input operations
|
pub fn clear_datamask(&mut self) {
|
||||||
#[inline(always)]
|
self.regs.clear_datamask();
|
||||||
pub fn clear_datamask(&self) {
|
|
||||||
self.port_reg()
|
|
||||||
.datamask()
|
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set DATAMASK bit for this particular pin. 1 is the default
|
#[inline]
|
||||||
/// state of the bit and allows access of the corresponding bit
|
pub fn set_datamask(&mut self) {
|
||||||
#[inline(always)]
|
self.regs.set_datamask();
|
||||||
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.read_pin_masked()
|
self.regs.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.read_pin_masked().map(|v| !v)
|
self.regs.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.write_pin_masked(true)
|
self.regs.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.write_pin_masked(false)
|
self.regs.write_pin_masked(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn irq_enb(
|
||||||
|
&mut self,
|
||||||
|
irq_cfg: crate::InterruptConfig,
|
||||||
|
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||||
|
irqsel: Option<&mut va108xx::Irqsel>,
|
||||||
|
) {
|
||||||
|
if let Some(syscfg) = syscfg {
|
||||||
|
crate::clock::enable_peripheral_clock(syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||||
|
}
|
||||||
|
self.regs.enable_irq();
|
||||||
|
if let Some(irqsel) = irqsel {
|
||||||
|
if irq_cfg.route {
|
||||||
|
match self.regs.id().group {
|
||||||
|
// Set the correct interrupt number in the IRQSEL register
|
||||||
|
DynGroup::A => {
|
||||||
|
irqsel
|
||||||
|
.porta0(self.regs.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||||
|
}
|
||||||
|
DynGroup::B => {
|
||||||
|
irqsel
|
||||||
|
.portb0(self.regs.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if irq_cfg.enable_in_nvic {
|
||||||
|
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
@ -549,13 +400,14 @@ impl DynPin {
|
|||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.configure_delay_internal(delay_1, delay_2);
|
self.regs.configure_delay(delay_1, delay_2);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -566,7 +418,7 @@ impl DynPin {
|
|||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.configure_pulse_mode_internal(enable, default_state);
|
self.regs.pulse_mode(enable, default_state);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
@ -582,102 +434,54 @@ impl DynPin {
|
|||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) => {
|
DynPinMode::Input(_) => {
|
||||||
self.configure_filter_type_internal(filter, clksel);
|
self.regs.configure_filter_type(filter, clksel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn configure_edge_interrupt(
|
pub fn configure_edge_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
edge_type: InterruptEdge,
|
edge_type: InterruptEdge,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
self.configure_edge_interrupt_internal(edge_type);
|
self.regs.configure_edge_interrupt(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn configure_level_interrupt(
|
pub fn configure_level_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
level_type: InterruptLevel,
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
self.configure_level_interrupt_internal(level_type);
|
self.regs.configure_level_interrupt(level_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the pin mode
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn change_mode(&mut self, mode: DynPinMode) {
|
pub fn toggle_with_toggle_reg(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
let ModeFields {
|
|
||||||
dir,
|
|
||||||
funsel,
|
|
||||||
opendrn,
|
|
||||||
pull_dir,
|
|
||||||
pull_en,
|
|
||||||
enb_input,
|
|
||||||
} = mode.into();
|
|
||||||
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
|
||||||
iocfg.write(|w| {
|
|
||||||
w.opendrn().bit(opendrn);
|
|
||||||
w.pen().bit(pull_en);
|
|
||||||
w.plevel().bit(pull_dir);
|
|
||||||
w.iewo().bit(enb_input);
|
|
||||||
unsafe { w.funsel().bits(funsel) }
|
|
||||||
});
|
|
||||||
let mask = self.mask_32();
|
|
||||||
unsafe {
|
|
||||||
if dir {
|
|
||||||
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
|
||||||
// Clear output
|
|
||||||
portreg.clrout().write(|w| w.bits(mask));
|
|
||||||
} else {
|
|
||||||
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
const fn port_reg(&self) -> &PortRegisterBlock {
|
|
||||||
match self.id().port() {
|
|
||||||
Port::A => unsafe { &(*pac::Porta::ptr()) },
|
|
||||||
Port::B => unsafe { &(*pac::Portb::ptr()) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
const fn iocfg_port(&self) -> &PortReg {
|
|
||||||
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
|
|
||||||
match self.id().port() {
|
|
||||||
Port::A => ioconfig.porta(self.id().num() as usize),
|
|
||||||
Port::B => ioconfig.portb0(self.id().num() as usize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_internal(&self) -> Result<bool, InvalidPinTypeError> {
|
|
||||||
match self.mode {
|
|
||||||
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
|
||||||
Ok(self.read_pin())
|
|
||||||
}
|
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
DynPinMode::Output(_) => {
|
DynPinMode::Output(_) => {
|
||||||
self.write_pin(bit);
|
self.regs.toggle();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
@ -685,190 +489,64 @@ impl DynPin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Read the logic level of an output pin
|
fn _read(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
pub(crate) fn read_pin(&self) -> bool {
|
match self.mode {
|
||||||
let portreg = self.port_reg();
|
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||||
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
Ok(self.regs.read_pin())
|
||||||
}
|
|
||||||
|
|
||||||
/// 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()));
|
|
||||||
}
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
|
||||||
/// cleared as well
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||||
if !self.datamask() {
|
match self.mode {
|
||||||
Err(IsMaskedError)
|
DynPinMode::Output(_) => {
|
||||||
} else {
|
self.regs.write_pin(bit);
|
||||||
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(self.mode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle the logic level of an output pin
|
#[inline]
|
||||||
#[inline(always)]
|
fn _is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
pub fn toggle_with_togout_reg(&mut self) {
|
self._read().map(|v| !v)
|
||||||
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
}
|
||||||
// this pin ID
|
#[inline]
|
||||||
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||||
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
///
|
||||||
|
/// 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]
|
||||||
fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) {
|
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
||||||
unsafe {
|
if self.regs.0 == I::DYN && self.mode == M::DYN {
|
||||||
self.port_reg()
|
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||||
.irq_sen()
|
// corresponding `Pin`
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
return Ok(unsafe { Pin::new() });
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Err(InvalidPinTypeError(self.mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure which edge or level type triggers an interrupt
|
/// Convert the pin into an async pin. The pin can be converted back by calling
|
||||||
#[inline]
|
/// [InputDynPinAsync::release]
|
||||||
fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) {
|
pub fn into_async_input(
|
||||||
unsafe {
|
self,
|
||||||
self.port_reg()
|
irq: crate::pac::Interrupt,
|
||||||
.irq_sen()
|
) -> Result<InputDynPinAsync, InvalidPinTypeError> {
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
InputDynPinAsync::new(self, irq)
|
||||||
if level == InterruptLevel::Low {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_evt()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
} else {
|
|
||||||
self.port_reg()
|
|
||||||
.irq_evt()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only useful for input pins
|
|
||||||
#[inline]
|
|
||||||
fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
|
||||||
self.iocfg_port().modify(|_, w| {
|
|
||||||
// Safety: Only write to register for this Pin ID
|
|
||||||
unsafe {
|
|
||||||
w.flttype().bits(filter as u8);
|
|
||||||
w.fltclk().bits(clksel as u8)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) {
|
|
||||||
let portreg = self.port_reg();
|
|
||||||
unsafe {
|
|
||||||
if enable {
|
|
||||||
portreg
|
|
||||||
.pulse()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
} else {
|
|
||||||
portreg
|
|
||||||
.pulse()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
}
|
|
||||||
if default_state == PinState::Low {
|
|
||||||
portreg
|
|
||||||
.pulsebase()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
} else {
|
|
||||||
portreg
|
|
||||||
.pulsebase()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only useful for output pins
|
|
||||||
#[inline]
|
|
||||||
fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) {
|
|
||||||
let portreg = self.port_reg();
|
|
||||||
unsafe {
|
|
||||||
if delay_1 {
|
|
||||||
portreg
|
|
||||||
.delay1()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
} else {
|
|
||||||
portreg
|
|
||||||
.delay1()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
}
|
|
||||||
if delay_2 {
|
|
||||||
portreg
|
|
||||||
.delay2()
|
|
||||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
|
||||||
} else {
|
|
||||||
portreg
|
|
||||||
.delay2()
|
|
||||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only serves disambiguation purposes for the Embedded HAL impl
|
|
||||||
#[inline(always)]
|
|
||||||
fn is_low_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
|
||||||
self.is_low()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only serves disambiguation purposes for the Embedded HAL impl
|
|
||||||
#[inline(always)]
|
|
||||||
fn is_high_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
|
|
||||||
self.is_high()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
const fn mask_32(&self) -> u32 {
|
|
||||||
1 << self.id().num()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,38 +588,33 @@ impl embedded_hal::digital::ErrorType for DynPin {
|
|||||||
impl embedded_hal::digital::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 embedded_hal::digital::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_mut()
|
self._is_high()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
self.is_low_mut()
|
self._is_low()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_hal::digital::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_mut()
|
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> {
|
||||||
self.is_low_mut()
|
self._is_low()
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.toggle()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,47 +22,14 @@
|
|||||||
//!
|
//!
|
||||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// Errors, Definitions and Constants
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
pub const NUM_PINS_PORT_A: usize = 32;
|
|
||||||
pub const NUM_PINS_PORT_B: usize = 24;
|
|
||||||
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("The pin is masked")]
|
#[error("The pin is masked")]
|
||||||
pub struct IsMaskedError;
|
pub struct IsMaskedError;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
pub const NUM_PINS_PORT_A: usize = 32;
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
pub const NUM_PINS_PORT_B: usize = 24;
|
||||||
pub enum Port {
|
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
|
||||||
A,
|
|
||||||
B,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum InterruptEdge {
|
|
||||||
HighToLow,
|
|
||||||
LowToHigh,
|
|
||||||
BothEdges,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum InterruptLevel {
|
|
||||||
Low = 0,
|
|
||||||
High = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum PinState {
|
|
||||||
Low = 0,
|
|
||||||
High = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod dynpin;
|
pub mod dynpin;
|
||||||
pub use dynpin::*;
|
pub use dynpin::*;
|
||||||
@ -72,3 +39,5 @@ pub use pin::*;
|
|||||||
|
|
||||||
pub mod asynch;
|
pub mod asynch;
|
||||||
pub use asynch::*;
|
pub use asynch::*;
|
||||||
|
|
||||||
|
mod reg;
|
||||||
|
@ -68,19 +68,48 @@
|
|||||||
//! # 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: [embedded_hal::digital::InputPin],
|
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
|
||||||
//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
|
//! and [`StatefulOutputPin`].
|
||||||
use super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode};
|
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||||
use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port};
|
use super::reg::RegisterInterface;
|
||||||
|
use super::{DynPin, InputPinAsync};
|
||||||
use crate::{
|
use crate::{
|
||||||
pac::{Porta, Portb},
|
pac::{Irqsel, Porta, Portb, Sysconfig},
|
||||||
typelevel::Sealed,
|
typelevel::Sealed,
|
||||||
|
InterruptConfig,
|
||||||
};
|
};
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem::transmute;
|
use core::mem::transmute;
|
||||||
|
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// 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
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -291,7 +320,10 @@ macro_rules! pin_id {
|
|||||||
pub enum $Id {}
|
pub enum $Id {}
|
||||||
impl Sealed for $Id {}
|
impl Sealed for $Id {}
|
||||||
impl PinId for $Id {
|
impl PinId for $Id {
|
||||||
const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM);
|
const DYN: DynPinId = DynPinId {
|
||||||
|
group: DynGroup::$Group,
|
||||||
|
num: $NUM,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -317,7 +349,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) const unsafe fn new() -> Pin<I, M> {
|
pub(crate) unsafe fn new() -> Pin<I, M> {
|
||||||
Pin {
|
Pin {
|
||||||
inner: DynPin::new(I::DYN, M::DYN),
|
inner: DynPin::new(I::DYN, M::DYN),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
@ -325,7 +357,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn id(&self) -> DynPinId {
|
pub fn id(&self) -> DynPinId {
|
||||||
self.inner.id()
|
self.inner.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +367,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
// 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.change_mode(N::DYN);
|
self.inner.regs.change_mode(N::DYN);
|
||||||
}
|
}
|
||||||
// Safe because we drop the existing Pin
|
// Safe because we drop the existing Pin
|
||||||
unsafe { Pin::new() }
|
unsafe { Pin::new() }
|
||||||
@ -395,16 +427,6 @@ 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()
|
||||||
@ -430,41 +452,48 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Those only serve for the embedded HAL implementations which have different mutability.
|
fn irq_enb(
|
||||||
|
&mut self,
|
||||||
#[inline]
|
irq_cfg: crate::InterruptConfig,
|
||||||
fn is_low_mut(&mut self) -> bool {
|
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||||
self.is_low()
|
irqsel: Option<&mut va108xx::Irqsel>,
|
||||||
|
) {
|
||||||
|
self.inner.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high_mut(&mut self) -> bool {
|
pub(crate) fn _set_high(&mut self) {
|
||||||
self.is_high()
|
self.inner.regs.write_pin(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
pub(crate) fn _set_low(&mut self) {
|
||||||
self.inner.enable_interrupt(irq_cfg);
|
self.inner.regs.write_pin(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
|
pub(crate) fn _is_low(&self) -> bool {
|
||||||
self.inner.disable_interrupt(reset_irqsel);
|
!self.inner.regs.read_pin()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the pin for an edge interrupt but does not enable the interrupt.
|
#[inline]
|
||||||
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
pub(crate) fn _is_high(&self) -> bool {
|
||||||
self.inner.configure_edge_interrupt(edge_type).unwrap();
|
self.inner.regs.read_pin()
|
||||||
}
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,34 +590,31 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
|||||||
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
|
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
|
||||||
InputPinAsync::new(self, irq)
|
InputPinAsync::new(self, irq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn configure_edge_interrupt(
|
||||||
|
&mut self,
|
||||||
|
edge_type: InterruptEdge,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) {
|
||||||
|
self.inner.regs.configure_edge_interrupt(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_level_interrupt(
|
||||||
|
&mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) {
|
||||||
|
self.inner.regs.configure_level_interrupt(level_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||||
#[inline]
|
|
||||||
pub fn set_high(&mut self) {
|
|
||||||
self.inner.write_pin(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_low(&mut self) {
|
|
||||||
self.inner.write_pin(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn toggle(&mut self) {
|
|
||||||
self.inner.toggle().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
|
||||||
self.inner.set_high_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
|
||||||
self.inner.set_low_masked()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See p.53 of the programmers guide for more information.
|
/// 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
|
||||||
@ -596,17 +622,78 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
|||||||
/// - 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.configure_delay(delay_1, delay_2).unwrap();
|
self.inner.regs.configure_delay(delay_1, delay_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn toggle_with_toggle_reg(&mut self) {
|
||||||
|
self.inner.regs.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.9.0",
|
||||||
|
note = "Please use the `configure_pulse_mode` method instead"
|
||||||
|
)]
|
||||||
|
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||||
|
self.configure_pulse_mode(enable, default_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See p.52 of the programmers guide for more information.
|
/// 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
|
self.inner.regs.pulse_mode(enable, default_state);
|
||||||
.configure_pulse_mode(enable, default_state)
|
}
|
||||||
.unwrap();
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.9.0",
|
||||||
|
note = "Please use the `configure_edge_interrupt` method instead"
|
||||||
|
)]
|
||||||
|
pub fn interrupt_edge(
|
||||||
|
&mut self,
|
||||||
|
edge_type: InterruptEdge,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) {
|
||||||
|
self.inner.regs.configure_edge_interrupt(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_edge_interrupt(
|
||||||
|
&mut self,
|
||||||
|
edge_type: InterruptEdge,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) {
|
||||||
|
self.inner.regs.configure_edge_interrupt(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.9.0",
|
||||||
|
note = "Please use the `configure_level_interrupt` method instead"
|
||||||
|
)]
|
||||||
|
pub fn level_interrupt(
|
||||||
|
&mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) {
|
||||||
|
self.configure_level_interrupt(level_type, irq_cfg, syscfg, irqsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_level_interrupt(
|
||||||
|
&mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) {
|
||||||
|
self.inner.regs.configure_level_interrupt(level_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +701,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
|||||||
/// See p.37 and p.38 of the programmers guide for more information.
|
/// 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.configure_filter_type(filter, clksel).unwrap();
|
self.inner.regs.configure_filter_type(filter, clksel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,53 +717,63 @@ where
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
|
impl<I: PinId, C: OutputConfig> 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> embedded_hal::digital::InputPin for Pin<I, Input<C>>
|
impl<I, C> 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_mut())
|
Ok(self._is_high())
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low_mut())
|
Ok(self._is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
|
impl<I, C> 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> InputPin for Pin<I, Output<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: OutputConfig + ReadableOutput,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
self.toggle();
|
Ok(self._is_low())
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
375
va108xx-hal/src/gpio/reg.rs
Normal file
375
va108xx-hal/src/gpio/reg.rs
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
use super::dynpin::{self, DynGroup, DynPinId, DynPinMode};
|
||||||
|
use super::pin::{FilterType, InterruptEdge, InterruptLevel, PinState};
|
||||||
|
use super::IsMaskedError;
|
||||||
|
use crate::clock::FilterClkSel;
|
||||||
|
use va108xx::{ioconfig, porta};
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
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;
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Register Interface
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub type PortReg = ioconfig::Porta;
|
||||||
|
|
||||||
|
/// Provide a safe register interface for pin objects
|
||||||
|
///
|
||||||
|
/// [`PORTA`] and [`PORTB`], 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;
|
||||||
|
|
||||||
|
const PORTA: *const PortRegisterBlock = va108xx::Porta::ptr();
|
||||||
|
const PORTB: *const PortRegisterBlock = va108xx::Portb::ptr();
|
||||||
|
|
||||||
|
/// 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]
|
||||||
|
fn port_reg(&self) -> &PortRegisterBlock {
|
||||||
|
match self.id().group {
|
||||||
|
DynGroup::A => unsafe { &(*Self::PORTA) },
|
||||||
|
DynGroup::B => unsafe { &(*Self::PORTB) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn iocfg_port(&self) -> &PortReg {
|
||||||
|
let ioconfig = unsafe { va108xx::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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.id().num
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
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 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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
use gpio::Port;
|
|
||||||
pub use va108xx;
|
pub use va108xx;
|
||||||
pub use va108xx as pac;
|
pub use va108xx as pac;
|
||||||
|
|
||||||
@ -20,12 +19,18 @@ pub mod uart;
|
|||||||
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum FunSel {
|
pub enum FunSel {
|
||||||
Sel0 = 0b00,
|
|
||||||
Sel1 = 0b01,
|
Sel1 = 0b01,
|
||||||
Sel2 = 0b10,
|
Sel2 = 0b10,
|
||||||
Sel3 = 0b11,
|
Sel3 = 0b11,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum PortSel {
|
||||||
|
PortA,
|
||||||
|
PortB,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum PeripheralSelect {
|
pub enum PeripheralSelect {
|
||||||
@ -72,33 +77,37 @@ impl InterruptConfig {
|
|||||||
|
|
||||||
pub type IrqCfg = InterruptConfig;
|
pub type IrqCfg = InterruptConfig;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("invalid pin with number {0}")]
|
pub struct InvalidPin(pub(crate) ());
|
||||||
pub struct InvalidPinError(u8);
|
|
||||||
|
|
||||||
/// Can be used to manually manipulate the function select of port pins.
|
/// Can be used to manually manipulate the function select of port pins
|
||||||
///
|
|
||||||
/// The function selection table can be found on p.36 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.
|
|
||||||
pub fn port_function_select(
|
pub fn port_function_select(
|
||||||
ioconfig: &mut pac::Ioconfig,
|
ioconfig: &mut pac::Ioconfig,
|
||||||
port: Port,
|
port: PortSel,
|
||||||
pin: u8,
|
pin: u8,
|
||||||
funsel: FunSel,
|
funsel: FunSel,
|
||||||
) -> Result<(), InvalidPinError> {
|
) -> Result<(), InvalidPin> {
|
||||||
if (port == Port::A && pin >= 32) || (port == Port::B && pin >= 24) {
|
match port {
|
||||||
return Err(InvalidPinError(pin));
|
PortSel::PortA => {
|
||||||
|
if pin > 31 {
|
||||||
|
return Err(InvalidPin(()));
|
||||||
|
}
|
||||||
|
ioconfig
|
||||||
|
.porta(pin as usize)
|
||||||
|
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
PortSel::PortB => {
|
||||||
|
if pin > 23 {
|
||||||
|
return Err(InvalidPin(()));
|
||||||
|
}
|
||||||
|
ioconfig
|
||||||
|
.portb0(pin as usize)
|
||||||
|
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let reg_block = match port {
|
|
||||||
Port::A => ioconfig.porta(pin as usize),
|
|
||||||
Port::B => ioconfig.portb0(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.
|
||||||
|
@ -22,7 +22,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use embedded_hal_nb::serial::Read;
|
use embedded_hal_nb::serial::Read;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Bank {
|
pub enum Bank {
|
||||||
A = 0,
|
A = 0,
|
||||||
@ -381,7 +381,6 @@ pub struct BufferTooShortError {
|
|||||||
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||||
const IDX: u8;
|
const IDX: u8;
|
||||||
const PERIPH_SEL: PeripheralSelect;
|
const PERIPH_SEL: PeripheralSelect;
|
||||||
const PTR: *const uart_base::RegisterBlock;
|
|
||||||
|
|
||||||
/// Retrieve the peripheral structure.
|
/// Retrieve the peripheral structure.
|
||||||
///
|
///
|
||||||
@ -389,11 +388,7 @@ pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
|||||||
///
|
///
|
||||||
/// This circumvents the safety guarantees of the HAL.
|
/// This circumvents the safety guarantees of the HAL.
|
||||||
unsafe fn steal() -> Self;
|
unsafe fn steal() -> Self;
|
||||||
|
fn ptr() -> *const uart_base::RegisterBlock;
|
||||||
#[inline(always)]
|
|
||||||
fn ptr() -> *const uart_base::RegisterBlock {
|
|
||||||
Self::PTR
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve the type erased peripheral register block.
|
/// Retrieve the type erased peripheral register block.
|
||||||
///
|
///
|
||||||
@ -410,11 +405,14 @@ impl Instance for pac::Uarta {
|
|||||||
const IDX: u8 = 0;
|
const IDX: u8 = 0;
|
||||||
|
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
|
||||||
const PTR: *const uart_base::RegisterBlock = Self::PTR;
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn steal() -> Self {
|
unsafe fn steal() -> Self {
|
||||||
Self::steal()
|
pac::Peripherals::steal().uarta
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn ptr() -> *const uart_base::RegisterBlock {
|
||||||
|
Self::ptr() as *const _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,25 +420,14 @@ impl Instance for pac::Uartb {
|
|||||||
const IDX: u8 = 1;
|
const IDX: u8 = 1;
|
||||||
|
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
|
||||||
const PTR: *const uart_base::RegisterBlock = Self::PTR;
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn steal() -> Self {
|
unsafe fn steal() -> Self {
|
||||||
Self::steal()
|
pac::Peripherals::steal().uartb
|
||||||
}
|
}
|
||||||
}
|
#[inline(always)]
|
||||||
|
fn ptr() -> *const uart_base::RegisterBlock {
|
||||||
impl Bank {
|
Self::ptr() as *const _
|
||||||
/// Retrieve the peripheral register block.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Circumvents the HAL safety guarantees.
|
|
||||||
pub unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock {
|
|
||||||
match self {
|
|
||||||
Bank::A => unsafe { pac::Uarta::reg_block() },
|
|
||||||
Bank::B => unsafe { pac::Uartb::reg_block() },
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,12 +794,14 @@ pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) {
|
|||||||
/// Serial receiver.
|
/// Serial receiver.
|
||||||
///
|
///
|
||||||
/// Can be created by using the [Uart::split] or [UartBase::split] API.
|
/// Can be created by using the [Uart::split] or [UartBase::split] API.
|
||||||
pub struct Rx<Uart>(Uart);
|
pub struct Rx<Uart> {
|
||||||
|
uart: Uart,
|
||||||
|
}
|
||||||
|
|
||||||
impl<Uart: Instance> Rx<Uart> {
|
impl<Uart: Instance> Rx<Uart> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
const fn new(uart: Uart) -> Self {
|
fn new(uart: Uart) -> Self {
|
||||||
Self(uart)
|
Self { uart }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Direct access to the peripheral structure.
|
/// Direct access to the peripheral structure.
|
||||||
@ -821,13 +810,13 @@ impl<Uart: Instance> Rx<Uart> {
|
|||||||
///
|
///
|
||||||
/// You must ensure that only registers related to the operation of the RX side are used.
|
/// You must ensure that only registers related to the operation of the RX side are used.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const unsafe fn uart(&self) -> &Uart {
|
pub unsafe fn uart(&self) -> &Uart {
|
||||||
&self.0
|
&self.uart
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&self) {
|
||||||
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
|
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -857,7 +846,7 @@ impl<Uart: Instance> Rx<Uart> {
|
|||||||
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
|
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn read_fifo(&self) -> nb::Result<u32, Infallible> {
|
pub fn read_fifo(&self) -> nb::Result<u32, Infallible> {
|
||||||
if self.0.rxstatus().read().rdavl().bit_is_clear() {
|
if self.uart.rxstatus().read().rdavl().bit_is_clear() {
|
||||||
return Err(nb::Error::WouldBlock);
|
return Err(nb::Error::WouldBlock);
|
||||||
}
|
}
|
||||||
Ok(self.read_fifo_unchecked())
|
Ok(self.read_fifo_unchecked())
|
||||||
@ -873,7 +862,7 @@ impl<Uart: Instance> Rx<Uart> {
|
|||||||
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
|
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn read_fifo_unchecked(&self) -> u32 {
|
pub fn read_fifo_unchecked(&self) -> u32 {
|
||||||
self.0.data().read().bits()
|
self.uart.data().read().bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
|
pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
|
||||||
@ -882,7 +871,7 @@ impl<Uart: Instance> Rx<Uart> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn release(self) -> Uart {
|
pub fn release(self) -> Uart {
|
||||||
self.0
|
self.uart
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -970,7 +959,9 @@ pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
|
|||||||
/// Serial transmitter
|
/// Serial transmitter
|
||||||
///
|
///
|
||||||
/// Can be created by using the [Uart::split] or [UartBase::split] API.
|
/// Can be created by using the [Uart::split] or [UartBase::split] API.
|
||||||
pub struct Tx<Uart>(Uart);
|
pub struct Tx<Uart> {
|
||||||
|
uart: Uart,
|
||||||
|
}
|
||||||
|
|
||||||
impl<Uart: Instance> Tx<Uart> {
|
impl<Uart: Instance> Tx<Uart> {
|
||||||
/// Retrieve a TX pin without expecting an explicit UART structure
|
/// Retrieve a TX pin without expecting an explicit UART structure
|
||||||
@ -980,12 +971,14 @@ impl<Uart: Instance> Tx<Uart> {
|
|||||||
/// Circumvents the HAL safety guarantees.
|
/// Circumvents the HAL safety guarantees.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn steal() -> Self {
|
pub unsafe fn steal() -> Self {
|
||||||
Self(Uart::steal())
|
Self {
|
||||||
|
uart: Uart::steal(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn new(uart: Uart) -> Self {
|
fn new(uart: Uart) -> Self {
|
||||||
Self(uart)
|
Self { uart }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Direct access to the peripheral structure.
|
/// Direct access to the peripheral structure.
|
||||||
@ -994,23 +987,25 @@ impl<Uart: Instance> Tx<Uart> {
|
|||||||
///
|
///
|
||||||
/// You must ensure that only registers related to the operation of the TX side are used.
|
/// You must ensure that only registers related to the operation of the TX side are used.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const unsafe fn uart(&self) -> &Uart {
|
pub unsafe fn uart(&self) -> &Uart {
|
||||||
&self.0
|
&self.uart
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&self) {
|
||||||
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
|
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enable(&mut self) {
|
pub fn enable(&mut self) {
|
||||||
self.0.enable().modify(|_, w| w.txenable().set_bit());
|
// Safety: We own the UART structure
|
||||||
|
enable_tx(unsafe { Uart::reg_block() });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable(&mut self) {
|
pub fn disable(&mut self) {
|
||||||
self.0.enable().modify(|_, w| w.txenable().clear_bit());
|
// Safety: We own the UART structure
|
||||||
|
disable_tx(unsafe { Uart::reg_block() });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
|
/// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
|
||||||
@ -1042,7 +1037,7 @@ impl<Uart: Instance> Tx<Uart> {
|
|||||||
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
|
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> {
|
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> {
|
||||||
if self.0.txstatus().read().wrrdy().bit_is_clear() {
|
if self.uart.txstatus().read().wrrdy().bit_is_clear() {
|
||||||
return Err(nb::Error::WouldBlock);
|
return Err(nb::Error::WouldBlock);
|
||||||
}
|
}
|
||||||
self.write_fifo_unchecked(data);
|
self.write_fifo_unchecked(data);
|
||||||
@ -1057,7 +1052,7 @@ impl<Uart: Instance> Tx<Uart> {
|
|||||||
/// API.
|
/// API.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_fifo_unchecked(&self, data: u32) {
|
pub fn write_fifo_unchecked(&self, data: u32) {
|
||||||
self.0.data().write(|w| unsafe { w.bits(data) });
|
self.uart.data().write(|w| unsafe { w.bits(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_async(self) -> TxAsync<Uart> {
|
pub fn into_async(self) -> TxAsync<Uart> {
|
||||||
@ -1140,7 +1135,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn uart(&self) -> &Uart {
|
pub fn uart(&self) -> &Uart {
|
||||||
&self.0 .0
|
&self.0.uart
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]
|
/// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
//! # Async UART reception functionality for the VA416xx family.
|
//! # Async UART reception functionality for the VA108xx family.
|
||||||
//!
|
//!
|
||||||
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the
|
//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
|
||||||
//! [embedded_io_async::Read] trait.
|
//! [embedded_io_async::Read] trait.
|
||||||
//! This trait allows for asynchronous reception of data streams. Please note that this module does
|
//! This trait allows for asynchronous reception of data streams. Please note that this module does
|
||||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||||
//! However, it provides two interrupt handlers:
|
//! However, it provides four interrupt handlers:
|
||||||
//!
|
//!
|
||||||
//! - [on_interrupt_rx]
|
//! - [on_interrupt_uart_a]
|
||||||
//! - [on_interrupt_rx_overwriting]
|
//! - [on_interrupt_uart_b]
|
||||||
|
//! - [on_interrupt_uart_a_overwriting]
|
||||||
|
//! - [on_interrupt_uart_b_overwriting]
|
||||||
//!
|
//!
|
||||||
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
|
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
|
||||||
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
|
//! [RxAsyncSharedConsumer] struct. The later two will overwrite old values in the used ring buffer.
|
||||||
//!
|
//!
|
||||||
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
|
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
|
||||||
//! structure returned by the interrupt handlers.
|
//! structure returned by the interrupt handlers.
|
||||||
@ -23,10 +25,11 @@ use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ord
|
|||||||
use critical_section::Mutex;
|
use critical_section::Mutex;
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use embedded_io::ErrorType;
|
use embedded_io::ErrorType;
|
||||||
|
use heapless::spsc::Consumer;
|
||||||
use portable_atomic::AtomicBool;
|
use portable_atomic::AtomicBool;
|
||||||
use va108xx::uarta as uart_base;
|
use va108xx as pac;
|
||||||
|
|
||||||
use super::{Bank, Instance, Rx, RxError, UartErrors};
|
use super::{Instance, Rx, RxError, UartErrors};
|
||||||
|
|
||||||
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||||
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
@ -69,7 +72,7 @@ pub struct AsyncUartErrors {
|
|||||||
pub uart_errors: UartErrors,
|
pub uart_errors: UartErrors,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Option<UartErrors> {
|
fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErrors> {
|
||||||
let rx_status = uart.rxstatus().read();
|
let rx_status = uart.rxstatus().read();
|
||||||
if rx_status.rxovr().bit_is_set()
|
if rx_status.rxovr().bit_is_set()
|
||||||
|| rx_status.rxfrm().bit_is_set()
|
|| rx_status.rxfrm().bit_is_set()
|
||||||
@ -91,65 +94,81 @@ fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Opt
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt_rx_common_post_processing(
|
fn on_interrupt_rx_common_post_processing<Uart: Instance>(
|
||||||
bank: Bank,
|
uart: &Uart,
|
||||||
rx_enabled: bool,
|
rx_enabled: bool,
|
||||||
read_some_data: bool,
|
read_some_data: bool,
|
||||||
irq_end: u32,
|
irq_end: u32,
|
||||||
) -> Option<UartErrors> {
|
) -> Option<UartErrors> {
|
||||||
let idx = bank as usize;
|
|
||||||
if read_some_data {
|
if read_some_data {
|
||||||
RX_HAS_DATA[idx].store(true, Ordering::Relaxed);
|
RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed);
|
||||||
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) {
|
if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) {
|
||||||
UART_RX_WAKERS[idx].wake();
|
UART_RX_WAKERS[Uart::IDX as usize].wake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut errors = None;
|
let mut errors = None;
|
||||||
let uart_regs = unsafe { bank.reg_block() };
|
|
||||||
// Check for RX errors
|
// Check for RX errors
|
||||||
if rx_enabled {
|
if rx_enabled {
|
||||||
errors = on_interrupt_handle_rx_errors(uart_regs);
|
errors = on_interrupt_handle_rx_errors(uart);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the interrupt status bits
|
// Clear the interrupt status bits
|
||||||
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interrupt handler with overwriting behaviour when the ring buffer is full.
|
/// Interrupt handler for UART A.
|
||||||
///
|
///
|
||||||
/// Should be called in the user interrupt handler to enable
|
/// Should be called in the user interrupt handler to enable
|
||||||
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||||
/// the ring buffer is full.
|
/// the ring buffer is full.
|
||||||
pub fn on_interrupt_rx_overwriting<const N: usize>(
|
pub fn on_interrupt_uart_a_overwriting<const N: usize>(
|
||||||
bank: Bank,
|
|
||||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
) -> Result<(), AsyncUartErrors> {
|
) -> Result<(), AsyncUartErrors> {
|
||||||
on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer)
|
on_interrupt_rx_async_heapless_queue_overwriting(
|
||||||
|
unsafe { pac::Uarta::steal() },
|
||||||
|
prod,
|
||||||
|
shared_consumer,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
|
/// Interrupt handler for UART B.
|
||||||
bank: Bank,
|
///
|
||||||
|
/// Should be called in the user interrupt handler to enable
|
||||||
|
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
|
||||||
|
/// the ring buffer is full.
|
||||||
|
pub fn on_interrupt_uart_b_overwriting<const N: usize>(
|
||||||
prod: &mut heapless::spsc::Producer<u8, N>,
|
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||||
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
) -> Result<(), AsyncUartErrors> {
|
) -> Result<(), AsyncUartErrors> {
|
||||||
let uart_regs = unsafe { bank.reg_block() };
|
on_interrupt_rx_async_heapless_queue_overwriting(
|
||||||
let irq_end = uart_regs.irq_end().read();
|
unsafe { pac::Uartb::steal() },
|
||||||
let enb_status = uart_regs.enable().read();
|
prod,
|
||||||
|
shared_consumer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N: usize>(
|
||||||
|
uart: Uart,
|
||||||
|
prod: &mut heapless::spsc::Producer<u8, N>,
|
||||||
|
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
let irq_end = uart.irq_end().read();
|
||||||
|
let enb_status = uart.enable().read();
|
||||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||||
let mut read_some_data = false;
|
let mut read_some_data = false;
|
||||||
let mut queue_overflow = false;
|
let mut queue_overflow = false;
|
||||||
|
|
||||||
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||||
if irq_end.irq_rx().bit_is_set() {
|
if irq_end.irq_rx().bit_is_set() {
|
||||||
let available_bytes = uart_regs.rxfifoirqtrg().read().bits() as usize;
|
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
|
||||||
|
|
||||||
// If this interrupt bit is set, the trigger level is available at the very least.
|
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||||
// Read everything as fast as possible
|
// Read everything as fast as possible
|
||||||
for _ in 0..available_bytes {
|
for _ in 0..available_bytes {
|
||||||
let byte = uart_regs.data().read().bits();
|
let byte = uart.data().read().bits();
|
||||||
if !prod.ready() {
|
if !prod.ready() {
|
||||||
queue_overflow = true;
|
queue_overflow = true;
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
@ -164,9 +183,9 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
|
|||||||
|
|
||||||
// Timeout, empty the FIFO completely.
|
// Timeout, empty the FIFO completely.
|
||||||
if irq_end.irq_rx_to().bit_is_set() {
|
if irq_end.irq_rx_to().bit_is_set() {
|
||||||
while uart_regs.rxstatus().read().rdavl().bit_is_set() {
|
while uart.rxstatus().read().rdavl().bit_is_set() {
|
||||||
// While there is data in the FIFO, write it into the reception buffer
|
// While there is data in the FIFO, write it into the reception buffer
|
||||||
let byte = uart_regs.data().read().bits();
|
let byte = uart.data().read().bits();
|
||||||
if !prod.ready() {
|
if !prod.ready() {
|
||||||
queue_overflow = true;
|
queue_overflow = true;
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
@ -180,7 +199,7 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let uart_errors =
|
let uart_errors =
|
||||||
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
|
||||||
if uart_errors.is_some() || queue_overflow {
|
if uart_errors.is_some() || queue_overflow {
|
||||||
return Err(AsyncUartErrors {
|
return Err(AsyncUartErrors {
|
||||||
queue_overflow,
|
queue_overflow,
|
||||||
@ -190,21 +209,29 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interrupt handler for asynchronous RX operations.
|
/// Interrupt handler for UART A.
|
||||||
///
|
///
|
||||||
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||||
pub fn on_interrupt_rx<const N: usize>(
|
pub fn on_interrupt_uart_a<const N: usize>(
|
||||||
bank: Bank,
|
|
||||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||||
) -> Result<(), AsyncUartErrors> {
|
) -> Result<(), AsyncUartErrors> {
|
||||||
on_interrupt_rx_async_heapless_queue(bank, prod)
|
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
|
/// Interrupt handler for UART B.
|
||||||
bank: Bank,
|
///
|
||||||
|
/// Should be called in the user interrupt handler to enable asynchronous reception.
|
||||||
|
pub fn on_interrupt_uart_b<const N: usize>(
|
||||||
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||||
) -> Result<(), AsyncUartErrors> {
|
) -> Result<(), AsyncUartErrors> {
|
||||||
let uart = unsafe { bank.reg_block() };
|
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uartb::steal() }, prod)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
|
||||||
|
uart: Uart,
|
||||||
|
prod: &mut heapless::spsc::Producer<'_, u8, N>,
|
||||||
|
) -> Result<(), AsyncUartErrors> {
|
||||||
|
//let uart = unsafe { Uart::steal() };
|
||||||
let irq_end = uart.irq_end().read();
|
let irq_end = uart.irq_end().read();
|
||||||
let enb_status = uart.enable().read();
|
let enb_status = uart.enable().read();
|
||||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||||
@ -241,7 +268,7 @@ pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let uart_errors =
|
let uart_errors =
|
||||||
on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits());
|
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
|
||||||
if uart_errors.is_some() || queue_overflow {
|
if uart_errors.is_some() || queue_overflow {
|
||||||
return Err(AsyncUartErrors {
|
return Err(AsyncUartErrors {
|
||||||
queue_overflow,
|
queue_overflow,
|
||||||
@ -259,32 +286,24 @@ impl Drop for ActiveReadGuard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RxAsyncInner<Uart: Instance, const N: usize> {
|
|
||||||
rx: Rx<Uart>,
|
|
||||||
pub queue: heapless::spsc::Consumer<'static, u8, N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Core data structure to allow asynchronous UART reception.
|
/// Core data structure to allow asynchronous UART reception.
|
||||||
///
|
///
|
||||||
/// If the ring buffer becomes full, data will be lost.
|
/// If the ring buffer becomes full, data will be lost.
|
||||||
pub struct RxAsync<Uart: Instance, const N: usize>(Option<RxAsyncInner<Uart, N>>);
|
pub struct RxAsync<Uart: Instance, const N: usize> {
|
||||||
|
rx: Rx<Uart>,
|
||||||
|
pub queue: heapless::spsc::Consumer<'static, u8, N>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
|
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
|
||||||
/// Error reporting is done using the result of the interrupt functions.
|
/// Error reporting is done using the result of the interrupt functions.
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_async_rx<Uart: Instance>(rx: &mut Rx<Uart>) {
|
|
||||||
rx.disable_interrupts();
|
|
||||||
rx.disable();
|
|
||||||
rx.clear_fifo();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
||||||
/// Create a new asynchronous receiver.
|
/// Create a new asynchronous receiver.
|
||||||
///
|
///
|
||||||
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
|
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
|
||||||
/// is filled by the interrupt handler [on_interrupt_rx].
|
/// is filled by the interrupt handler.
|
||||||
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
|
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
|
||||||
rx.disable_interrupts();
|
rx.disable_interrupts();
|
||||||
rx.disable();
|
rx.disable();
|
||||||
@ -294,23 +313,7 @@ impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
|
|||||||
rx.enable_interrupts();
|
rx.enable_interrupts();
|
||||||
rx.enable();
|
rx.enable();
|
||||||
});
|
});
|
||||||
Self(Some(RxAsyncInner { rx, queue }))
|
Self { rx, queue }
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
|
||||||
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(mut self) -> (Rx<Uart>, heapless::spsc::Consumer<'static, u8, N>) {
|
|
||||||
self.stop();
|
|
||||||
let inner = self.0.take().unwrap();
|
|
||||||
(inner.rx, inner.queue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> Drop for RxAsync<Uart, N> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +321,7 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N
|
|||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||||
// empty, we can read data immediately.
|
// empty, we can read data immediately.
|
||||||
if self.0.as_ref().unwrap().queue.len() == 0 {
|
if self.queue.len() == 0 {
|
||||||
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||||
@ -330,38 +333,33 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N
|
|||||||
}
|
}
|
||||||
data_to_read
|
data_to_read
|
||||||
};
|
};
|
||||||
let mut_ref = self.0.as_mut().unwrap();
|
let fut = RxFuture::new(&mut self.rx);
|
||||||
let fut = RxFuture::new(&mut mut_ref.rx);
|
|
||||||
// Data is available, so read that data immediately.
|
// Data is available, so read that data immediately.
|
||||||
let read_data = handle_data_in_queue(&mut mut_ref.queue);
|
let read_data = handle_data_in_queue(&mut self.queue);
|
||||||
if read_data > 0 {
|
if read_data > 0 {
|
||||||
return Ok(read_data);
|
return Ok(read_data);
|
||||||
}
|
}
|
||||||
// Await data.
|
// Await data.
|
||||||
let _ = fut.await;
|
let _ = fut.await;
|
||||||
Ok(handle_data_in_queue(&mut mut_ref.queue))
|
Ok(handle_data_in_queue(&mut self.queue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RxAsyncOverwritingInner<Uart: Instance, const N: usize> {
|
|
||||||
rx: Rx<Uart>,
|
|
||||||
pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Core data structure to allow asynchronous UART reception.
|
/// Core data structure to allow asynchronous UART reception.
|
||||||
///
|
///
|
||||||
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
|
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
|
||||||
/// [on_interrupt_rx_overwriting] interrupt handlers.
|
/// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers.
|
||||||
pub struct RxAsyncOverwriting<Uart: Instance, const N: usize>(
|
pub struct RxAsyncSharedConsumer<Uart: Instance, const N: usize> {
|
||||||
Option<RxAsyncOverwritingInner<Uart, N>>,
|
rx: Rx<Uart>,
|
||||||
);
|
queue: &'static Mutex<RefCell<Option<Consumer<'static, u8, N>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncOverwriting<Uart, N> {
|
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncSharedConsumer<Uart, N> {
|
||||||
/// Error reporting is done using the result of the interrupt functions.
|
/// Error reporting is done using the result of the interrupt functions.
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
|
impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
|
||||||
/// Create a new asynchronous receiver.
|
/// Create a new asynchronous receiver.
|
||||||
///
|
///
|
||||||
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
|
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
|
||||||
@ -369,7 +367,7 @@ impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
|
|||||||
/// interrupt handler to overwrite old data.
|
/// interrupt handler to overwrite old data.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mut rx: Rx<Uart>,
|
mut rx: Rx<Uart>,
|
||||||
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
queue: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
rx.disable_interrupts();
|
rx.disable_interrupts();
|
||||||
rx.disable();
|
rx.disable();
|
||||||
@ -379,44 +377,25 @@ impl<Uart: Instance, const N: usize> RxAsyncOverwriting<Uart, N> {
|
|||||||
rx.enable_interrupts();
|
rx.enable_interrupts();
|
||||||
rx.enable();
|
rx.enable();
|
||||||
});
|
});
|
||||||
Self(Some(RxAsyncOverwritingInner {
|
Self { rx, queue }
|
||||||
rx,
|
|
||||||
shared_consumer,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
|
||||||
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(mut self) -> Rx<Uart> {
|
|
||||||
self.stop();
|
|
||||||
let inner = self.0.take().unwrap();
|
|
||||||
inner.rx
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> Drop for RxAsyncOverwriting<Uart, N> {
|
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedConsumer<Uart, N> {
|
||||||
fn drop(&mut self) {
|
|
||||||
self.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwriting<Uart, N> {
|
|
||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
// Need to wait for the IRQ to read data and set this flag. If the queue is not
|
||||||
// empty, we can read data immediately.
|
// empty, we can read data immediately.
|
||||||
|
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let queue = self.0.as_ref().unwrap().shared_consumer.borrow(cs);
|
let queue = self.queue.borrow(cs);
|
||||||
if queue.borrow().as_ref().unwrap().len() == 0 {
|
if queue.borrow().as_ref().unwrap().len() == 0 {
|
||||||
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
let _guard = ActiveReadGuard(Uart::IDX as usize);
|
||||||
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<Uart, N>| {
|
let mut handle_data_in_queue = || {
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut();
|
let mut consumer_ref = self.queue.borrow(cs).borrow_mut();
|
||||||
let consumer = consumer_ref.as_mut().unwrap();
|
let consumer = consumer_ref.as_mut().unwrap();
|
||||||
let data_to_read = consumer.len().min(buf.len());
|
let data_to_read = consumer.len().min(buf.len());
|
||||||
for byte in buf.iter_mut().take(data_to_read) {
|
for byte in buf.iter_mut().take(data_to_read) {
|
||||||
@ -426,15 +405,15 @@ impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwrit
|
|||||||
data_to_read
|
data_to_read
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx);
|
let fut = RxFuture::new(&mut self.rx);
|
||||||
// Data is available, so read that data immediately.
|
// Data is available, so read that data immediately.
|
||||||
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
let read_data = handle_data_in_queue();
|
||||||
if read_data > 0 {
|
if read_data > 0 {
|
||||||
return Ok(read_data);
|
return Ok(read_data);
|
||||||
}
|
}
|
||||||
// Await data.
|
// Await data.
|
||||||
let _ = fut.await;
|
let _ = fut.await;
|
||||||
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
|
let read_data = handle_data_in_queue();
|
||||||
Ok(read_data)
|
Ok(read_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,13 @@
|
|||||||
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
|
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
|
||||||
//! This trait allows for asynchronous sending of data streams. Please note that this module does
|
//! This trait allows for asynchronous sending of data streams. Please note that this module does
|
||||||
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
//! not specify/declare the interrupt handlers which must be provided for async support to work.
|
||||||
//! However, it the [on_interrupt_tx] interrupt handler.
|
//! However, it provides two interrupt handlers:
|
||||||
//!
|
//!
|
||||||
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
|
//! - [on_interrupt_uart_a_tx]
|
||||||
//! for a given UART bank.
|
//! - [on_interrupt_uart_b_tx]
|
||||||
|
//!
|
||||||
|
//! Those should be called in ALL user interrupt handlers which handle UART TX interrupts,
|
||||||
|
//! depending on which UARTs are used.
|
||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
@ -27,14 +30,21 @@ static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
|
|||||||
// critical section.
|
// critical section.
|
||||||
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
|
|
||||||
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
|
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
|
||||||
/// UART bank.
|
/// has to call this once in the interrupt handler responsible for UART A TX interrupts for
|
||||||
///
|
/// asynchronous operations to work.
|
||||||
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
|
pub fn on_interrupt_uart_a_tx() {
|
||||||
/// the given UART bank.
|
on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
|
||||||
pub fn on_interrupt_tx(bank: Bank) {
|
}
|
||||||
let uart = unsafe { bank.reg_block() };
|
|
||||||
let idx = bank as usize;
|
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
|
||||||
|
/// has to call this once in the interrupt handler responsible for UART B TX interrupts for
|
||||||
|
/// asynchronous operations to work.
|
||||||
|
pub fn on_interrupt_uart_b_tx() {
|
||||||
|
on_interrupt_uart_tx(unsafe { pac::Uartb::steal() });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
|
||||||
let irq_enb = uart.irq_enb().read();
|
let irq_enb = uart.irq_enb().read();
|
||||||
// IRQ is not related to TX.
|
// IRQ is not related to TX.
|
||||||
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
|
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
|
||||||
@ -44,7 +54,7 @@ pub fn on_interrupt_tx(bank: Bank) {
|
|||||||
let tx_status = uart.txstatus().read();
|
let tx_status = uart.txstatus().read();
|
||||||
let unexpected_overrun = tx_status.wrlost().bit_is_set();
|
let unexpected_overrun = tx_status.wrlost().bit_is_set();
|
||||||
let mut context = critical_section::with(|cs| {
|
let mut context = critical_section::with(|cs| {
|
||||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||||
*context_ref.borrow()
|
*context_ref.borrow()
|
||||||
});
|
});
|
||||||
context.tx_overrun = unexpected_overrun;
|
context.tx_overrun = unexpected_overrun;
|
||||||
@ -57,12 +67,12 @@ pub fn on_interrupt_tx(bank: Bank) {
|
|||||||
uart.enable().modify(|_, w| w.txenable().clear_bit());
|
uart.enable().modify(|_, w| w.txenable().clear_bit());
|
||||||
// Write back updated context structure.
|
// Write back updated context structure.
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||||
*context_ref.borrow_mut() = context;
|
*context_ref.borrow_mut() = context;
|
||||||
});
|
});
|
||||||
// Transfer is done.
|
// Transfer is done.
|
||||||
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
UART_TX_WAKERS[idx].wake();
|
UART_TX_WAKERS[Uart::IDX as usize].wake();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Safety: We documented that the user provided slice must outlive the future, so we convert
|
// Safety: We documented that the user provided slice must outlive the future, so we convert
|
||||||
@ -82,7 +92,7 @@ pub fn on_interrupt_tx(bank: Bank) {
|
|||||||
|
|
||||||
// Write back updated context structure.
|
// Write back updated context structure.
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
|
||||||
*context_ref.borrow_mut() = context;
|
*context_ref.borrow_mut() = context;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
## [v0.5.0] 2025-02-17
|
## [v0.5.0] 2025-02-14
|
||||||
|
|
||||||
- Re-generated PAC with `svd2rust` v0.35.0 and added optional `defmt` and `Debug` implementations
|
- Re-generated PAC with `svd2rust` v0.35.0 and added optional `defmt` and `Debug` implementations
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ bitfield = ">=0.17, <=0.18"
|
|||||||
max116xx-10bit = "0.3"
|
max116xx-10bit = "0.3"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
path = "../va108xx-hal"
|
|
||||||
version = "0.9"
|
version = "0.9"
|
||||||
features = ["rt"]
|
features = ["rt"]
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
|
||||||
use embedded_hal::spi::{SpiBus, MODE_3};
|
use embedded_hal::spi::{SpiBus, MODE_3};
|
||||||
|
use embedded_hal::{delay::DelayNs, digital::OutputPin};
|
||||||
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 va108xx_hal::spi::SpiClkConfig;
|
use va108xx_hal::spi::SpiClkConfig;
|
||||||
@ -41,7 +41,9 @@ fn main() -> ! {
|
|||||||
|
|
||||||
// Need to set the ADC chip select low
|
// Need to set the ADC chip select low
|
||||||
let mut adc_cs = pinsa.pa17.into_push_pull_output();
|
let mut adc_cs = pinsa.pa17.into_push_pull_output();
|
||||||
adc_cs.set_high();
|
adc_cs
|
||||||
|
.set_high()
|
||||||
|
.expect("Setting ADC chip select high failed");
|
||||||
|
|
||||||
let spi_cfg = SpiConfig::default()
|
let spi_cfg = SpiConfig::default()
|
||||||
.clk_cfg(
|
.clk_cfg(
|
||||||
|
@ -43,16 +43,18 @@ fn main() -> ! {
|
|||||||
|
|
||||||
// Configure an edge interrupt on the button and route it to interrupt vector 15
|
// Configure an edge interrupt on the button and route it to interrupt vector 15
|
||||||
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
let mut button = Button::new(pinsa.pa11.into_floating_input());
|
||||||
|
button.configure_edge_interrupt(
|
||||||
|
edge_irq,
|
||||||
|
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
);
|
||||||
|
|
||||||
if PRESS_MODE == PressMode::Toggle {
|
if PRESS_MODE == PressMode::Toggle {
|
||||||
// This filter debounces the switch for edge based interrupts
|
// This filter debounces the switch for edge based interrupts
|
||||||
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
||||||
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
|
||||||
}
|
}
|
||||||
button.configure_and_enable_edge_interrupt(
|
|
||||||
edge_irq,
|
|
||||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
|
||||||
);
|
|
||||||
|
|
||||||
set_up_ms_tick(
|
set_up_ms_tick(
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
|
use embedded_hal::digital::{OutputPin, StatefulOutputPin};
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::set_up_ms_delay_provider};
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::set_up_ms_delay_provider};
|
||||||
use vorago_reb1::leds::Leds;
|
use vorago_reb1::leds::Leds;
|
||||||
@ -66,19 +67,22 @@ fn main() -> ! {
|
|||||||
let mut led3 = pins.pa6.into_readable_push_pull_output();
|
let mut led3 = pins.pa6.into_readable_push_pull_output();
|
||||||
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
led1.set_low();
|
led1.set_low().ok();
|
||||||
led2.set_low();
|
led2.set_low().ok();
|
||||||
led3.set_low();
|
led3.set_low().ok();
|
||||||
delay.delay_ms(200);
|
delay.delay_ms(200);
|
||||||
led1.set_high();
|
led1.set_high().ok();
|
||||||
led2.set_high();
|
led2.set_high().ok();
|
||||||
led3.set_high();
|
led3.set_high().ok();
|
||||||
delay.delay_ms(200);
|
delay.delay_ms(200);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
led1.toggle();
|
led1.toggle().ok();
|
||||||
delay.delay_ms(200);
|
delay.delay_ms(200);
|
||||||
led2.toggle();
|
led2.toggle().ok();
|
||||||
|
delay.delay_ms(200);
|
||||||
|
// Alternatively use deidscted register.
|
||||||
|
led3.toggle_with_toggle_reg();
|
||||||
delay.delay_ms(200);
|
delay.delay_ms(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,13 @@
|
|||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::digital::OutputPin;
|
||||||
use embedded_hal::spi::{SpiBus, SpiDevice, MODE_0};
|
use embedded_hal::spi::{SpiBus, SpiDevice, MODE_0};
|
||||||
use embedded_hal::{delay::DelayNs, spi};
|
use embedded_hal::{delay::DelayNs, spi};
|
||||||
use max116xx_10bit::VoltageRefMode;
|
use max116xx_10bit::VoltageRefMode;
|
||||||
use max116xx_10bit::{AveragingConversions, AveragingResults};
|
use max116xx_10bit::{AveragingConversions, AveragingResults};
|
||||||
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 va108xx_hal::gpio::Port;
|
|
||||||
use va108xx_hal::spi::{OptionalHwCs, SpiClkConfig};
|
use va108xx_hal::spi::{OptionalHwCs, SpiClkConfig};
|
||||||
use va108xx_hal::timer::CountdownTimer;
|
use va108xx_hal::timer::CountdownTimer;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
@ -24,7 +24,7 @@ use va108xx_hal::{
|
|||||||
spi::{Spi, SpiBase, SpiConfig},
|
spi::{Spi, SpiBase, SpiConfig},
|
||||||
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, InterruptConfig},
|
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, InterruptConfig},
|
||||||
};
|
};
|
||||||
use va108xx_hal::{port_function_select, FunSel};
|
use va108xx_hal::{port_function_select, FunSel, PortSel};
|
||||||
use vorago_reb1::max11619::{
|
use vorago_reb1::max11619::{
|
||||||
max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup,
|
max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup,
|
||||||
max11619_internally_clocked, EocPin, AN2_CHANNEL, POTENTIOMETER_CHANNEL,
|
max11619_internally_clocked, EocPin, AN2_CHANNEL, POTENTIOMETER_CHANNEL,
|
||||||
@ -135,14 +135,16 @@ fn main() -> ! {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if MUX_MODE == MuxMode::PortB19to17 {
|
if MUX_MODE == MuxMode::PortB19to17 {
|
||||||
port_function_select(&mut dp.ioconfig, Port::B, 19, FunSel::Sel1).ok();
|
port_function_select(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok();
|
||||||
port_function_select(&mut dp.ioconfig, Port::B, 18, FunSel::Sel2).ok();
|
port_function_select(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok();
|
||||||
port_function_select(&mut dp.ioconfig, Port::B, 17, FunSel::Sel1).ok();
|
port_function_select(&mut dp.ioconfig, PortSel::PortB, 17, FunSel::Sel1).ok();
|
||||||
port_function_select(&mut dp.ioconfig, Port::B, 16, FunSel::Sel1).ok();
|
port_function_select(&mut dp.ioconfig, PortSel::PortB, 16, FunSel::Sel1).ok();
|
||||||
}
|
}
|
||||||
// Set the accelerometer chip select low in case the board slot is populated
|
// Set the accelerometer chip select low in case the board slot is populated
|
||||||
let mut accel_cs = pinsa.pa16.into_push_pull_output();
|
let mut accel_cs = pinsa.pa16.into_push_pull_output();
|
||||||
accel_cs.set_high();
|
accel_cs
|
||||||
|
.set_high()
|
||||||
|
.expect("Setting accelerometer chip select high failed");
|
||||||
|
|
||||||
let spi = Spi::new(
|
let spi = Spi::new(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
//!
|
//!
|
||||||
//! - [Button Blinky with IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
|
//! - [Button Blinky with IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
|
||||||
//! - [Button Blinky with IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
//! - [Button Blinky with IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
||||||
|
use embedded_hal::digital::InputPin;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11},
|
gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11},
|
||||||
InterruptConfig,
|
pac, InterruptConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -19,32 +20,36 @@ impl Button {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pressed(&mut self) -> bool {
|
pub fn pressed(&mut self) -> bool {
|
||||||
self.0.is_low()
|
self.0.is_low().ok().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn released(&mut self) -> bool {
|
pub fn released(&mut self) -> bool {
|
||||||
self.0.is_high()
|
self.0.is_high().ok().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures an IRQ on edge.
|
/// Configures an IRQ on edge.
|
||||||
pub fn configure_and_enable_edge_interrupt(
|
pub fn configure_edge_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
edge_type: InterruptEdge,
|
edge_type: InterruptEdge,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) {
|
) {
|
||||||
self.0.configure_edge_interrupt(edge_type);
|
self.0
|
||||||
self.0.enable_interrupt(irq_cfg);
|
.configure_edge_interrupt(edge_type, irq_cfg, syscfg, irqsel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures an IRQ on level.
|
/// Configures an IRQ on level.
|
||||||
pub fn configure_and_enable_level_interrupt(
|
pub fn configure_level_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
level: InterruptLevel,
|
level: InterruptLevel,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: InterruptConfig,
|
||||||
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) {
|
) {
|
||||||
self.0.configure_level_interrupt(level);
|
self.0
|
||||||
self.0.enable_interrupt(irq_cfg);
|
.configure_level_interrupt(level, irq_cfg, syscfg, irqsel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures a filter on the button. This can be useful for debouncing the switch.
|
/// Configures a filter on the button. This can be useful for debouncing the switch.
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
//! - [LED example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-leds.rs)
|
//! - [LED example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-leds.rs)
|
||||||
//! - [Button Blinky using IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
|
//! - [Button Blinky using IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs)
|
||||||
//! - [Button Blinky using IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
//! - [Button Blinky using IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs)
|
||||||
|
use embedded_hal::digital::OutputPin;
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{
|
||||||
gpio::dynpin::DynPin,
|
gpio::dynpin::DynPin,
|
||||||
gpio::pin::{Pin, PushPullOutput, PA10, PA6, PA7},
|
gpio::pin::{Pin, PushPullOutput, PA10, PA6, PA7},
|
||||||
@ -84,6 +85,6 @@ impl Led {
|
|||||||
/// Toggles the LED
|
/// Toggles the LED
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn toggle(&mut self) {
|
pub fn toggle(&mut self) {
|
||||||
self.0.toggle().ok();
|
self.0.toggle_with_toggle_reg().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user