1 Commits

Author SHA1 Message Date
063b307701 Bugfix and improvements for async GPIO 2025-02-14 17:46:29 +01:00
43 changed files with 1568 additions and 1287 deletions

View File

@ -148,13 +148,3 @@ example.
The Segger RTT viewer can be used to display log messages received from the target. The base The Segger RTT viewer can be used to display log messages received from the target. The base
address for the RTT block placement is 0x10000000. It is recommended to use a search range of address for the RTT block placement is 0x10000000. It is recommended to use a search range of
0x1000 around that base address when using the RTT viewer. 0x1000 around that base address when using the RTT viewer.
## Learning (Embedded) Rust
If you are unfamiliar with Rust on Embedded Systems or Rust in general, the following resources
are recommended:
- [Rust Book](https://doc.rust-lang.org/book/)
- [Embedded Rust Book](https://docs.rust-embedded.org/book/)
- [Embedded Rust Discovery](https://docs.rust-embedded.org/discovery/microbit/)
- [Awesome Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust)

View File

@ -14,6 +14,6 @@ embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.11" path = "../va108xx-hal"
features = ["rt"] features = ["rt"]

View File

@ -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);
} }
} }

View File

@ -15,10 +15,12 @@ num_enum = { version = "0.7", default-features = false }
static_assertions = "1" static_assertions = "1"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.11" version = "0.9"
# path = "../va108xx-hal"
[dependencies.vorago-reb1] [dependencies.vorago-reb1]
version = "0.8" version = "0.7"
# path = "../vorago-reb1"
[features] [features]
default = [] default = []

View File

@ -27,8 +27,10 @@ version = "1"
features = ["cortex-m-systick"] features = ["cortex-m-systick"]
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.10" version = "0.6"
path = "../va108xx-hal"
features = ["rt", "defmt"] features = ["rt", "defmt"]
[dependencies.va108xx] [dependencies.va108xx]
version = "0.5" version = "0.3"
path = "../va108xx"

View File

@ -27,8 +27,8 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt" "executor-interrupt"
]} ]}
va108xx-hal = { version = "0.11" } va108xx-hal = { version = "0.9", path = "../../va108xx-hal" }
va108xx-embassy = { version = "0.2" } va108xx-embassy = { version = "0.1", path = "../../va108xx-embassy" }
[features] [features]
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"] default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]

View File

@ -9,11 +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_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},
@ -64,13 +67,15 @@ async fn main(spawner: Spawner) {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va108xx_embassy::init( unsafe {
&mut dp.sysconfig, embassy::init(
&dp.irqsel, &mut dp.sysconfig,
SYSCLK_FREQ, &dp.irqsel,
dp.tim23, SYSCLK_FREQ,
dp.tim22, dp.tim23,
); dp.tim22,
)
};
let porta = PinsA::new(&mut dp.sysconfig, dp.porta); let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let portb = PinsB::new(&mut dp.sysconfig, dp.portb); let portb = PinsB::new(&mut dp.sysconfig, dp.portb);
@ -112,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;
} }
} }
@ -245,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();
} }

View File

@ -18,19 +18,21 @@ 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};
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_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
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,
}; };
@ -55,13 +57,15 @@ async fn main(spawner: Spawner) {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va108xx_embassy::init( unsafe {
&mut dp.sysconfig, embassy::init(
&dp.irqsel, &mut dp.sysconfig,
SYSCLK_FREQ, &dp.irqsel,
dp.tim23, SYSCLK_FREQ,
dp.tim22, dp.tim23,
); dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta); let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output(); let mut led0 = porta.pa10.into_readable_push_pull_output();
@ -102,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!(
@ -124,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());
@ -145,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 {
@ -158,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 {

View File

@ -12,14 +12,16 @@
#![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};
use va108xx_embassy::embassy;
use va108xx_hal::{ 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,
}; };
@ -41,13 +43,15 @@ async fn main(_spawner: Spawner) {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here. // Safety: Only called once here.
va108xx_embassy::init( unsafe {
&mut dp.sysconfig, embassy::init(
&dp.irqsel, &mut dp.sysconfig,
SYSCLK_FREQ, &dp.irqsel,
dp.tim23, SYSCLK_FREQ,
dp.tim22, dp.tim23,
); dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta); let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output(); let mut led0 = porta.pa10.into_readable_push_pull_output();
@ -71,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
@ -89,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();
} }

View File

@ -2,8 +2,10 @@
#![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;
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "custom-irqs")] { if #[cfg(feature = "custom-irqs")] {
@ -26,25 +28,27 @@ async fn main(_spawner: Spawner) {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here. // Safety: Only called once here.
cfg_if::cfg_if! { unsafe {
if #[cfg(not(feature = "custom-irqs"))] { cfg_if::cfg_if! {
va108xx_embassy::init( if #[cfg(not(feature = "custom-irqs"))] {
&mut dp.sysconfig, embassy::init(
&dp.irqsel, &mut dp.sysconfig,
SYSCLK_FREQ, &dp.irqsel,
dp.tim23, SYSCLK_FREQ,
dp.tim22, dp.tim23,
); dp.tim22,
} else { );
va108xx_embassy::init_with_custom_irqs( } else {
&mut dp.sysconfig, embassy::init_with_custom_irqs(
&dp.irqsel, &mut dp.sysconfig,
SYSCLK_FREQ, &dp.irqsel,
dp.tim23, SYSCLK_FREQ,
dp.tim22, dp.tim23,
pac::Interrupt::OC23, dp.tim22,
pac::Interrupt::OC24, pac::Interrupt::OC23,
); pac::Interrupt::OC24,
);
}
} }
} }
@ -56,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();
} }
} }

View File

@ -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.11" } va108xx-hal = "0.9"
vorago-reb1 = { version = "0.8" } vorago-reb1 = "0.7"

View File

@ -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(),

View File

@ -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;
} }
} }

View File

@ -16,8 +16,8 @@ embedded-io = "0.6"
cortex-m-semihosting = "0.5.0" cortex-m-semihosting = "0.5.0"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.11" version = "0.9"
features = ["rt", "defmt"] features = ["rt", "defmt"]
[dependencies.vorago-reb1] [dependencies.vorago-reb1]
version = "0.8" version = "0.7"

View File

@ -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);
} }
} }

View File

@ -45,6 +45,9 @@ fn main() -> ! {
.expect("TX send error"); .expect("TX send error");
} }
Err(nb::Error::WouldBlock) => (), Err(nb::Error::WouldBlock) => (),
Err(nb::Error::Other(uart_error)) => {
rprintln!("UART receive error {:?}", uart_error);
}
} }
} }
} }

View File

@ -29,7 +29,9 @@ rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
rtic-sync = {version = "1", features = ["defmt-03"]} rtic-sync = {version = "1", features = ["defmt-03"]}
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = "0.11" version = "0.9"
# path = "../va108xx-hal"
[dependencies.vorago-reb1] [dependencies.vorago-reb1]
version = "0.8" version = "0.7"
# path = "../vorago-reb1"

View File

@ -11,7 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
rtt-target = { version = "0.5" } rtt-target = { version = "0.5" }
cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
embedded-hal = "1" embedded-hal = "1"
va108xx-hal = { version = "0.10.0" } va108xx-hal = { path = "../../va108xx-hal" }
[profile.dev] [profile.dev]
codegen-units = 1 codegen-units = 1

View File

@ -11,7 +11,7 @@ panic-rtt-target = { version = "0.1.3" }
rtt-target = { version = "0.5" } rtt-target = { version = "0.5" }
cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
embedded-hal = "1" embedded-hal = "1"
va108xx-hal = { version = "0.10.0" } va108xx-hal = { path = "../../va108xx-hal" }
[profile.dev] [profile.dev]
codegen-units = 1 codegen-units = 1

View File

@ -8,16 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.2.1] 2025-03-07
- Bumped allowed va108xx-hal to v0.11
## [v0.2.0] 2025-02-17
- Bumped va108xx-hal to v0.10.0
- Remove `embassy` module, expose public functions in library root directly
## [v0.1.2] and [v0.1.1] 2025-02-13 ## [v0.1.2] and [v0.1.1] 2025-02-13
Docs patch Docs patch
@ -25,7 +15,3 @@ Docs patch
## [v0.1.0] 2025-02-13 ## [v0.1.0] 2025-02-13
Initial release Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.1...HEAD
[v0.2.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.0...va10xx-embassy-v0.2.1
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.1.2...va10xx-embassy-v0.2.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va108xx-embassy" name = "va108xx-embassy"
version = "0.2.1" version = "0.1.2"
edition = "2021" edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers" description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
@ -20,7 +20,7 @@ embassy-time-queue-utils = "0.1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] } once_cell = { version = "1", default-features = false, features = ["critical-section"] }
va108xx-hal = { version = ">=0.10, <=0.11" } va108xx-hal = { version = "0.9", path = "../va108xx-hal" }
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies] [target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] } portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }

View File

@ -1,17 +1,17 @@
//! # Embassy-rs support for the Vorago VA108xx MCU family //! # Embassy-rs support for the Vorago VA108xx MCU family
//! //!
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for //! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
//! the VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses //! VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
//! the TIM peripherals provided by the VA108xx family for this purpose. //! peripherals provided by the VA108xx family for this purpose.
//! //!
//! ## Usage //! ## Usage
//! //!
//! This library exposes the [init] or the [init_with_custom_irqs] functions which set up the time //! This library only exposes the [embassy::init] method which sets up the time driver. This
//! driver. This function must be called once at the start of the application. //! function must be called once at the start of the application.
//! //!
//! This implementation requires two TIM peripherals provided by the VA108xx device. //! This implementation requires two TIM peripherals provided by the VA108xx device.
//! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances //! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
//! into the [init_with_custom_irqs] and [init] method. //! into the [embassy::init_with_custom_irqs] and [embassy::init] method.
//! //!
//! The application also requires two interrupt handlers to handle the timekeeper and alarm //! The application also requires two interrupt handlers to handle the timekeeper and alarm
//! interrupts. By default, this library will define the interrupt handler inside the library //! interrupts. By default, this library will define the interrupt handler inside the library
@ -24,7 +24,7 @@
//! You can disable the default features and then specify one of the features above to use the //! You can disable the default features and then specify one of the features above to use the
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and //! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the //! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! application code. If this is done, [init_with_custom_irqs] must be used //! application code. If this is done, [embassy::init_with_custom_irqs] must be used
//! method to pass the IRQ numbers to the library. //! method to pass the IRQ numbers to the library.
//! //!
//! ## Examples //! ## Examples
@ -76,7 +76,7 @@ macro_rules! embassy_time_driver_irqs {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn $timekeeper_irq() { fn $timekeeper_irq() {
// Safety: We call it once here. // Safety: We call it once here.
unsafe { $crate::time_driver().on_interrupt_timekeeping() } unsafe { $crate::embassy::time_driver().on_interrupt_timekeeping() }
} }
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq; const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
@ -85,7 +85,7 @@ macro_rules! embassy_time_driver_irqs {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn $alarm_irq() { fn $alarm_irq() {
// Safety: We call it once here. // Safety: We call it once here.
unsafe { $crate::time_driver().on_interrupt_alarm() } unsafe { $crate::embassy::time_driver().on_interrupt_alarm() }
} }
}; };
} }
@ -99,58 +99,71 @@ embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
#[cfg(feature = "irq-oc28-oc29")] #[cfg(feature = "irq-oc28-oc29")]
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28); embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
/// Expose the time driver so the user can specify the IRQ handlers themselves. pub mod embassy {
pub fn time_driver() -> &'static TimerDriver { use super::*;
&TIME_DRIVER use va108xx_hal::{pac, timer::TimRegInterface};
}
/// Initialization method for embassy. /// Expose the time driver so the user can specify the IRQ handlers themselves.
/// pub fn time_driver() -> &'static TimerDriver {
/// This should be used if the interrupt handler is provided by the library, which is the &TIME_DRIVER
/// default case. }
#[cfg(feature = "irqs-in-lib")]
pub fn init<TimekeeperTim: TimRegInterface + ValidTim, AlarmTim: TimRegInterface + ValidTim>(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
) {
TIME_DRIVER.init(
syscfg,
irqsel,
sysclk,
timekeeper_tim,
alarm_tim,
TIMEKEEPER_IRQ,
ALARM_IRQ,
)
}
/// Initialization method for embassy when using custom IRQ handlers. /// Initialization method for embassy
/// ///
/// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs. /// # Safety
pub fn init_with_custom_irqs< ///
TimekeeperTim: TimRegInterface + ValidTim, /// This has to be called once at initialization time to initiate the time driver for
AlarmTim: TimRegInterface + ValidTim, /// embassy.
>( #[cfg(feature = "irqs-in-lib")]
syscfg: &mut pac::Sysconfig, pub unsafe fn init<
irqsel: &pac::Irqsel, TimekeeperTim: TimRegInterface + ValidTim,
sysclk: impl Into<Hertz>, AlarmTim: TimRegInterface + ValidTim,
timekeeper_tim: TimekeeperTim, >(
alarm_tim: AlarmTim, syscfg: &mut pac::Sysconfig,
timekeeper_irq: pac::Interrupt, irqsel: &pac::Irqsel,
alarm_irq: pac::Interrupt, sysclk: impl Into<Hertz>,
) { timekeeper_tim: TimekeeperTim,
TIME_DRIVER.init( alarm_tim: AlarmTim,
syscfg, ) {
irqsel, TIME_DRIVER.init(
sysclk, syscfg,
timekeeper_tim, irqsel,
alarm_tim, sysclk,
timekeeper_irq, timekeeper_tim,
alarm_irq, alarm_tim,
) TIMEKEEPER_IRQ,
ALARM_IRQ,
)
}
/// Initialization method for embassy
///
/// # Safety
///
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
pub unsafe fn init_with_custom_irqs<
TimekeeperTim: TimRegInterface + ValidTim,
AlarmTim: TimRegInterface + ValidTim,
>(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: TimekeeperTim,
alarm_tim: AlarmTim,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
TIME_DRIVER.init(
syscfg,
irqsel,
sysclk,
timekeeper_tim,
alarm_tim,
timekeeper_irq,
alarm_irq,
)
}
} }
struct AlarmState { struct AlarmState {

View File

@ -8,25 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.11.1] 2025-03-10 ## [v0.10.0]
## Fixed
- Fix `embedded_io` UART implementation to implement the documented contract properly.
The implementation will now block until at least one byte is available or can be written, unless
the send or receive buffer is empty.
## [v0.11.0] 2025-03-07
## Changed
- Bugfix for I2C `TimingCfg::reg`
- Simplified UART error handling. All APIs are now infallible because writing to a FIFO or
reading from a FIFO never fails. Users can either poll errors using `Rx::poll_errors` or
`Uart::poll_rx_errors` / `UartBase::poll_rx_errors`, or detect errors using the provided
interrupt handlers.
## [v0.10.0] 2025-02-17
## Added ## Added
@ -34,19 +16,16 @@ 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
- Bug in async GPIO interrupt handler where all enabled interrupts, even the ones which might - Bug in async GPIO interrupt handler where all enabled interrupts, even the ones which might
be unrelated to the pin, were disabled. be unrelated to the pin, were disabled.
## [v0.9.0] 2025-02-13 ## [v0.9.0]
## Fixed ## Fixed
@ -122,14 +101,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Updated `embedded-hal` to v1 - Updated `embedded-hal` to v1
- Added optional `defmt` v0.3 feature and support. - Added optional `defmt` v0.3 feature and support.
## v0.5.2 2024-06-16 ## [v0.5.2] 2024-06-16
## Fixed ## Fixed
- Replaced usage to `ptr::write_volatile` in UART module which is denied on more recent Rust - Replaced usage to `ptr::write_volatile` in UART module which is denied on more recent Rust
compilers. compilers.
## v0.5.1 ## [v0.5.1]
### Changes ### Changes
@ -138,7 +117,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `once_cell` to 1.12.0 - `once_cell` to 1.12.0
- Other dependencies: Only revision has changed - Other dependencies: Only revision has changed
## v0.5.0 ## [v0.5.0]
### Added ### Added
@ -151,14 +130,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Bugfix in UART code where RX and TX could not be enabled or disabled independently - Bugfix in UART code where RX and TX could not be enabled or disabled independently
## v0.4.3 ## [v0.4.3]
- Various smaller fixes for READMEs, update of links in documentation - Various smaller fixes for READMEs, update of links in documentation
- Simplified CI for github, do not use `cross` - Simplified CI for github, do not use `cross`
- New `blinky-pac` example - New `blinky-pac` example
- Use HAL delay in `blinky` example - Use HAL delay in `blinky` example
## v0.4.2 ## [v0.4.2]
### Added ### Added
@ -168,24 +147,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Clear TX and RX FIFO in SPI transfer function - Clear TX and RX FIFO in SPI transfer function
## v0.4.1 ## [v0.4.1]
### Fixed ### Fixed
- Initial blockmode setting was not set in SPI constructor - Initial blockmode setting was not set in SPI constructor
## v0.4.0 ## [v0.4.0]
### Changed ### Changed
- Replaced `Hertz` by `impl Into<Hertz>` completely and removed - Replaced `Hertz` by `impl Into<Hertz>` completely and removed
`+ Copy` where not necessary `+ Copy` where not necessary
## v0.3.1 ## [v0.3.1]
- Updated all links to point to new repository - Updated all links to point to new repository
## v0.3.0 ## [v0.3.0]
### Added ### Added
@ -197,7 +176,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Primary repository now hosted on IRS external git: https://egit.irs.uni-stuttgart.de/rust/va108xx-hal - Primary repository now hosted on IRS external git: https://egit.irs.uni-stuttgart.de/rust/va108xx-hal
- Relicensed as Apache-2.0 - Relicensed as Apache-2.0
## v0.2.3 ## [0.2.3]
### Added ### Added
@ -209,7 +188,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Improved Timer API. It is now possible to simply use `new` on `CountDownTimer` - Improved Timer API. It is now possible to simply use `new` on `CountDownTimer`
## v0.2.2 ## [0.2.2]
### Added ### Added
@ -221,7 +200,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- API which expects values in Hertz now uses `impl Into<Hertz>` as input parameter - API which expects values in Hertz now uses `impl Into<Hertz>` as input parameter
## v0.2.1 ## [0.2.1]
### Added ### Added
@ -235,7 +214,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Moved the `FilterClkSel` struct to the `clock` module, re-exporting in `gpio` - Moved the `FilterClkSel` struct to the `clock` module, re-exporting in `gpio`
- Clearing output state at initialization of Output pins - Clearing output state at initialization of Output pins
## v0.2.0 ## [0.2.0]
### Changed ### Changed
@ -250,7 +229,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Some bugfixes for GPIO implementation - Some bugfixes for GPIO implementation
- Rust edition updated to 2021 - Rust edition updated to 2021
## v0.1.0 ## [0.1.0]
### Added ### Added
@ -259,12 +238,3 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- RTT example application - RTT example application
- Added basic test binary in form of an example - Added basic test binary in form of an example
- README with basic instructions how to set up own binary crate - README with basic instructions how to set up own binary crate
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...HEAD
[v0.11.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...va108xx-hal-v0.11.1
[v0.11.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.10.0...va108xx-hal-v0.11.0
[v0.10.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.9.0...va108xx-hal-v0.10.0
[v0.9.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.8.0...va108xx-hal-v0.9.0
[v0.8.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.7.0...va108xx-hal-v0.8.0
[v0.7.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.6.0...va108xx-hal-v0.7.0
[v0.6.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/tag/va108xx-hal-v0.6.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va108xx-hal" name = "va108xx-hal"
version = "0.11.1" version = "0.9.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers" description = "HAL for the Vorago VA108xx family of microcontrollers"
@ -28,8 +28,8 @@ heapless = "0.8"
static_cell = "2" static_cell = "2"
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
void = { version = "1", default-features = false } void = { version = "1", default-features = false }
once_cell = { version = "1", default-features = false } once_cell = {version = "1", default-features = false }
va108xx = { version = "0.5", default-features = false, features = ["critical-section", "defmt"] } va108xx = { version = "0.5", path = "../va108xx", default-features = false, features = ["critical-section", "defmt"] }
embassy-sync = "0.6" embassy-sync = "0.6"
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }

View File

@ -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>> {

View File

@ -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()
} }
} }

View File

@ -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;

View File

@ -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
View 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()));
}
}
}
}

View File

@ -198,13 +198,13 @@ impl TimingCfg {
} }
pub fn reg(&self) -> u32 { pub fn reg(&self) -> u32 {
((self.tbuf as u32) << 28) (self.tbuf as u32) << 28
| ((self.thd_sta as u32) << 24) | (self.thd_sta as u32) << 24
| ((self.tsu_sta as u32) << 20) | (self.tsu_sta as u32) << 20
| ((self.tsu_sto as u32) << 16) | (self.tsu_sto as u32) << 16
| ((self.tlow as u32) << 12) | (self.tlow as u32) << 12
| ((self.thigh as u32) << 8) | (self.thigh as u32) << 8
| ((self.tf as u32) << 4) | (self.tf as u32) << 4
| (self.tr as u32) | (self.tr as u32)
} }
} }
@ -376,7 +376,7 @@ impl<I2c: Instance> I2cBase<I2c> {
if let Some(max_words) = max_words { if let Some(max_words) = max_words {
self.i2c self.i2c
.s0_maxwords() .s0_maxwords()
.write(|w| unsafe { w.bits((1 << 31) | max_words as u32) }); .write(|w| unsafe { w.bits(1 << 31 | max_words as u32) });
} }
let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr); let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr);
// The first bit is the read/write value. Normally, both read and write are matched // The first bit is the read/write value. Normally, both read and write are matched
@ -437,7 +437,7 @@ impl<I2c: Instance> I2cBase<I2c> {
let clk_div = self.calc_clk_div(speed_mode)?; let clk_div = self.calc_clk_div(speed_mode)?;
self.i2c self.i2c
.clkscale() .clkscale()
.write(|w| unsafe { w.bits(((speed_mode as u32) << 31) | clk_div as u32) }); .write(|w| unsafe { w.bits((speed_mode as u32) << 31 | clk_div as u32) });
Ok(()) Ok(())
} }

View File

@ -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.

View File

@ -1,12 +1,5 @@
//! # API for the UART peripheral //! # API for the UART peripheral
//! //!
//! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures.
//! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver
//! using interrupts.
//!
//! The [rx_asynch] and [tx_asynch] modules provide an asynchronous non-blocking API for the UART
//! peripheral.
//!
//! ## Examples //! ## Examples
//! //!
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/uart.rs) //! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/uart.rs)
@ -29,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,
@ -71,6 +64,55 @@ pub struct NoInterruptIdWasSet;
#[error("transer is pending")] #[error("transer is pending")]
pub struct TransferPendingError; pub struct TransferPendingError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RxError {
#[error("overrun error")]
Overrun,
#[error("framing error")]
Framing,
#[error("parity error")]
Parity,
}
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
#[error("rx error: {0}")]
Rx(#[from] RxError),
#[error("break condition")]
BreakCondition,
}
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_io::Error for RxError {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_hal_nb::serial::Error for RxError {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match self {
RxError::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
RxError::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
RxError::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
}
}
}
impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match self {
Error::Rx(rx_error) => embedded_hal_nb::serial::Error::kind(rx_error),
Error::BreakCondition => embedded_hal_nb::serial::ErrorKind::Other,
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Event { pub enum Event {
@ -321,7 +363,7 @@ impl UartErrors {
impl UartErrors { impl UartErrors {
#[inline(always)] #[inline(always)]
pub fn error(&self) -> bool { pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity || self.other self.overflow || self.framing || self.parity
} }
} }
@ -339,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.
/// ///
@ -347,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.
/// ///
@ -368,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 _
} }
} }
@ -380,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() },
}
} }
} }
@ -543,27 +572,22 @@ impl<Uart: Instance> UartBase<Uart> {
self.uart self.uart
} }
/// Poll receiver errors.
pub fn poll_rx_errors(&self) -> Option<UartErrors> {
self.rx.poll_errors()
}
pub fn split(self) -> (Tx<Uart>, Rx<Uart>) { pub fn split(self) -> (Tx<Uart>, Rx<Uart>) {
(self.tx, self.rx) (self.tx, self.rx)
} }
} }
impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> { impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> {
type Error = Infallible; type Error = Error;
} }
impl<UartInstance> embedded_hal_nb::serial::ErrorType for UartBase<UartInstance> { impl<UartInstance> embedded_hal_nb::serial::ErrorType for UartBase<UartInstance> {
type Error = Infallible; type Error = Error;
} }
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for UartBase<Uart> { impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for UartBase<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> { fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.rx.read() self.rx.read().map_err(|e| e.map(Error::Rx))
} }
} }
@ -674,11 +698,6 @@ where
self self
} }
/// Poll receiver errors.
pub fn poll_rx_errors(&self) -> Option<UartErrors> {
self.inner.poll_rx_errors()
}
#[inline] #[inline]
pub fn enable_rx(&mut self) { pub fn enable_rx(&mut self) {
self.inner.enable_rx(); self.inner.enable_rx();
@ -775,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.
@ -789,30 +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
}
pub fn poll_errors(&self) -> Option<UartErrors> {
let mut errors = UartErrors::default();
let uart = unsafe { &(*Uart::ptr()) };
let status_reader = uart.rxstatus().read();
if status_reader.rxovr().bit_is_set() {
errors.overflow = true;
} else if status_reader.rxfrm().bit_is_set() {
errors.framing = true;
} else if status_reader.rxpar().bit_is_set() {
errors.parity = true;
} else {
return None;
};
Some(errors)
} }
#[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]
@ -842,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())
@ -858,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> {
@ -867,20 +871,39 @@ impl<Uart: Instance> Rx<Uart> {
#[inline(always)] #[inline(always)]
pub fn release(self) -> Uart { pub fn release(self) -> Uart {
self.0 self.uart
} }
} }
impl<Uart> embedded_io::ErrorType for Rx<Uart> { impl<Uart> embedded_io::ErrorType for Rx<Uart> {
type Error = Infallible; type Error = RxError;
} }
impl<Uart> embedded_hal_nb::serial::ErrorType for Rx<Uart> { impl<Uart> embedded_hal_nb::serial::ErrorType for Rx<Uart> {
type Error = Infallible; type Error = RxError;
} }
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> { impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> { fn read(&mut self) -> nb::Result<u8, Self::Error> {
let uart = unsafe { &(*Uart::ptr()) };
let status_reader = uart.rxstatus().read();
let err = if status_reader.rxovr().bit_is_set() {
Some(RxError::Overrun)
} else if status_reader.rxfrm().bit_is_set() {
Some(RxError::Framing)
} else if status_reader.rxpar().bit_is_set() {
Some(RxError::Parity)
} else {
None
};
if let Some(err) = err {
// The status code is always related to the next bit for the framing
// and parity status bits. We have to read the DATA register
// so that the next status reflects the next DATA word
// For overrun error, we read as well to clear the peripheral
self.read_fifo_unchecked();
return Err(err.into());
}
self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| { self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| {
if let nb::Error::Other(_) = e { if let nb::Error::Other(_) = e {
unreachable!() unreachable!()
@ -895,23 +918,13 @@ impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
if buf.is_empty() { if buf.is_empty() {
return Ok(0); return Ok(0);
} }
let mut read = 0;
loop {
if self.0.rxstatus().read().rdavl().bit_is_set() {
break;
}
}
for byte in buf.iter_mut() { for byte in buf.iter_mut() {
match <Self as embedded_hal_nb::serial::Read<u8>>::read(self) { let w = nb::block!(<Self as embedded_hal_nb::serial::Read<u8>>::read(self))?;
Ok(w) => { *byte = w;
*byte = w;
read += 1;
}
Err(nb::Error::WouldBlock) => break,
}
} }
Ok(read) Ok(buf.len())
} }
} }
@ -946,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
@ -956,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.
@ -970,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.
@ -1018,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);
@ -1033,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> {
@ -1069,20 +1088,14 @@ impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
if buf.is_empty() { if buf.is_empty() {
return Ok(0); return Ok(0);
} }
loop {
if self.0.txstatus().read().wrrdy().bit_is_set() {
break;
}
}
let mut written = 0;
for byte in buf.iter() { for byte in buf.iter() {
match <Self as embedded_hal_nb::serial::Write<u8>>::write(self, *byte) { nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::write(
Ok(_) => written += 1, self, *byte
Err(nb::Error::WouldBlock) => return Ok(written), ))?;
}
} }
Ok(written) Ok(buf.len())
} }
fn flush(&mut self) -> Result<(), Self::Error> { fn flush(&mut self) -> Result<(), Self::Error> {
@ -1122,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]
@ -1200,10 +1213,15 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
// 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 there is data in the FIFO, write it into the reception buffer loop {
while let Ok(byte) = self.0.read_fifo() { // While there is data in the FIFO, write it into the reception buffer
buf[result.bytes_read] = byte as u8; let read_result = self.0.read();
result.bytes_read += 1; if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
buf[result.bytes_read] = byte;
result.bytes_read += 1;
} else {
break;
}
} }
} }
@ -1280,13 +1298,12 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
if context.rx_idx == context.max_len { if context.rx_idx == context.max_len {
break; break;
} }
// While there is data in the FIFO, write it into the reception buffer let read_result = self.0.read();
match self.0.read() { if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
Ok(byte) => { buf[context.rx_idx] = byte;
buf[result.bytes_read] = byte; context.rx_idx += 1;
result.bytes_read += 1; } else {
} break;
Err(_) => break,
} }
} }
self.irq_completion_handler_max_size_timeout(&mut result, context); self.irq_completion_handler_max_size_timeout(&mut result, context);
@ -1305,6 +1322,29 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
Ok(result) Ok(result)
} }
fn read_handler(
&self,
errors: &mut Option<UartErrors>,
read_res: &nb::Result<u8, RxError>,
) -> Option<u8> {
match read_res {
Ok(byte) => Some(*byte),
Err(nb::Error::WouldBlock) => None,
Err(nb::Error::Other(e)) => {
// Ensure `errors` is Some(IrqUartError), initializing if it's None
let err = errors.get_or_insert(UartErrors::default());
// Now we can safely modify fields inside `err`
match e {
RxError::Overrun => err.overflow = true,
RxError::Framing => err.framing = true,
RxError::Parity => err.parity = true,
}
None
}
}
}
fn check_for_errors(&self, errors: &mut Option<UartErrors>) { fn check_for_errors(&self, errors: &mut Option<UartErrors>) {
let rx_status = self.uart().rxstatus().read(); let rx_status = self.uart().rxstatus().read();

View File

@ -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, 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];
@ -46,7 +49,7 @@ impl RxFuture {
} }
impl Future for RxFuture { impl Future for RxFuture {
type Output = Result<(), Infallible>; type Output = Result<(), RxError>;
fn poll( fn poll(
self: core::pin::Pin<&mut Self>, self: core::pin::Pin<&mut Self>,
@ -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)
} }
} }

View File

@ -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;
}); });
} }

View File

@ -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

View File

@ -8,15 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.8.1] 2025-03-07
- Bumped allowed `va108xx-hal` dependency to 0.11
- Bumped `bitfield` dependency
## [v0.8.0] 2025-02-17
- Bumped `va108xx-hal` dependency to 0.10
## [v0.7.0] 2025-02-13 ## [v0.7.0] 2025-02-13
- Bumped `va108xx-hal` dependency to 0.9 - Bumped `va108xx-hal` dependency to 0.9
@ -61,9 +52,3 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added basic accelerometer example. Board in not populated so it is not complete, but - Added basic accelerometer example. Board in not populated so it is not complete, but
it provides a starting point it provides a starting point
- Added ADC base library and example building on the new max116xx-10bit device driver crate - Added ADC base library and example building on the new max116xx-10bit device driver crate
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.8.1...HEAD
[v0.8.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.8.0...vorago-reb1-v0.8.1
[v0.8.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.7.0...vorago-reb1-v0.8.0
[v0.7.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.6.0...vorago-reb1-v0.7.0
[v0.6.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/vorago-reb1-v0.5.0...vorago-reb1-v0.6.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "vorago-reb1" name = "vorago-reb1"
version = "0.8.1" version = "0.7.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "Board Support Crate for the Vorago REB1 development board" description = "Board Support Crate for the Vorago REB1 development board"
@ -15,10 +15,12 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
nb = "1" nb = "1"
bitfield = ">=0.17, <=0.19" bitfield = ">=0.17, <=0.18"
max116xx-10bit = "0.3" max116xx-10bit = "0.3"
va108xx-hal = { version = ">=0.10, <=0.11", features = ["rt"] } [dependencies.va108xx-hal]
version = "0.9"
features = ["rt"]
[features] [features]
rt = ["va108xx-hal/rt"] rt = ["va108xx-hal/rt"]

View File

@ -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(

View File

@ -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),

View File

@ -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);
} }
} }

View File

@ -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,

View File

@ -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.

View File

@ -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();
} }
} }