32 Commits

Author SHA1 Message Date
bd286bdb2a Merge pull request 'small README tweak' (#44) from readme-tweak into main
Reviewed-on: #44
2025-02-13 14:56:48 +01:00
d3cc00a4a5 small README tweak 2025-02-13 14:56:24 +01:00
1018a65447 Merge pull request 'add more defmt features' (#42) from add-more-defmt-features into main
Reviewed-on: #42
2025-02-13 14:52:03 +01:00
0a31b637e6 add more defmt features 2025-02-13 14:51:54 +01:00
6e1ae70054 Merge pull request 'add various debug impls' (#43) from add-various-debug-impls into main
Reviewed-on: #43
2025-02-13 14:51:27 +01:00
8ae2d6189a add various debug impls 2025-02-13 14:50:00 +01:00
549a98dbaf Merge pull request 'all doc fixes' (#41) from doc-fixes into main
Reviewed-on: #41
2025-02-13 12:23:56 +01:00
e24fc608a3 all doc fixes 2025-02-13 11:44:14 +01:00
7b74312013 Merge pull request 'completed async RX support as well' (#39) from async-uart-rx into main
Reviewed-on: #39
2025-02-13 11:33:36 +01:00
417f5b7f67 completed async RX support as well 2025-02-13 11:31:17 +01:00
3e796ef22b Merge pull request 'UART B hotfix' (#40) from uart-b-hotfix into main
Reviewed-on: #40
2025-02-13 11:30:37 +01:00
b145047b95 UART B hotfix 2025-02-13 11:14:39 +01:00
82b4c16f8e Merge pull request 'bump PAC version' (#38) from bump-va108xx-version into main
Reviewed-on: #38
2025-02-12 14:57:53 +01:00
189ac2d256 bump PAC version 2025-02-12 14:47:18 +01:00
c5543d8606 Merge pull request 'add back doc attribute' (#37) from prep-pac-release into main
Reviewed-on: #37
2025-02-12 14:25:28 +01:00
691911d087 add back doc attribute 2025-02-12 14:23:25 +01:00
3953897c48 Merge pull request 'Async UART support' (#33) from async-uart into main
Reviewed-on: #33
2025-02-12 14:15:10 +01:00
6e0d417a5c Async UART TX support 2025-02-12 14:13:35 +01:00
4edba63b02 Merge pull request 'docs fix' (#36) from docs-fix into main
Reviewed-on: #36
2025-02-12 14:11:21 +01:00
bcd79f0f20 docs fix 2025-02-12 14:07:18 +01:00
77608da74e Merge pull request 'dynpin defmt bugfix' (#35) from dynpin-fix-2 into main
Reviewed-on: #35
2025-02-12 14:06:51 +01:00
066d91aee5 dynpin defmt bugfix 2025-02-12 14:06:41 +01:00
e869355960 Merge pull request 'Regenerate PAC' (#29) from regenerate-pac into main
Reviewed-on: #29
2025-02-12 14:05:06 +01:00
99631dbd03 va108xx v0.4.0: Regnerate PAC 2025-02-12 14:04:56 +01:00
698ed3a700 Merge pull request 'dynpin bugfix' (#34) from bugfix-dynpin into main
Reviewed-on: #34
2025-02-12 13:41:34 +01:00
f3d840ace7 dynpin bugfix 2025-02-12 13:41:06 +01:00
f781505ec5 Merge pull request 'Asynchronous GPIO support' (#30) from async-gpio into main
Reviewed-on: #30
2025-02-11 18:56:28 +01:00
c6e840a991 Asynchronous GPIO support 2025-02-11 18:56:11 +01:00
454635a473 Merge pull request 'update error handling' (#31) from update-error-types into main
Reviewed-on: #31
2025-02-11 16:19:47 +01:00
67ddba9c42 update error handling 2025-02-11 16:19:20 +01:00
6efc902e02 Merge pull request 'disable the unittests' (#32) from disable-empty-unittests into main
Reviewed-on: #32
2025-02-11 16:18:31 +01:00
b6e9a7f68e disable the unittests 2025-02-10 17:13:03 +01:00
48 changed files with 2442 additions and 572 deletions

View File

@ -17,7 +17,7 @@ use va108xx_hal::{
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
time::Hertz, time::Hertz,
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, IrqCfg}, timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, InterruptConfig},
}; };
#[allow(dead_code)] #[allow(dead_code)]
@ -44,8 +44,8 @@ fn main() -> ! {
rprintln!("-- VA108xx Test Application --"); rprintln!("-- VA108xx Test Application --");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap(); let cp = cortex_m::Peripherals::take().unwrap();
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb); let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb);
let mut led1 = pinsa.pa10.into_readable_push_pull_output(); let mut led1 = pinsa.pa10.into_readable_push_pull_output();
let test_case = TestCase::DelayMs; let test_case = TestCase::DelayMs;
@ -155,7 +155,7 @@ fn main() -> ! {
} }
TestCase::DelayMs => { TestCase::DelayMs => {
let mut ms_timer = set_up_ms_tick( let mut ms_timer = set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true), InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),

View File

@ -8,6 +8,11 @@ cfg-if = "1"
cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
embedded-hal-async = "1"
embedded-io = "0.6"
embedded-io-async = "0.6"
heapless = "0.8"
static_cell = "2"
rtt-target = "0.6" rtt-target = "0.6"
panic-rtt-target = "0.2" panic-rtt-target = "0.2"

View File

@ -0,0 +1,258 @@
//! This example demonstrates the usage of async GPIO operations on VA108xx.
//!
//! You need to tie the PA0 to the PA1 pin for this example to work. You can optionally tie the PB22 to PB23 pins well
//! and then set the `CHECK_PB22_TO_PB23` to true to also test async operations on Port B.
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_sync::channel::{Receiver, Sender};
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
use embassy_time::{Duration, Instant, Timer};
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
use embedded_hal_async::digital::Wait;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::gpio::{on_interrupt_for_asynch_gpio, InputDynPinAsync, InputPinAsync, PinsB};
use va108xx_hal::{
gpio::{DynPin, PinsA},
pac::{self, interrupt},
prelude::*,
};
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
const CHECK_PA0_TO_PA1: bool = true;
const CHECK_PB22_TO_PB23: bool = false;
// Can also be set to OC10 and works as well.
const PB22_TO_PB23_IRQ: pac::Interrupt = pac::Interrupt::OC11;
#[derive(Clone, Copy)]
pub struct GpioCmd {
cmd_type: GpioCmdType,
after_delay: u32,
}
impl GpioCmd {
pub fn new(cmd_type: GpioCmdType, after_delay: u32) -> Self {
Self {
cmd_type,
after_delay,
}
}
}
#[derive(Clone, Copy)]
pub enum GpioCmdType {
SetHigh,
SetLow,
RisingEdge,
FallingEdge,
}
// Declare a bounded channel of 3 u32s.
static CHANNEL_PA0_PA1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
static CHANNEL_PB22_TO_PB23: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
#[embassy_executor::main]
async fn main(spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async GPIO Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here.
unsafe {
embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
)
};
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let portb = PinsB::new(&mut dp.sysconfig, dp.portb);
let mut led0 = porta.pa10.into_readable_push_pull_output();
let out_pa0 = porta.pa0.into_readable_push_pull_output();
let in_pa1 = porta.pa1.into_floating_input();
let out_pb22 = portb.pb22.into_readable_push_pull_output();
let in_pb23 = portb.pb23.into_floating_input();
let in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
let out_pa0_dyn = out_pa0.downgrade();
let in_pb23_async = InputDynPinAsync::new(in_pb23.downgrade(), PB22_TO_PB23_IRQ).unwrap();
let out_pb22_dyn = out_pb22.downgrade();
spawner
.spawn(output_task(
"PA0 to PA1",
out_pa0_dyn,
CHANNEL_PA0_PA1.receiver(),
))
.unwrap();
spawner
.spawn(output_task(
"PB22 to PB23",
out_pb22_dyn,
CHANNEL_PB22_TO_PB23.receiver(),
))
.unwrap();
if CHECK_PA0_TO_PA1 {
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), in_pa1_async).await;
rprintln!("Example PA0 to PA1 done");
}
if CHECK_PB22_TO_PB23 {
check_pin_to_pin_async_ops("PB22 to PB23", CHANNEL_PB22_TO_PB23.sender(), in_pb23_async)
.await;
rprintln!("Example PB22 to PB23 done");
}
rprintln!("Example done, toggling LED0");
loop {
led0.toggle().unwrap();
Timer::after(Duration::from_millis(500)).await;
}
}
async fn check_pin_to_pin_async_ops(
ctx: &'static str,
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
mut async_input: impl Wait,
) {
rprintln!(
"{}: sending SetHigh command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
async_input.wait_for_high().await.unwrap();
rprintln!(
"{}: Input pin is high now ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending SetLow command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
async_input.wait_for_low().await.unwrap();
rprintln!(
"{}: Input pin is low now ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending RisingEdge command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
async_input.wait_for_rising_edge().await.unwrap();
rprintln!(
"{}: input pin had rising edge ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending Falling command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
.await;
async_input.wait_for_falling_edge().await.unwrap();
rprintln!(
"{}: input pin had a falling edge ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending Falling command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
.await;
async_input.wait_for_any_edge().await.unwrap();
rprintln!(
"{}: input pin had a falling (any) edge ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending Falling command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
async_input.wait_for_any_edge().await.unwrap();
rprintln!(
"{}: input pin had a rising (any) edge ({} ms)",
ctx,
Instant::now().as_millis()
);
}
#[embassy_executor::task(pool_size = 2)]
async fn output_task(
ctx: &'static str,
mut out: DynPin,
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
) {
loop {
let next_cmd = receiver.receive().await;
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
match next_cmd.cmd_type {
GpioCmdType::SetHigh => {
rprintln!("{}: Set output high", ctx);
out.set_high().unwrap();
}
GpioCmdType::SetLow => {
rprintln!("{}: Set output low", ctx);
out.set_low().unwrap();
}
GpioCmdType::RisingEdge => {
rprintln!("{}: Rising edge", ctx);
if !out.is_low().unwrap() {
out.set_low().unwrap();
}
out.set_high().unwrap();
}
GpioCmdType::FallingEdge => {
rprintln!("{}: Falling edge", ctx);
if !out.is_high().unwrap() {
out.set_high().unwrap();
}
out.set_low().unwrap();
}
}
}
}
// PB22 to PB23 can be handled by both OC10 and OC11 depending on configuration.
#[interrupt]
#[allow(non_snake_case)]
fn OC10() {
on_interrupt_for_asynch_gpio();
}
#[interrupt]
#[allow(non_snake_case)]
fn OC11() {
on_interrupt_for_asynch_gpio();
}

View File

@ -0,0 +1,171 @@
//! Asynchronous UART reception example application.
//!
//! This application receives data on two UARTs permanently using a ring buffer.
//! The ring buffer are read them asynchronously. UART A is received on ports PA8 and PA9.
//! UART B is received on ports PA2 and PA3.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
//! Tie a USB to UART converter with RX to PA3 and TX to PA2 for UART B.
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
//! type something in the terminal and check if the data is echoed back. You can also check the
//! RTT logs to see received data.
#![no_std]
#![no_main]
use core::cell::RefCell;
use critical_section::Mutex;
use embassy_executor::Spawner;
use embassy_time::Instant;
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use embedded_io_async::Read;
use heapless::spsc::{Consumer, Producer, Queue};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::{
gpio::PinsA,
pac::{self, interrupt},
prelude::*,
uart::{
self, on_interrupt_uart_b_overwriting,
rx_asynch::{on_interrupt_uart_a, RxAsync},
RxAsyncSharedConsumer, Tx,
},
InterruptConfig,
};
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
static QUEUE_UART_A: static_cell::ConstStaticCell<Queue<u8, 256>> =
static_cell::ConstStaticCell::new(Queue::new());
static PRODUCER_UART_A: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
static QUEUE_UART_B: static_cell::ConstStaticCell<Queue<u8, 256>> =
static_cell::ConstStaticCell::new(Queue::new());
static PRODUCER_UART_B: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
static CONSUMER_UART_B: Mutex<RefCell<Option<Consumer<u8, 256>>>> = Mutex::new(RefCell::new(None));
// main is itself an async function.
#[embassy_executor::main]
async fn main(spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async UART RX Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here.
unsafe {
embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output();
let mut led1 = porta.pa7.into_readable_push_pull_output();
let mut led2 = porta.pa6.into_readable_push_pull_output();
let tx_uart_a = porta.pa9.into_funsel_2();
let rx_uart_a = porta.pa8.into_funsel_2();
let uarta = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx_uart_a, rx_uart_a),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC2, true, true),
);
let tx_uart_b = porta.pa3.into_funsel_2();
let rx_uart_b = porta.pa2.into_funsel_2();
let uartb = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uartb,
(tx_uart_b, rx_uart_b),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC3, true, true),
);
let (mut tx_uart_a, rx_uart_a) = uarta.split();
let (tx_uart_b, rx_uart_b) = uartb.split();
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
// Pass the producer to the interrupt handler.
let (prod_uart_b, cons_uart_b) = QUEUE_UART_B.take().split();
critical_section::with(|cs| {
*PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod_uart_a);
*PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod_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 async_rx_uart_b = RxAsyncSharedConsumer::new(rx_uart_b, &CONSUMER_UART_B);
spawner
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
.unwrap();
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART A: {}", Instant::now().as_secs());
led0.toggle().ok();
led1.toggle().ok();
led2.toggle().ok();
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!(
"Read {} bytes asynchronously on UART A: {:?}",
read_bytes,
read_str
);
tx_uart_a.write_all(read_str.as_bytes()).unwrap();
}
}
#[embassy_executor::task]
async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART B: {}", Instant::now().as_secs());
// Infallible asynchronous operation.
let read_bytes = async_rx.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!(
"Read {} bytes asynchronously on UART B: {:?}",
read_bytes,
read_str
);
tx.write_all(read_str.as_bytes()).unwrap();
}
}
#[interrupt]
#[allow(non_snake_case)]
fn OC2() {
let mut prod =
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_uart_a(&mut 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.
if let Err(errors) = errors {
rprintln!("UART A errors: {:?}", errors);
}
}
#[interrupt]
#[allow(non_snake_case)]
fn OC3() {
let mut prod =
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
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));
// In a production app, we could use a channel to send the errors to the main task.
if let Err(errors) = errors {
rprintln!("UART B errors: {:?}", errors);
}
}

View File

@ -0,0 +1,97 @@
//! Asynchronous UART transmission example application.
//!
//! This application receives sends 4 strings with different sizes permanently using UART A.
//! Ports PA8 and PA9 are used for this.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
//! can verify the correctness of the sent strings.
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io_async::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::{
gpio::PinsA,
pac::{self, interrupt},
prelude::*,
uart::{self, on_interrupt_uart_a_tx, TxAsync},
InterruptConfig,
};
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
const STR_LIST: &[&str] = &[
"Hello World\r\n",
"Smoll\r\n",
"A string which is larger than the FIFO size\r\n",
"A really large string which is significantly larger than the FIFO size\r\n",
];
// main is itself an async function.
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async UART TX Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here.
unsafe {
embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output();
let mut led1 = porta.pa7.into_readable_push_pull_output();
let mut led2 = porta.pa6.into_readable_push_pull_output();
let tx = porta.pa9.into_funsel_2();
let rx = porta.pa8.into_funsel_2();
let uarta = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx, rx),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC2, true, true),
);
let (tx, _rx) = uarta.split();
let mut async_tx = TxAsync::new(tx);
let mut ticker = Ticker::every(Duration::from_secs(1));
let mut idx = 0;
loop {
rprintln!("Current time: {}", Instant::now().as_secs());
led0.toggle().ok();
led1.toggle().ok();
led2.toggle().ok();
let _written = async_tx
.write(STR_LIST[idx].as_bytes())
.await
.expect("writing failed");
idx += 1;
if idx == STR_LIST.len() {
idx = 0;
}
ticker.next().await;
}
}
#[interrupt]
#[allow(non_snake_case)]
fn OC2() {
on_interrupt_uart_a_tx();
}

View File

@ -52,7 +52,7 @@ async fn main(_spawner: Spawner) {
} }
} }
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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();
let mut led1 = porta.pa7.into_readable_push_pull_output(); let mut led1 = porta.pa7.into_readable_push_pull_output();
let mut led2 = porta.pa6.into_readable_push_pull_output(); let mut led2 = porta.pa6.into_readable_push_pull_output();

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 = "0.8" va108xx-hal = { version = "0.9", path = "../../va108xx-hal" }
vorago-reb1 = { path = "../../vorago-reb1" } vorago-reb1 = { path = "../../vorago-reb1" }

View File

@ -12,7 +12,7 @@ mod app {
gpio::{FilterType, InterruptEdge, PinsA}, gpio::{FilterType, InterruptEdge, PinsA},
pac, pac,
prelude::*, prelude::*,
timer::{default_ms_irq_handler, set_up_ms_tick, IrqCfg}, timer::{default_ms_irq_handler, set_up_ms_tick, InterruptConfig},
}; };
use vorago_reb1::button::Button; use vorago_reb1::button::Button;
use vorago_reb1::leds::Leds; use vorago_reb1::leds::Leds;
@ -61,23 +61,24 @@ mod app {
rprintln!("Using {:?} mode", mode); rprintln!("Using {:?} mode", mode);
let mut dp = cx.device; let mut dp = cx.device;
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let edge_irq = match mode { let edge_irq = match mode {
PressMode::Toggle => InterruptEdge::HighToLow, PressMode::Toggle => InterruptEdge::HighToLow,
PressMode::Keep => InterruptEdge::BothEdges, PressMode::Keep => InterruptEdge::BothEdges,
}; };
// 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()).edge_irq( let mut button = Button::new(pinsa.pa11.into_floating_input());
button.configure_edge_interrupt(
edge_irq, edge_irq,
IrqCfg::new(pac::interrupt::OC15, true, true), InterruptConfig::new(pac::interrupt::OC15, true, true),
Some(&mut dp.sysconfig), Some(&mut dp.sysconfig),
Some(&mut dp.irqsel), 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 = button.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);
} }
let mut leds = Leds::new( let mut leds = Leds::new(
@ -89,7 +90,7 @@ mod app {
led.off(); led.off();
} }
set_up_ms_tick( set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true), InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),

View File

@ -23,12 +23,13 @@ mod app {
gpio::PinsA, gpio::PinsA,
pac, pac,
prelude::*, prelude::*,
uart::{self, RxWithIrq, Tx}, uart::{self, RxWithInterrupt, Tx},
InterruptConfig,
}; };
#[local] #[local]
struct Local { struct Local {
rx: RxWithIrq<pac::Uarta>, rx: RxWithInterrupt<pac::Uarta>,
tx: Tx<pac::Uarta>, tx: Tx<pac::Uarta>,
} }
@ -47,19 +48,20 @@ mod app {
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw()); Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
let mut dp = cx.device; let mut dp = cx.device;
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
let tx = gpioa.pa9.into_funsel_2(); let tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2(); let rx = gpioa.pa8.into_funsel_2();
let irq_uart = uart::Uart::new( let irq_uart = uart::Uart::new_with_interrupt(
&mut dp.sysconfig, &mut dp.sysconfig,
SYSCLK_FREQ, SYSCLK_FREQ,
dp.uarta, dp.uarta,
(tx, rx), (tx, rx),
115200.Hz(), 115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC3, true, true),
); );
let (tx, rx) = irq_uart.split(); let (tx, rx) = irq_uart.split();
let mut rx = rx.into_rx_with_irq(&mut dp.sysconfig, &mut dp.irqsel, pac::interrupt::OC3); let mut rx = rx.into_rx_with_irq();
rx.start(); rx.start();
@ -90,7 +92,7 @@ mod app {
fn reception_task(mut cx: reception_task::Context) { fn reception_task(mut cx: reception_task::Context) {
let mut buf: [u8; 16] = [0; 16]; let mut buf: [u8; 16] = [0; 16];
let mut ringbuf_full = false; let mut ringbuf_full = false;
let result = cx.local.rx.irq_handler(&mut buf); let result = cx.local.rx.on_interrupt(&mut buf);
if result.bytes_read > 0 && result.errors.is_none() { if result.bytes_read > 0 && result.errors.is_none() {
cx.shared.rb.lock(|rb| { cx.shared.rb.lock(|rb| {
if rb.vacant_len() < result.bytes_read { if rb.vacant_len() < result.bytes_read {

View File

@ -35,11 +35,7 @@ mod app {
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw()); Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
let porta = PinsA::new( let porta = PinsA::new(&mut cx.device.sysconfig, cx.device.porta);
&mut cx.device.sysconfig,
Some(cx.device.ioconfig),
cx.device.porta,
);
let led0 = porta.pa10.into_readable_push_pull_output(); let led0 = porta.pa10.into_readable_push_pull_output();
let led1 = porta.pa7.into_readable_push_pull_output(); let led1 = porta.pa7.into_readable_push_pull_output();
let led2 = porta.pa6.into_readable_push_pull_output(); let led2 = porta.pa6.into_readable_push_pull_output();

View File

@ -16,7 +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.8" path = "../../va108xx-hal"
version = "0.9"
features = ["rt", "defmt"] features = ["rt", "defmt"]
[dependencies.vorago-reb1] [dependencies.vorago-reb1]

View File

@ -18,14 +18,14 @@ use va108xx_hal::{
prelude::*, prelude::*,
timer::DelayMs, timer::DelayMs,
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer}, timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer},
IrqCfg, InterruptConfig,
}; };
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let mut delay_ms = DelayMs::new(set_up_ms_tick( let mut delay_ms = DelayMs::new(set_up_ms_tick(
IrqCfg::new(interrupt::OC0, true, true), InterruptConfig::new(interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),
@ -33,7 +33,7 @@ fn main() -> ! {
)) ))
.unwrap(); .unwrap();
let mut delay_tim1 = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1); let mut delay_tim1 = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led1 = porta.pa10.into_readable_push_pull_output(); let mut led1 = porta.pa10.into_readable_push_pull_output();
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();

View File

@ -17,7 +17,7 @@ use va108xx_hal::{
prelude::*, prelude::*,
timer::{ timer::{
default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource, default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
CountdownTimer, Event, IrqCfg, CountdownTimer, Event, InterruptConfig,
}, },
}; };
@ -39,7 +39,7 @@ fn main() -> ! {
CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim3).auto_disable(true); CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim3).auto_disable(true);
cascade_triggerer.listen( cascade_triggerer.listen(
Event::TimeOut, Event::TimeOut,
IrqCfg::new(pac::Interrupt::OC1, true, false), InterruptConfig::new(pac::Interrupt::OC1, true, false),
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
Some(&mut dp.sysconfig), Some(&mut dp.sysconfig),
); );
@ -62,7 +62,7 @@ fn main() -> ! {
// the timer expires // the timer expires
cascade_target_1.listen( cascade_target_1.listen(
Event::TimeOut, Event::TimeOut,
IrqCfg::new(pac::Interrupt::OC2, true, false), InterruptConfig::new(pac::Interrupt::OC2, true, false),
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
Some(&mut dp.sysconfig), Some(&mut dp.sysconfig),
); );
@ -88,7 +88,7 @@ fn main() -> ! {
// the timer expires // the timer expires
cascade_target_2.listen( cascade_target_2.listen(
Event::TimeOut, Event::TimeOut,
IrqCfg::new(pac::Interrupt::OC3, true, false), InterruptConfig::new(pac::Interrupt::OC3, true, false),
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
Some(&mut dp.sysconfig), Some(&mut dp.sysconfig),
); );

View File

@ -19,7 +19,7 @@ fn main() -> ! {
rtt_init_print!(); rtt_init_print!();
rprintln!("-- VA108xx PWM example application--"); rprintln!("-- VA108xx PWM example application--");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut pwm = pwm::PwmPin::new( let mut pwm = pwm::PwmPin::new(
&mut dp.sysconfig, &mut dp.sysconfig,
50.MHz(), 50.MHz(),

View File

@ -17,7 +17,7 @@ use va108xx_hal::{
prelude::*, prelude::*,
spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs}, spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
timer::{default_ms_irq_handler, set_up_ms_tick}, timer::{default_ms_irq_handler, set_up_ms_tick},
IrqCfg, InterruptConfig,
}; };
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
@ -47,7 +47,7 @@ fn main() -> ! {
rprintln!("-- VA108xx SPI example application--"); rprintln!("-- VA108xx SPI example application--");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let mut delay = set_up_ms_tick( let mut delay = set_up_ms_tick(
IrqCfg::new(interrupt::OC0, true, true), InterruptConfig::new(interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),
@ -58,8 +58,8 @@ fn main() -> ! {
.expect("creating SPI clock config failed"); .expect("creating SPI clock config failed");
let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None); let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None);
let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None); let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb); let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb);
let mut spi_cfg = spi::SpiConfig::default(); let mut spi_cfg = spi::SpiConfig::default();
if EXAMPLE_SEL == ExampleSelect::Loopback { if EXAMPLE_SEL == ExampleSelect::Loopback {

View File

@ -12,7 +12,9 @@ use va108xx_hal::{
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
time::Hertz, time::Hertz,
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, IrqCfg, MS_COUNTER}, timer::{
default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, InterruptConfig, MS_COUNTER,
},
}; };
#[allow(dead_code)] #[allow(dead_code)]
@ -65,7 +67,7 @@ fn main() -> ! {
} }
LibType::Hal => { LibType::Hal => {
set_up_ms_tick( set_up_ms_tick(
IrqCfg::new(interrupt::OC0, true, true), InterruptConfig::new(interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),
@ -75,7 +77,7 @@ fn main() -> ! {
CountdownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1); CountdownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1);
second_timer.listen( second_timer.listen(
Event::TimeOut, Event::TimeOut,
IrqCfg::new(interrupt::OC1, true, true), InterruptConfig::new(interrupt::OC1, true, true),
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
Some(&mut dp.sysconfig), Some(&mut dp.sysconfig),
); );

View File

@ -24,12 +24,18 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
let tx = gpioa.pa9.into_funsel_2(); let tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2(); let rx = gpioa.pa8.into_funsel_2();
let uart = uart::Uart::new_without_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx, rx),
115200.Hz(),
);
let uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz()); let (mut tx, mut rx) = uart.split();
let (mut tx, mut rx) = uarta.split();
writeln!(tx, "Hello World\r").unwrap(); writeln!(tx, "Hello World\r").unwrap();
loop { loop {
// Echo what is received on the serial link. // Echo what is received on the serial link.

View File

@ -71,7 +71,7 @@ mod app {
}; };
use va108xx_hal::gpio::PinsA; use va108xx_hal::gpio::PinsA;
use va108xx_hal::uart::IrqContextTimeoutOrMaxSize; use va108xx_hal::uart::IrqContextTimeoutOrMaxSize;
use va108xx_hal::{pac, uart}; use va108xx_hal::{pac, uart, InterruptConfig};
use vorago_reb1::m95m01::M95M01; use vorago_reb1::m95m01::M95M01;
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
@ -84,7 +84,7 @@ mod app {
#[local] #[local]
struct Local { struct Local {
uart_rx: uart::RxWithIrq<pac::Uarta>, uart_rx: uart::RxWithInterrupt<pac::Uarta>,
uart_tx: uart::Tx<pac::Uarta>, uart_tx: uart::Tx<pac::Uarta>,
rx_context: IrqContextTimeoutOrMaxSize, rx_context: IrqContextTimeoutOrMaxSize,
verif_reporter: VerificationReportCreator, verif_reporter: VerificationReportCreator,
@ -110,19 +110,21 @@ mod app {
let mut dp = cx.device; let mut dp = cx.device;
let nvm = M95M01::new(&mut dp.sysconfig, SYSCLK_FREQ, dp.spic); let nvm = M95M01::new(&mut dp.sysconfig, SYSCLK_FREQ, dp.spic);
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
let tx = gpioa.pa9.into_funsel_2(); let tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2(); let rx = gpioa.pa8.into_funsel_2();
let irq_uart = uart::Uart::new( let irq_uart = uart::Uart::new_with_interrupt(
&mut dp.sysconfig, &mut dp.sysconfig,
SYSCLK_FREQ, SYSCLK_FREQ,
dp.uarta, dp.uarta,
(tx, rx), (tx, rx),
UART_BAUDRATE.Hz(), UART_BAUDRATE.Hz(),
InterruptConfig::new(pac::Interrupt::OC0, true, true),
); );
let (tx, rx) = irq_uart.split(); let (tx, rx) = irq_uart.split();
let mut rx = rx.into_rx_with_irq(&mut dp.sysconfig, &mut dp.irqsel, pac::interrupt::OC0); // Unwrap is okay, we explicitely set the interrupt ID.
let mut rx = rx.into_rx_with_irq();
let verif_reporter = VerificationReportCreator::new(0).unwrap(); let verif_reporter = VerificationReportCreator::new(0).unwrap();
@ -175,7 +177,7 @@ mod app {
match cx match cx
.local .local
.uart_rx .uart_rx
.irq_handler_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf) .on_interrupt_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
{ {
Ok(result) => { Ok(result) => {
if RX_DEBUGGING { if RX_DEBUGGING {

View File

@ -43,7 +43,7 @@ use once_cell::sync::OnceCell;
use va108xx_hal::pac::interrupt; use va108xx_hal::pac::interrupt;
use va108xx_hal::{ use va108xx_hal::{
clock::enable_peripheral_clock, clock::enable_peripheral_clock,
enable_interrupt, pac, enable_nvic_interrupt, pac,
prelude::*, prelude::*,
timer::{enable_tim_clk, get_tim_raw, TimRegInterface}, timer::{enable_tim_clk, get_tim_raw, TimRegInterface},
PeripheralSelect, PeripheralSelect,
@ -221,7 +221,7 @@ impl TimerDriver {
.tim0(timekeeper_tim.tim_id() as usize) .tim0(timekeeper_tim.tim_id() as usize)
.write(|w| unsafe { w.bits(timekeeper_irq as u32) }); .write(|w| unsafe { w.bits(timekeeper_irq as u32) });
unsafe { unsafe {
enable_interrupt(timekeeper_irq); enable_nvic_interrupt(timekeeper_irq);
} }
timekeeper_reg_block timekeeper_reg_block
.ctrl() .ctrl()
@ -239,7 +239,7 @@ impl TimerDriver {
}); });
// Enable general interrupts. The IRQ enable of the peripheral remains cleared. // Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe { unsafe {
enable_interrupt(alarm_irq); enable_nvic_interrupt(alarm_irq);
} }
irqsel irqsel
.tim0(alarm_tim.tim_id() as usize) .tim0(alarm_tim.tim_id() as usize)
@ -299,7 +299,7 @@ impl TimerDriver {
.cnt_value() .cnt_value()
.write(|w| unsafe { w.bits(remaining_ticks.unwrap() as u32) }); .write(|w| unsafe { w.bits(remaining_ticks.unwrap() as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit()); alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) }) alarm_tim.enable().write(|w| unsafe { w.bits(1) });
} }
} }
}) })

View File

@ -10,13 +10,49 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [v0.9.0] ## [v0.9.0]
## Fixed
- Important bugfix for UART driver which causes UART B drivers not to work.
## Removed
- Deleted some HAL re-exports in the PWM module - Deleted some HAL re-exports in the PWM module
## Changed
- GPIO API: Interrupt, pulse and filter and `set_datamask` and `clear_datamask` APIs are now - GPIO API: Interrupt, pulse and filter and `set_datamask` and `clear_datamask` APIs are now
methods which mutable modify the pin instead of consuming and returning it. methods which mutable modify the pin instead of consuming and returning it.
- Simplified PWM module implementation.
- All error types now implement `core::error::Error` by using the `thiserror::Error` derive.
- `InvalidPinTypeError` now wraps the pin mode.
- I2C `TimingCfg` constructor now returns explicit error instead of generic Error.
Removed the timing configuration error type from the generic I2C error enumeration.
- `PinsA` and `PinsB` constructor do not expect an optional `pac::Ioconfig` argument anymore.
- `IrqCfg` renamed to `InterruptConfig`, kept alias for old name.
- All library provided interrupt handlers now start with common prefix `on_interrupt_*`
- `RxWithIrq` renamed to `RxWithInterrupt`
- `Rx::into_rx_with_irq` does not expect any arguments any more.
- `filter_type` renamed to `configure_filter_type`.
- `level_irq` renamed to `configure_level_interrupt`.
- `edge_irq` renamed to `configure_edge_interrupt`.
- `PinsA` and `PinsB` constructor do not expect an optional IOCONFIG argument anymore.
- UART interrupt management is now handled by the main constructor instead of later stages to
statically ensure one interrupt vector for the UART peripheral. `Uart::new` expects an
optional `InterruptConfig` argument.
- `enable_interrupt` and `disable_interrupt` renamed to `enable_nvic_interrupt` and
`disable_nvic_interrupt` to distinguish them from peripheral interrupts more clearly.
- `port_mux` renamed to `port_function_select`
- Renamed `IrqUartErrors` to `UartErrors`.
## Added
- Add `downgrade` method for `Pin` and `upgrade` method for `DynPin` as explicit conversion - Add `downgrade` method for `Pin` and `upgrade` method for `DynPin` as explicit conversion
methods. methods.
- Asynchronous GPIO support.
- Asynchronous UART TX support.
- Asynchronous UART RX support.
- Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks. - Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks.
- Simplified PWM module implementation. - `Uart::with_with_interrupt` and `Uart::new_without_interrupt`
## [v0.8.0] 2024-09-30 ## [v0.8.0] 2024-09-30

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va108xx-hal" name = "va108xx-hal"
version = "0.8.0" 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"
@ -16,18 +16,29 @@ cortex-m-rt = "0.7"
nb = "1" nb = "1"
paste = "1" paste = "1"
embedded-hal = "1" embedded-hal = "1"
embedded-hal-async = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
embedded-io-async = "0.6"
fugit = "0.3" fugit = "0.3"
typenum = "1" typenum = "1"
critical-section = "1" critical-section = "1"
delegate = ">=0.12, <=0.13" delegate = ">=0.12, <=0.13"
heapless = "0.8"
static_cell = "2"
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.3", default-features = false, features = ["critical-section"]} va108xx = { version = "0.4", default-features = false, features = ["critical-section"] }
embassy-sync = "0.6"
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
portable-atomic = "1"
[features] [features]
default = ["rt"] default = ["rt"]
rt = ["va108xx/rt"] rt = ["va108xx/rt"]

View File

@ -25,12 +25,6 @@ rustup target add thumbv6m-none-eabi
After that, you can use `cargo build` to build the development version of the crate. After that, you can use `cargo build` to build the development version of the crate.
If you have not done this yet, it is recommended to read some of the excellent resources
available to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
## Setting up your own binary crate ## Setting up your own binary crate
If you have a custom board, you might be interested in setting up a new binary crate for your If you have a custom board, you might be interested in setting up a new binary crate for your
@ -65,3 +59,11 @@ is contained within the
7. Flashing the board might work differently for different boards and there is usually 7. Flashing the board might work differently for different boards and there is usually
more than one way. You can find example instructions in primary README. more than one way. You can find example instructions in primary README.
## Embedded Rust
If you have not done this yet, it is recommended to read some of the excellent resources available
to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)

3
va108xx-hal/docs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open

View File

@ -11,6 +11,7 @@ static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
pub type PeripheralClocks = PeripheralSelect; pub type PeripheralClocks = PeripheralSelect;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FilterClkSel { pub enum FilterClkSel {
SysClk = 0, SysClk = 0,
Clk1 = 1, Clk1 = 1,
@ -39,13 +40,27 @@ pub fn get_sys_clock() -> Option<Hertz> {
pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkSel, div: u32) { pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkSel, div: u32) {
match clk_sel { match clk_sel {
FilterClkSel::SysClk => (), FilterClkSel::SysClk => (),
FilterClkSel::Clk1 => syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) }), FilterClkSel::Clk1 => {
FilterClkSel::Clk2 => syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) }), syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) });
FilterClkSel::Clk3 => syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) }), }
FilterClkSel::Clk4 => syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) }), FilterClkSel::Clk2 => {
FilterClkSel::Clk5 => syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) }), syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) });
FilterClkSel::Clk6 => syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) }), }
FilterClkSel::Clk7 => syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) }), FilterClkSel::Clk3 => {
syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk4 => {
syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk5 => {
syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk6 => {
syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk7 => {
syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) });
}
} }
} }

View File

@ -0,0 +1,449 @@
//! # Async GPIO functionality for the VA108xx family.
//!
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
//! which must be provided for async support to work. However, it provides one generic
//! [handler][on_interrupt_for_asynch_gpio] which should be called in ALL user interrupt handlers
//! which handle GPIO interrupts.
//!
//! # Example
//!
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
use core::future::Future;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal::digital::InputPin;
use embedded_hal_async::digital::Wait;
use portable_atomic::AtomicBool;
use va108xx::{self as pac, Irqsel, Sysconfig};
use crate::InterruptConfig;
use super::{
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
NUM_GPIO_PINS, NUM_PINS_PORT_A,
};
static WAKERS: [AtomicWaker; NUM_GPIO_PINS] = [const { AtomicWaker::new() }; NUM_GPIO_PINS];
static EDGE_DETECTION: [AtomicBool; NUM_GPIO_PINS] =
[const { AtomicBool::new(false) }; NUM_GPIO_PINS];
#[inline]
fn pin_id_to_offset(dyn_pin_id: DynPinId) -> usize {
match dyn_pin_id.group {
DynGroup::A => dyn_pin_id.num as usize,
DynGroup::B => NUM_PINS_PORT_A + dyn_pin_id.num as usize,
}
}
/// Generic interrupt handler for GPIO interrupts to support the async functionalities.
///
/// 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]
pub fn on_interrupt_for_asynch_gpio() {
let periphs = unsafe { pac::Peripherals::steal() };
handle_interrupt_for_gpio_and_port(
periphs.porta.irq_enb().read().bits(),
periphs.porta.edge_status().read().bits(),
0,
);
handle_interrupt_for_gpio_and_port(
periphs.portb.irq_enb().read().bits(),
periphs.portb.edge_status().read().bits(),
NUM_PINS_PORT_A,
);
}
// 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, edge_status: u32, pin_base_offset: usize) {
while irq_enb != 0 {
let bit_pos = irq_enb.trailing_zeros() as usize;
let bit_mask = 1 << bit_pos;
WAKERS[pin_base_offset + bit_pos].wake();
if edge_status & bit_mask != 0 {
EDGE_DETECTION[pin_base_offset + bit_pos]
.store(true, core::sync::atomic::Ordering::Relaxed);
}
// Clear the processed bit
irq_enb &= !bit_mask;
}
}
/// Input pin future which implements the [Future] trait.
///
/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
/// struture is granted to allow writing custom async structures.
pub struct InputPinFuture {
pin_id: DynPinId,
}
impl InputPinFuture {
/// # 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 new_with_dyn_pin(
pin: &mut DynPin,
irq: pac::Interrupt,
edge: InterruptEdge,
sys_cfg: &mut Sysconfig,
irq_sel: &mut Irqsel,
) -> Result<Self, InvalidPinTypeError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()));
}
EDGE_DETECTION[pin_id_to_offset(pin.id())]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.interrupt_edge(
edge,
InterruptConfig::new(irq, true, true),
Some(sys_cfg),
Some(irq_sel),
)
.unwrap();
Ok(Self { pin_id: pin.id() })
}
/// # 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>>,
irq: pac::Interrupt,
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 {
EDGE_DETECTION[pin_id_to_offset(pin.id())]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.configure_edge_interrupt(
edge,
InterruptConfig::new(irq, true, true),
Some(sys_cfg),
Some(irq_sel),
);
Self { pin_id: pin.id() }
}
}
impl Drop for InputPinFuture {
fn drop(&mut self) {
let periphs = unsafe { pac::Peripherals::steal() };
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)) });
}
}
}
impl Future for InputPinFuture {
type Output = ();
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
let idx = pin_id_to_offset(self.pin_id);
WAKERS[idx].register(cx.waker());
if EDGE_DETECTION[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
return core::task::Poll::Ready(());
}
core::task::Poll::Pending
}
}
pub struct InputDynPinAsync {
pin: DynPin,
irq: pac::Interrupt,
}
impl InputDynPinAsync {
/// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
/// the asynchronous functionality to work.
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()));
}
Ok(Self { pin, irq })
}
/// Asynchronously wait until the pin is high.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
let fut = unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::LowToHigh,
)
.unwrap()
};
if self.pin.is_high().unwrap() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin is low.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_low(&mut self) {
let fut = unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::HighToLow,
)
.unwrap()
};
if self.pin.is_low().unwrap() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin sees a falling edge.
pub async fn wait_for_falling_edge(&mut self) {
unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::HighToLow,
)
.unwrap()
}
.await;
}
/// Asynchronously wait until the pin sees a rising edge.
pub async fn wait_for_rising_edge(&mut self) {
unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::LowToHigh,
)
.unwrap()
}
.await;
}
/// Asynchronously wait until the pin sees any edge (either rising or falling).
pub async fn wait_for_any_edge(&mut self) {
unsafe {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_unchecked_with_dyn_pin(
&mut self.pin,
self.irq,
InterruptEdge::BothEdges,
)
.unwrap()
}
.await;
}
pub fn release(self) -> DynPin {
self.pin
}
}
impl embedded_hal::digital::ErrorType for InputDynPinAsync {
type Error = core::convert::Infallible;
}
impl Wait for InputDynPinAsync {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
self.wait_for_high().await;
Ok(())
}
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
self.wait_for_low().await;
Ok(())
}
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_rising_edge().await;
Ok(())
}
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_falling_edge().await;
Ok(())
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_any_edge().await;
Ok(())
}
}
pub struct InputPinAsync<I: PinId, C: InputConfig> {
pin: Pin<I, pin::Input<C>>,
irq: pac::Interrupt,
}
impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
/// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
/// the asynchronous functionality to work.
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
Self { pin, irq }
}
/// Asynchronously wait until the pin is high.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
let fut = unsafe {
InputPinFuture::new_unchecked_with_pin(
&mut self.pin,
self.irq,
InterruptEdge::LowToHigh,
)
};
if self.pin.is_high().unwrap() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin is low.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_low(&mut self) {
let fut = unsafe {
InputPinFuture::new_unchecked_with_pin(
&mut self.pin,
self.irq,
InterruptEdge::HighToLow,
)
};
if self.pin.is_low().unwrap() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin sees falling edge.
pub async fn wait_for_falling_edge(&mut self) {
unsafe {
// 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.
pub async fn wait_for_rising_edge(&mut self) {
unsafe {
// 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).
pub async fn wait_for_any_edge(&mut self) {
unsafe {
InputPinFuture::new_unchecked_with_pin(
&mut self.pin,
self.irq,
InterruptEdge::BothEdges,
)
}
.await;
}
pub fn release(self) -> Pin<I, pin::Input<C>> {
self.pin
}
}
impl<I: PinId, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
type Error = core::convert::Infallible;
}
impl<I: PinId, C: InputConfig> Wait for InputPinAsync<I, C> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
self.wait_for_high().await;
Ok(())
}
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
self.wait_for_low().await;
Ok(())
}
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_rising_edge().await;
Ok(())
}
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_falling_edge().await;
Ok(())
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_any_edge().await;
Ok(())
}
}

View File

@ -59,8 +59,9 @@
use super::{ use super::{
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState}, pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
reg::RegisterInterface, reg::RegisterInterface,
InputDynPinAsync,
}; };
use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg}; use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
//================================================================================================== //==================================================================================================
// DynPinMode configurations // DynPinMode configurations
@ -68,6 +69,7 @@ use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};
/// Value-level `enum` for disabled configurations /// Value-level `enum` for disabled configurations
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynDisabled { pub enum DynDisabled {
Floating, Floating,
PullDown, PullDown,
@ -75,7 +77,8 @@ pub enum DynDisabled {
} }
/// Value-level `enum` for input configurations /// Value-level `enum` for input configurations
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynInput { pub enum DynInput {
Floating, Floating,
PullDown, PullDown,
@ -83,7 +86,8 @@ pub enum DynInput {
} }
/// Value-level `enum` for output configurations /// Value-level `enum` for output configurations
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynOutput { pub enum DynOutput {
PushPull, PushPull,
OpenDrain, OpenDrain,
@ -101,9 +105,10 @@ pub type DynAlternate = FunSel;
/// ///
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time /// [`DynPin`]s are not tracked and verified at compile-time, so run-time
/// operations are fallible. This `enum` represents the corresponding errors. /// operations are fallible. This `enum` represents the corresponding errors.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidPinTypeError; #[error("Invalid pin type for operation: {0:?}")]
pub struct InvalidPinTypeError(pub DynPinMode);
impl embedded_hal::digital::Error for InvalidPinTypeError { impl embedded_hal::digital::Error for InvalidPinTypeError {
fn kind(&self) -> embedded_hal::digital::ErrorKind { fn kind(&self) -> embedded_hal::digital::ErrorKind {
@ -116,7 +121,8 @@ impl embedded_hal::digital::Error for InvalidPinTypeError {
//================================================================================================== //==================================================================================================
/// Value-level `enum` representing pin modes /// Value-level `enum` representing pin modes
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynPinMode { pub enum DynPinMode {
Input(DynInput), Input(DynInput),
Output(DynOutput), Output(DynOutput),
@ -151,14 +157,16 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
//================================================================================================== //==================================================================================================
/// Value-level `enum` for pin groups /// Value-level `enum` for pin groups
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynGroup { pub enum DynGroup {
A, A,
B, B,
} }
/// Value-level `struct` representing pin IDs /// Value-level `struct` representing pin IDs
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DynPinId { pub struct DynPinId {
pub group: DynGroup, pub group: DynGroup,
pub num: u8, pub num: u8,
@ -172,16 +180,15 @@ pub struct DynPinId {
/// ///
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to /// This `struct` takes ownership of a [`DynPinId`] and provides an API to
/// access the corresponding regsiters. /// access the corresponding regsiters.
pub(crate) struct DynRegisters { #[derive(Debug)]
id: DynPinId, pub(crate) struct DynRegisters(DynPinId);
}
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`] // [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
// guarantees that each pin is a singleton, so this implementation is safe. // guarantees that each pin is a singleton, so this implementation is safe.
unsafe impl RegisterInterface for DynRegisters { unsafe impl RegisterInterface for DynRegisters {
#[inline] #[inline]
fn id(&self) -> DynPinId { fn id(&self) -> DynPinId {
self.id self.0
} }
} }
@ -194,7 +201,7 @@ impl DynRegisters {
/// the same [`DynPinId`] /// the same [`DynPinId`]
#[inline] #[inline]
unsafe fn new(id: DynPinId) -> Self { unsafe fn new(id: DynPinId) -> Self {
DynRegisters { id } DynRegisters(id)
} }
} }
@ -206,6 +213,7 @@ impl DynRegisters {
/// ///
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented /// This type acts as a type-erased version of [`Pin`]. Every pin is represented
/// 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)]
pub struct DynPin { pub struct DynPin {
pub(crate) regs: DynRegisters, pub(crate) regs: DynRegisters,
mode: DynPinMode, mode: DynPinMode,
@ -230,7 +238,7 @@ impl DynPin {
/// Return a copy of the pin ID /// Return a copy of the pin ID
#[inline] #[inline]
pub fn id(&self) -> DynPinId { pub fn id(&self) -> DynPinId {
self.regs.id self.regs.0
} }
/// Return a copy of the pin mode /// Return a copy of the pin mode
@ -249,6 +257,11 @@ impl DynPin {
} }
} }
#[inline]
pub fn is_input_pin(&self) -> bool {
matches!(self.mode, DynPinMode::Input(_))
}
#[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);
@ -343,7 +356,7 @@ impl DynPin {
pub(crate) fn irq_enb( pub(crate) fn irq_enb(
&mut self, &mut self,
irq_cfg: crate::IrqCfg, irq_cfg: crate::InterruptConfig,
syscfg: Option<&mut va108xx::Sysconfig>, syscfg: Option<&mut va108xx::Sysconfig>,
irqsel: Option<&mut va108xx::Irqsel>, irqsel: Option<&mut va108xx::Irqsel>,
) { ) {
@ -358,16 +371,19 @@ impl DynPin {
DynGroup::A => { DynGroup::A => {
irqsel irqsel
.porta0(self.regs.id().num as usize) .porta0(self.regs.id().num as usize)
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); .write(|w| unsafe { w.bits(irq_cfg.id as u32) });
} }
DynGroup::B => { DynGroup::B => {
irqsel irqsel
.portb0(self.regs.id().num as usize) .portb0(self.regs.id().num as usize)
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); .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. /// See p.53 of the programmers guide for more information.
@ -382,7 +398,7 @@ impl DynPin {
self.regs.delay(delay_1, delay_2); self.regs.delay(delay_1, delay_2);
Ok(self) Ok(self)
} }
_ => Err(InvalidPinTypeError), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -400,7 +416,7 @@ impl DynPin {
self.regs.pulse_mode(enable, default_state); self.regs.pulse_mode(enable, default_state);
Ok(()) Ok(())
} }
_ => Err(InvalidPinTypeError), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -416,7 +432,7 @@ impl DynPin {
self.regs.filter_type(filter, clksel); self.regs.filter_type(filter, clksel);
Ok(()) Ok(())
} }
_ => Err(InvalidPinTypeError), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -424,7 +440,7 @@ impl DynPin {
pub fn interrupt_edge( pub fn interrupt_edge(
&mut self, &mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>, syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>, irqsel: Option<&mut pac::Irqsel>,
) -> Result<(), InvalidPinTypeError> { ) -> Result<(), InvalidPinTypeError> {
@ -434,7 +450,7 @@ impl DynPin {
self.irq_enb(irq_cfg, syscfg, irqsel); self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(()) Ok(())
} }
_ => Err(InvalidPinTypeError), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -442,7 +458,7 @@ impl DynPin {
pub fn interrupt_level( pub fn interrupt_level(
&mut self, &mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>, syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>, irqsel: Option<&mut pac::Irqsel>,
) -> Result<(), InvalidPinTypeError> { ) -> Result<(), InvalidPinTypeError> {
@ -452,7 +468,7 @@ impl DynPin {
self.irq_enb(irq_cfg, syscfg, irqsel); self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(()) Ok(())
} }
_ => Err(InvalidPinTypeError), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -463,7 +479,7 @@ impl DynPin {
self.regs.toggle(); self.regs.toggle();
Ok(()) Ok(())
} }
_ => Err(InvalidPinTypeError), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -473,7 +489,7 @@ impl DynPin {
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => { DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
Ok(self.regs.read_pin()) Ok(self.regs.read_pin())
} }
_ => Err(InvalidPinTypeError), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
#[inline] #[inline]
@ -483,7 +499,7 @@ impl DynPin {
self.regs.write_pin(bit); self.regs.write_pin(bit);
Ok(()) Ok(())
} }
_ => Err(InvalidPinTypeError), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -511,12 +527,21 @@ impl DynPin {
/// or refuse to perform it. /// or refuse to perform it.
#[inline] #[inline]
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> { pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
if self.regs.id == I::DYN && self.mode == M::DYN { if self.regs.0 == I::DYN && self.mode == M::DYN {
// The `DynPin` is consumed, so it is safe to replace it with the // The `DynPin` is consumed, so it is safe to replace it with the
// corresponding `Pin` // corresponding `Pin`
return Ok(unsafe { Pin::new() }); return Ok(unsafe { Pin::new() });
} }
Err(InvalidPinTypeError) 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)
} }
} }

View File

@ -22,14 +22,22 @@
//! //!
//! - [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)
#[derive(Debug, PartialEq, Eq)] #[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")]
pub struct IsMaskedError; pub struct IsMaskedError;
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;
pub mod dynpin; pub mod dynpin;
pub use dynpin::*; pub use dynpin::*;
pub mod pin; pub mod pin;
pub use pin::*; pub use pin::*;
pub mod asynch;
pub use asynch::*;
mod reg; mod reg;

View File

@ -72,11 +72,11 @@
//! and [`StatefulOutputPin`]. //! and [`StatefulOutputPin`].
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode}; use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
use super::reg::RegisterInterface; use super::reg::RegisterInterface;
use super::DynPin; use super::{DynPin, InputPinAsync};
use crate::{ use crate::{
pac::{Irqsel, Porta, Portb, Sysconfig}, pac::{Irqsel, Porta, Portb, Sysconfig},
typelevel::Sealed, typelevel::Sealed,
IrqCfg, InterruptConfig,
}; };
use core::convert::Infallible; use core::convert::Infallible;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -119,8 +119,11 @@ pub trait InputConfig: Sealed {
const DYN: DynInput; const DYN: DynInput;
} }
#[derive(Debug)]
pub enum Floating {} pub enum Floating {}
#[derive(Debug)]
pub enum PullDown {} pub enum PullDown {}
#[derive(Debug)]
pub enum PullUp {} pub enum PullUp {}
impl InputConfig for Floating { impl InputConfig for Floating {
@ -148,6 +151,7 @@ pub type InputPullUp = Input<PullUp>;
/// ///
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or /// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
/// [`PullUp`] /// [`PullUp`]
#[derive(Debug)]
pub struct Input<C: InputConfig> { pub struct Input<C: InputConfig> {
cfg: PhantomData<C>, cfg: PhantomData<C>,
} }
@ -177,13 +181,17 @@ pub trait OutputConfig: Sealed {
pub trait ReadableOutput: Sealed {} pub trait ReadableOutput: Sealed {}
/// Type-level variant of [`OutputConfig`] for a push-pull configuration /// Type-level variant of [`OutputConfig`] for a push-pull configuration
#[derive(Debug)]
pub enum PushPull {} pub enum PushPull {}
/// Type-level variant of [`OutputConfig`] for an open drain configuration /// Type-level variant of [`OutputConfig`] for an open drain configuration
#[derive(Debug)]
pub enum OpenDrain {} pub enum OpenDrain {}
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration /// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
#[derive(Debug)]
pub enum ReadablePushPull {} pub enum ReadablePushPull {}
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration /// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
#[derive(Debug)]
pub enum ReadableOpenDrain {} pub enum ReadableOpenDrain {}
impl Sealed for PushPull {} impl Sealed for PushPull {}
@ -210,6 +218,7 @@ impl OutputConfig for ReadableOpenDrain {
/// ///
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or /// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
/// their respective readable versions /// their respective readable versions
#[derive(Debug)]
pub struct Output<C: OutputConfig> { pub struct Output<C: OutputConfig> {
cfg: PhantomData<C>, cfg: PhantomData<C>,
} }
@ -304,6 +313,7 @@ macro_rules! pin_id {
// Need paste macro to use ident in doc attribute // Need paste macro to use ident in doc attribute
paste! { paste! {
#[doc = "Pin ID representing pin " $Id] #[doc = "Pin ID representing pin " $Id]
#[derive(Debug)]
pub enum $Id {} pub enum $Id {}
impl Sealed for $Id {} impl Sealed for $Id {}
impl PinId for $Id { impl PinId for $Id {
@ -321,6 +331,7 @@ macro_rules! pin_id {
//================================================================================================== //==================================================================================================
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types /// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
#[derive(Debug)]
pub struct Pin<I: PinId, M: PinMode> { pub struct Pin<I: PinId, M: PinMode> {
inner: DynPin, inner: DynPin,
phantom: PhantomData<(I, M)>, phantom: PhantomData<(I, M)>,
@ -342,6 +353,10 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
} }
} }
pub fn id(&self) -> DynPinId {
self.inner.id()
}
/// Convert the pin to the requested [`PinMode`] /// Convert the pin to the requested [`PinMode`]
#[inline] #[inline]
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> { pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
@ -450,7 +465,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
fn irq_enb( fn irq_enb(
&mut self, &mut self,
irq_cfg: crate::IrqCfg, irq_cfg: crate::InterruptConfig,
syscfg: Option<&mut va108xx::Sysconfig>, syscfg: Option<&mut va108xx::Sysconfig>,
irqsel: Option<&mut va108xx::Irqsel>, irqsel: Option<&mut va108xx::Irqsel>,
) { ) {
@ -571,10 +586,16 @@ impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
//================================================================================================== //==================================================================================================
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> { impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
pub fn interrupt_edge( /// Convert the pin into an async pin. The pin can be converted back by calling
/// [InputPinAsync::release]
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
InputPinAsync::new(self, irq)
}
pub fn configure_edge_interrupt(
&mut self, &mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>, syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>, irqsel: Option<&mut Irqsel>,
) { ) {
@ -582,10 +603,10 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
self.irq_enb(irq_cfg, syscfg, irqsel); self.irq_enb(irq_cfg, syscfg, irqsel);
} }
pub fn interrupt_level( pub fn configure_level_interrupt(
&mut self, &mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>, syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>, irqsel: Option<&mut Irqsel>,
) { ) {
@ -621,7 +642,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
pub fn interrupt_edge( pub fn interrupt_edge(
&mut self, &mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>, syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>, irqsel: Option<&mut Irqsel>,
) { ) {
@ -632,7 +653,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
pub fn interrupt_level( pub fn interrupt_level(
&mut self, &mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>, syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>, irqsel: Option<&mut Irqsel>,
) { ) {
@ -644,7 +665,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> { impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// See p.37 and p.38 of the programmers guide for more information. /// See p.37 and p.38 of the programmers guide for more information.
#[inline] #[inline]
pub fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.inner.regs.filter_type(filter, clksel); self.inner.regs.filter_type(filter, clksel);
} }
} }
@ -731,8 +752,8 @@ macro_rules! pins {
) => { ) => {
paste!( paste!(
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB) /// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
#[derive(Debug)]
pub struct $PinsName { pub struct $PinsName {
iocfg: Option<va108xx::Ioconfig>,
port: $Port, port: $Port,
$( $(
#[doc = "Pin " $Id] #[doc = "Pin " $Id]
@ -747,7 +768,6 @@ macro_rules! pins {
#[inline] #[inline]
pub fn new( pub fn new(
syscfg: &mut va108xx::Sysconfig, syscfg: &mut va108xx::Sysconfig,
iocfg: Option<va108xx::Ioconfig>,
port: $Port port: $Port
) -> $PinsName { ) -> $PinsName {
syscfg.peripheral_clk_enable().modify(|_, w| { syscfg.peripheral_clk_enable().modify(|_, w| {
@ -756,7 +776,7 @@ macro_rules! pins {
w.ioconfig().set_bit() w.ioconfig().set_bit()
}); });
$PinsName { $PinsName {
iocfg, //iocfg,
port, port,
// Safe because we only create one `Pin` per `PinId` // Safe because we only create one `Pin` per `PinId`
$( $(
@ -773,8 +793,8 @@ macro_rules! pins {
} }
/// Consumes the Pins struct and returns the port definitions /// Consumes the Pins struct and returns the port definitions
pub fn release(self) -> (Option<va108xx::Ioconfig>, $Port) { pub fn release(self) -> $Port {
(self.iocfg, self.port) self.port
} }
} }
); );

View File

@ -73,13 +73,6 @@ impl From<DynPinMode> for ModeFields {
//================================================================================================== //==================================================================================================
pub type PortReg = ioconfig::Porta; pub type PortReg = ioconfig::Porta;
/*
pub type IocfgPort = ioconfig::Porta;
#[repr(C)]
pub(super) struct IocfgPortGroup {
port: [IocfgPort; 32],
}
*/
/// Provide a safe register interface for pin objects /// Provide a safe register interface for pin objects
/// ///
@ -311,7 +304,7 @@ pub(super) unsafe trait RegisterInterface {
unsafe { unsafe {
portreg portreg
.datamask() .datamask()
.modify(|r, w| w.bits(r.bits() | self.mask_32())) .modify(|r, w| w.bits(r.bits() | self.mask_32()));
} }
} }
@ -323,7 +316,7 @@ pub(super) unsafe trait RegisterInterface {
unsafe { unsafe {
portreg portreg
.datamask() .datamask()
.modify(|r, w| w.bits(r.bits() & !self.mask_32())) .modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} }
} }

View File

@ -18,42 +18,50 @@ const CLK_400K: Hertz = Hertz::from_raw(400_000);
const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000); const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000);
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FifoEmptyMode { pub enum FifoEmptyMode {
Stall = 0, Stall = 0,
EndTransaction = 1, EndTransaction = 1,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClockTooSlowForFastI2c; #[error("clock too slow for fast I2C mode")]
pub struct ClockTooSlowForFastI2cError;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[error("invalid timing parameters")]
pub struct InvalidTimingParamsError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error { pub enum Error {
InvalidTimingParams, //#[error("Invalid timing parameters")]
//InvalidTimingParams,
#[error("arbitration lost")]
ArbitrationLost, ArbitrationLost,
#[error("nack address")]
NackAddr, NackAddr,
/// Data not acknowledged in write operation /// Data not acknowledged in write operation
#[error("data not acknowledged in write operation")]
NackData, NackData,
/// Not enough data received in read operation /// Not enough data received in read operation
#[error("insufficient data received")]
InsufficientDataReceived, InsufficientDataReceived,
/// Number of bytes in transfer too large (larger than 0x7fe) /// Number of bytes in transfer too large (larger than 0x7fe)
#[error("data too large (larger than 0x7fe)")]
DataTooLarge, DataTooLarge,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InitError { pub enum InitError {
/// Wrong address used in constructor /// Wrong address used in constructor
#[error("wrong address mode")]
WrongAddrMode, WrongAddrMode,
/// APB1 clock is too slow for fast I2C mode. /// APB1 clock is too slow for fast I2C mode.
ClkTooSlow(ClockTooSlowForFastI2c), #[error("clock too slow for fast I2C mode: {0}")]
} ClkTooSlow(#[from] ClockTooSlowForFastI2cError),
impl From<ClockTooSlowForFastI2c> for InitError {
fn from(value: ClockTooSlowForFastI2c) -> Self {
Self::ClkTooSlow(value)
}
} }
impl embedded_hal::i2c::Error for Error { impl embedded_hal::i2c::Error for Error {
@ -66,7 +74,7 @@ impl embedded_hal::i2c::Error for Error {
Error::NackData => { Error::NackData => {
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data) embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data)
} }
Error::DataTooLarge | Error::InsufficientDataReceived | Error::InvalidTimingParams => { Error::DataTooLarge | Error::InsufficientDataReceived => {
embedded_hal::i2c::ErrorKind::Other embedded_hal::i2c::ErrorKind::Other
} }
} }
@ -82,18 +90,21 @@ enum I2cCmd {
} }
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cSpeed { pub enum I2cSpeed {
Regular100khz = 0, Regular100khz = 0,
Fast400khz = 1, Fast400khz = 1,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cDirection { pub enum I2cDirection {
Send = 0, Send = 0,
Read = 1, Read = 1,
} }
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cAddress { pub enum I2cAddress {
Regular(u8), Regular(u8),
TenBit(u16), TenBit(u16),
@ -134,9 +145,12 @@ impl Instance for pac::I2cb {
// Config // Config
//================================================================================================== //==================================================================================================
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TrTfThighTlow(u8, u8, u8, u8); pub struct TrTfThighTlow(u8, u8, u8, u8);
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8); pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8);
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TimingCfg { pub struct TimingCfg {
// 4 bit max width // 4 bit max width
tr: u8, tr: u8,
@ -160,7 +174,7 @@ impl TimingCfg {
pub fn new( pub fn new(
first_16_bits: TrTfThighTlow, first_16_bits: TrTfThighTlow,
second_16_bits: TsuStoTsuStaThdStaTBuf, second_16_bits: TsuStoTsuStaThdStaTBuf,
) -> Result<Self, Error> { ) -> Result<Self, InvalidTimingParamsError> {
if first_16_bits.0 > 0xf if first_16_bits.0 > 0xf
|| first_16_bits.1 > 0xf || first_16_bits.1 > 0xf
|| first_16_bits.2 > 0xf || first_16_bits.2 > 0xf
@ -170,7 +184,7 @@ impl TimingCfg {
|| second_16_bits.2 > 0xf || second_16_bits.2 > 0xf
|| second_16_bits.3 > 0xf || second_16_bits.3 > 0xf
{ {
return Err(Error::InvalidTimingParams); return Err(InvalidTimingParamsError);
} }
Ok(TimingCfg { Ok(TimingCfg {
tr: first_16_bits.0, tr: first_16_bits.0,
@ -211,6 +225,7 @@ impl Default for TimingCfg {
} }
} }
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MasterConfig { pub struct MasterConfig {
pub tx_fe_mode: FifoEmptyMode, pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode, pub rx_fe_mode: FifoEmptyMode,
@ -299,7 +314,7 @@ impl<I2c: Instance> I2cBase<I2c> {
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
ms_cfg: Option<&MasterConfig>, ms_cfg: Option<&MasterConfig>,
sl_cfg: Option<&SlaveConfig>, sl_cfg: Option<&SlaveConfig>,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
enable_peripheral_clock(syscfg, I2c::PERIPH_SEL); enable_peripheral_clock(syscfg, I2c::PERIPH_SEL);
let mut i2c_base = I2cBase { let mut i2c_base = I2cBase {
@ -377,12 +392,12 @@ impl<I2c: Instance> I2cBase<I2c> {
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b); let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
self.i2c self.i2c
.s0_addressb() .s0_addressb()
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) }) .write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
} }
if let Some(addr_b_mask) = sl_cfg.addr_b_mask { if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
self.i2c self.i2c
.s0_addressmaskb() .s0_addressmaskb()
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) }) .write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) });
} }
} }
@ -402,19 +417,22 @@ impl<I2c: Instance> I2cBase<I2c> {
}); });
} }
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2c> { fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
if speed_mode == I2cSpeed::Regular100khz { if speed_mode == I2cSpeed::Regular100khz {
Ok(((self.sys_clk.raw() / CLK_100K.raw() / 20) - 1) as u8) Ok(((self.sys_clk.raw() / CLK_100K.raw() / 20) - 1) as u8)
} else { } else {
if self.sys_clk.raw() < MIN_CLK_400K.raw() { if self.sys_clk.raw() < MIN_CLK_400K.raw() {
return Err(ClockTooSlowForFastI2c); return Err(ClockTooSlowForFastI2cError);
} }
Ok(((self.sys_clk.raw() / CLK_400K.raw() / 25) - 1) as u8) Ok(((self.sys_clk.raw() / CLK_400K.raw() / 25) - 1) as u8)
} }
} }
/// Configures the clock scale for a given speed mode setting /// Configures the clock scale for a given speed mode setting
pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2c> { pub fn cfg_clk_scale(
&mut self,
speed_mode: I2cSpeed,
) -> Result<(), ClockTooSlowForFastI2cError> {
let clk_div = self.calc_clk_div(speed_mode)?; let clk_div = self.calc_clk_div(speed_mode)?;
self.i2c self.i2c
.clkscale() .clkscale()
@ -460,7 +478,7 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
i2c: I2c, i2c: I2c,
cfg: MasterConfig, cfg: MasterConfig,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cMaster { Ok(I2cMaster {
i2c_base: I2cBase::new(syscfg, sysclk, i2c, speed_mode, Some(&cfg), None)?, i2c_base: I2cBase::new(syscfg, sysclk, i2c, speed_mode, Some(&cfg), None)?,
addr: PhantomData, addr: PhantomData,
@ -990,7 +1008,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
i2c: I2c, i2c: I2c,
cfg: SlaveConfig, cfg: SlaveConfig,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cSlave { Ok(I2cSlave {
i2c_base: I2cBase::new(sys_cfg, sys_clk, i2c, speed_mode, None, Some(&cfg))?, i2c_base: I2cBase::new(sys_cfg, sys_clk, i2c, speed_mode, None, Some(&cfg))?,
addr: PhantomData, addr: PhantomData,
@ -1152,7 +1170,7 @@ impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
i2c: I2c, i2c: I2c,
cfg: SlaveConfig, cfg: SlaveConfig,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode) Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)
} }
} }

View File

@ -17,6 +17,7 @@ pub mod typelevel;
pub mod uart; pub mod uart;
#[derive(Debug, Eq, Copy, Clone, PartialEq)] #[derive(Debug, Eq, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FunSel { pub enum FunSel {
Sel1 = 0b01, Sel1 = 0b01,
Sel2 = 0b10, Sel2 = 0b10,
@ -24,12 +25,14 @@ pub enum FunSel {
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PortSel { pub enum PortSel {
PortA, PortA,
PortB, PortB,
} }
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect { pub enum PeripheralSelect {
PortA = 0, PortA = 0,
PortB = 1, PortB = 1,
@ -46,31 +49,39 @@ pub enum PeripheralSelect {
Gpio = 24, Gpio = 24,
} }
/// Generic IRQ config which can be used to specify whether the HAL driver will /// Generic interrupt config which can be used to specify whether the HAL driver will
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the /// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform /// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
/// this steps themselves /// perform those steps themselves.
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct IrqCfg { pub struct InterruptConfig {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs /// Interrupt target vector. Should always be set, might be required for disabling IRQs
pub irq: pac::Interrupt, pub id: pac::Interrupt,
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral /// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral.
pub route: bool, pub route: bool,
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC /// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for
pub enable: bool, /// multiple purposes, the user can enable the interrupts themselves.
pub enable_in_nvic: bool,
} }
impl IrqCfg { impl InterruptConfig {
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self { pub fn new(id: pac::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
IrqCfg { irq, route, enable } InterruptConfig {
id,
route,
enable_in_nvic,
}
} }
} }
pub type IrqCfg = InterruptConfig;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidPin(pub(crate) ()); pub struct InvalidPin(pub(crate) ());
/// Can be used to manually manipulate the function select of port pins /// Can be used to manually manipulate the function select of port pins
pub fn port_mux( pub fn port_function_select(
ioconfig: &mut pac::Ioconfig, ioconfig: &mut pac::Ioconfig,
port: PortSel, port: PortSel,
pin: u8, pin: u8,
@ -104,7 +115,7 @@ pub fn port_mux(
/// ///
/// This function is `unsafe` because it can break mask-based critical sections. /// This function is `unsafe` because it can break mask-based critical sections.
#[inline] #[inline]
pub unsafe fn enable_interrupt(irq: pac::Interrupt) { pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
unsafe { unsafe {
cortex_m::peripheral::NVIC::unmask(irq); cortex_m::peripheral::NVIC::unmask(irq);
} }
@ -112,6 +123,6 @@ pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
/// Disable a specific interrupt using the NVIC peripheral. /// Disable a specific interrupt using the NVIC peripheral.
#[inline] #[inline]
pub fn disable_interrupt(irq: pac::Interrupt) { pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
cortex_m::peripheral::NVIC::mask(irq); cortex_m::peripheral::NVIC::mask(irq);
} }

View File

@ -15,7 +15,9 @@ use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
const DUTY_MAX: u16 = u16::MAX; const DUTY_MAX: u16 = u16::MAX;
pub struct PwmCommon { #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct PwmCommon {
sys_clk: Hertz, sys_clk: Hertz,
/// For PWMB, this is the upper limit /// For PWMB, this is the upper limit
current_duty: u16, current_duty: u16,
@ -80,7 +82,7 @@ where
pin pin
} }
pub fn reduce(self) -> ReducedPwmPin<Mode> { pub fn downgrade(self) -> ReducedPwmPin<Mode> {
self.inner self.inner
} }
@ -213,22 +215,6 @@ impl<Mode> ReducedPwmPin<Mode> {
} }
} }
} }
/*
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim>> for ReducedPwmPin<PwmA> {
fn from(pwm_pin: PwmPin<Pin, Tim>) -> Self {
ReducedPwmPin {
dyn_reg: TimDynRegister {
}
// ::from(pwm_pin.reg),
common: pwm_pin.pwm_base,
pin_id: Pin::DYN,
mode: PhantomData,
}
}
}
*/
impl<Mode> ReducedPwmPin<Mode> { impl<Mode> ReducedPwmPin<Mode> {
#[inline] #[inline]
@ -293,6 +279,24 @@ impl<Mode> ReducedPwmPin<Mode> {
} }
} }
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for ReducedPwmPin<PwmA>
where
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
{
fn from(value: PwmPin<Pin, Tim, PwmA>) -> Self {
value.downgrade()
}
}
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmB>> for ReducedPwmPin<PwmB>
where
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
{
fn from(value: PwmPin<Pin, Tim, PwmB>) -> Self {
value.downgrade()
}
}
impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> { impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
fn from(other: ReducedPwmPin<PwmA>) -> Self { fn from(other: ReducedPwmPin<PwmA>) -> Self {
let mut pwmb = Self { let mut pwmb = Self {

View File

@ -37,6 +37,7 @@ pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
pub const DEFAULT_CLK_DIV: u16 = 2; pub const DEFAULT_CLK_DIV: u16 = 2;
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HwChipSelectId { pub enum HwChipSelectId {
Id0 = 0, Id0 = 0,
Id1 = 1, Id1 = 1,
@ -50,6 +51,7 @@ pub enum HwChipSelectId {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiPort { pub enum SpiPort {
Porta = 0, Porta = 0,
Portb = 1, Portb = 1,
@ -58,6 +60,7 @@ pub enum SpiPort {
} }
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WordSize { pub enum WordSize {
OneBit = 0x00, OneBit = 0x00,
FourBits = 0x03, FourBits = 0x03,
@ -571,10 +574,13 @@ impl SpiClkConfig {
} }
} }
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum SpiClkConfigError { pub enum SpiClkConfigError {
#[error("division by zero")]
DivIsZero, DivIsZero,
#[error("divide value is not even")]
DivideValueNotEven, DivideValueNotEven,
#[error("scrdv value is too large")]
ScrdvValueTooLarge, ScrdvValueTooLarge,
} }
@ -786,7 +792,7 @@ where
// initialization. Returns the amount of written bytes. // initialization. Returns the amount of written bytes.
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize { fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
if self.blockmode { if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit()) self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit());
} }
// Fill the first half of the write FIFO // Fill the first half of the write FIFO
let mut current_write_idx = 0; let mut current_write_idx = 0;
@ -800,7 +806,7 @@ where
current_write_idx += 1; current_write_idx += 1;
} }
if self.blockmode { if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit()) self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit());
} }
current_write_idx current_write_idx
} }
@ -809,7 +815,7 @@ where
// initialization. // initialization.
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize { fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
if self.blockmode { if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit()) self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit());
} }
// Fill the first half of the write FIFO // Fill the first half of the write FIFO
let mut current_write_idx = 0; let mut current_write_idx = 0;
@ -823,7 +829,7 @@ where
current_write_idx += 1; current_write_idx += 1;
} }
if self.blockmode { if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit()) self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit());
} }
current_write_idx current_write_idx
} }

View File

@ -20,7 +20,7 @@ pub fn enable_rom_scrubbing(
} }
pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) { pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) }) syscfg.rom_scrub().write(|w| unsafe { w.bits(0) });
} }
/// Enable scrubbing for the RAM /// Enable scrubbing for the RAM
@ -39,7 +39,7 @@ pub fn enable_ram_scrubbing(
} }
pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) { pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) {
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) }) syscfg.ram_scrub().write(|w| unsafe { w.bits(0) });
} }
/// Clear the reset bit. This register is active low, so doing this will hold the peripheral /// Clear the reset bit. This register is active low, so doing this will hold the peripheral

View File

@ -4,10 +4,10 @@
//! //!
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs) //! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs)
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs) //! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
pub use crate::IrqCfg; pub use crate::InterruptConfig;
use crate::{ use crate::{
clock::{enable_peripheral_clock, PeripheralClocks}, clock::{enable_peripheral_clock, PeripheralClocks},
enable_interrupt, enable_nvic_interrupt,
gpio::{ gpio::{
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14, AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8, PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
@ -79,6 +79,7 @@ pub enum Event {
} }
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] #[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CascadeCtrl { pub struct CascadeCtrl {
/// Enable Cascade 0 signal active as a requirement for counting /// Enable Cascade 0 signal active as a requirement for counting
pub enb_start_src_csd0: bool, pub enb_start_src_csd0: bool,
@ -108,6 +109,7 @@ pub struct CascadeCtrl {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CascadeSel { pub enum CascadeSel {
Csd0 = 0, Csd0 = 0,
Csd1 = 1, Csd1 = 1,
@ -286,7 +288,7 @@ pub type TimRegBlock = tim0::RegisterBlock;
/// ///
/// # Safety /// # Safety
/// ///
/// Users should only implement the [`tim_id`] function. No default function /// Users should only implement the [Self::tim_id] function. No default function
/// implementations should be overridden. The implementing type must also have /// implementations should be overridden. The implementing type must also have
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each /// "control" over the corresponding pin ID, i.e. it must guarantee that a each
/// pin ID is a singleton. /// pin ID is a singleton.
@ -319,7 +321,7 @@ pub unsafe trait TimRegInterface {
va108xx::Peripherals::steal() va108xx::Peripherals::steal()
.sysconfig .sysconfig
.tim_reset() .tim_reset()
.modify(|r, w| w.bits(r.bits() & !self.mask_32())) .modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} }
} }
@ -330,7 +332,7 @@ pub unsafe trait TimRegInterface {
va108xx::Peripherals::steal() va108xx::Peripherals::steal()
.sysconfig .sysconfig
.tim_reset() .tim_reset()
.modify(|r, w| w.bits(r.bits() | self.mask_32())) .modify(|r, w| w.bits(r.bits() | self.mask_32()));
} }
} }
} }
@ -362,7 +364,7 @@ unsafe impl TimRegInterface for TimDynRegister {
pub struct CountdownTimer<Tim: ValidTim> { pub struct CountdownTimer<Tim: ValidTim> {
tim: Tim, tim: Tim,
curr_freq: Hertz, curr_freq: Hertz,
irq_cfg: Option<IrqCfg>, irq_cfg: Option<InterruptConfig>,
sys_clk: Hertz, sys_clk: Hertz,
rst_val: u32, rst_val: u32,
last_cnt: u32, last_cnt: u32,
@ -415,13 +417,13 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
pub fn listen( pub fn listen(
&mut self, &mut self,
event: Event, event: Event,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
irq_sel: Option<&mut pac::Irqsel>, irq_sel: Option<&mut pac::Irqsel>,
sys_cfg: Option<&mut pac::Sysconfig>, sys_cfg: Option<&mut pac::Sysconfig>,
) { ) {
match event { match event {
Event::TimeOut => { Event::TimeOut => {
cortex_m::peripheral::NVIC::mask(irq_cfg.irq); cortex_m::peripheral::NVIC::mask(irq_cfg.id);
self.irq_cfg = Some(irq_cfg); self.irq_cfg = Some(irq_cfg);
if irq_cfg.route { if irq_cfg.route {
if let Some(sys_cfg) = sys_cfg { if let Some(sys_cfg) = sys_cfg {
@ -430,7 +432,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
if let Some(irq_sel) = irq_sel { if let Some(irq_sel) = irq_sel {
irq_sel irq_sel
.tim0(Tim::TIM_ID as usize) .tim0(Tim::TIM_ID as usize)
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); .write(|w| unsafe { w.bits(irq_cfg.id as u32) });
} }
} }
self.listening = true; self.listening = true;
@ -520,8 +522,8 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
pub fn enable(&mut self) { pub fn enable(&mut self) {
if let Some(irq_cfg) = self.irq_cfg { if let Some(irq_cfg) = self.irq_cfg {
self.enable_interrupt(); self.enable_interrupt();
if irq_cfg.enable { if irq_cfg.enable_in_nvic {
unsafe { enable_interrupt(irq_cfg.irq) }; unsafe { enable_nvic_interrupt(irq_cfg.id) };
} }
} }
self.tim self.tim
@ -719,7 +721,7 @@ impl<TIM: ValidTim> embedded_hal::delay::DelayNs for CountdownTimer<TIM> {
// Set up a millisecond timer on TIM0. Please note that the user still has to provide an IRQ handler // Set up a millisecond timer on TIM0. Please note that the user still has to provide an IRQ handler
// which should call [default_ms_irq_handler]. // which should call [default_ms_irq_handler].
pub fn set_up_ms_tick<TIM: ValidTim>( pub fn set_up_ms_tick<TIM: ValidTim>(
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
sys_cfg: &mut pac::Sysconfig, sys_cfg: &mut pac::Sysconfig,
irq_sel: Option<&mut pac::Irqsel>, irq_sel: Option<&mut pac::Irqsel>,
sys_clk: impl Into<Hertz>, sys_clk: impl Into<Hertz>,

View File

@ -7,12 +7,11 @@
//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader) //! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader)
use core::{convert::Infallible, ops::Deref}; use core::{convert::Infallible, ops::Deref};
use fugit::RateExtU32; use fugit::RateExtU32;
use va108xx::Uarta;
pub use crate::IrqCfg; pub use crate::InterruptConfig;
use crate::{ use crate::{
clock::enable_peripheral_clock, clock::enable_peripheral_clock,
enable_interrupt, enable_nvic_interrupt,
gpio::pin::{ gpio::pin::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30, AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30,
PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9, PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
@ -23,6 +22,13 @@ use crate::{
}; };
use embedded_hal_nb::serial::Read; use embedded_hal_nb::serial::Read;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
A = 0,
B = 1,
}
//================================================================================================== //==================================================================================================
// Type-Level support // Type-Level support
//================================================================================================== //==================================================================================================
@ -48,30 +54,35 @@ impl Pins<pac::Uartb> for (Pin<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {}
// Regular Definitions // Regular Definitions
//================================================================================================== //==================================================================================================
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("no interrupt ID was set")]
pub struct NoInterruptIdWasSet;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("transer is pending")]
pub struct TransferPendingError; pub struct TransferPendingError;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RxError { pub enum RxError {
#[error("overrun error")]
Overrun, Overrun,
#[error("framing error")]
Framing, Framing,
#[error("parity error")]
Parity, Parity,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error { pub enum Error {
Rx(RxError), #[error("rx error: {0}")]
Rx(#[from] RxError),
#[error("break condition")]
BreakCondition, BreakCondition,
} }
impl From<RxError> for Error {
fn from(value: RxError) -> Self {
Self::Rx(value)
}
}
impl embedded_io::Error for Error { impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind { fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other embedded_io::ErrorKind::Other
@ -227,6 +238,7 @@ impl From<Hertz> for Config {
//================================================================================================== //==================================================================================================
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqContextTimeoutOrMaxSize { pub struct IrqContextTimeoutOrMaxSize {
rx_idx: usize, rx_idx: usize,
mode: IrqReceptionMode, mode: IrqReceptionMode,
@ -252,17 +264,19 @@ impl IrqContextTimeoutOrMaxSize {
/// This struct is used to return the default IRQ handler result to the user /// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)] #[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqResult { pub struct IrqResult {
pub bytes_read: usize, pub bytes_read: usize,
pub errors: Option<IrqUartError>, pub errors: Option<UartErrors>,
} }
/// This struct is used to return the default IRQ handler result to the user /// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)] #[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqResultMaxSizeOrTimeout { pub struct IrqResultMaxSizeOrTimeout {
complete: bool, complete: bool,
timeout: bool, timeout: bool,
pub errors: Option<IrqUartError>, pub errors: Option<UartErrors>,
pub bytes_read: usize, pub bytes_read: usize,
} }
@ -309,20 +323,22 @@ impl IrqResultMaxSizeOrTimeout {
} }
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum IrqReceptionMode { enum IrqReceptionMode {
Idle, Idle,
Pending, Pending,
} }
#[derive(Default, Debug, Copy, Clone)] #[derive(Default, Debug, Copy, Clone)]
pub struct IrqUartError { #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UartErrors {
overflow: bool, overflow: bool,
framing: bool, framing: bool,
parity: bool, parity: bool,
other: bool, other: bool,
} }
impl IrqUartError { impl UartErrors {
#[inline(always)] #[inline(always)]
pub fn overflow(&self) -> bool { pub fn overflow(&self) -> bool {
self.overflow self.overflow
@ -344,7 +360,7 @@ impl IrqUartError {
} }
} }
impl IrqUartError { impl UartErrors {
#[inline(always)] #[inline(always)]
pub fn error(&self) -> bool { pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity self.overflow || self.framing || self.parity
@ -373,6 +389,16 @@ 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; fn ptr() -> *const uart_base::RegisterBlock;
/// Retrieve the type erased peripheral register block.
///
/// # Safety
///
/// This circumvents the safety guarantees of the HAL.
#[inline(always)]
unsafe fn reg_block() -> &'static uart_base::RegisterBlock {
unsafe { &(*Self::ptr()) }
}
} }
impl Instance for pac::Uarta { impl Instance for pac::Uarta {
@ -380,11 +406,13 @@ impl Instance for pac::Uarta {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
#[inline(always)]
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uarta pac::Peripherals::steal().uarta
} }
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uarta::ptr() as *const _ Self::ptr() as *const _
} }
} }
@ -393,11 +421,13 @@ impl Instance for pac::Uartb {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
#[inline(always)]
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uartb pac::Peripherals::steal().uartb
} }
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uarta::ptr() as *const _ Self::ptr() as *const _
} }
} }
@ -592,15 +622,51 @@ where
UartInstance: Instance, UartInstance: Instance,
PinsInstance: Pins<UartInstance>, PinsInstance: Pins<UartInstance>,
{ {
pub fn new( /// Calls [Self::new] with the interrupt configuration to some valid value.
pub fn new_with_interrupt(
syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>,
uart: UartInstance,
pins: PinsInstance,
config: impl Into<Config>,
irq_cfg: InterruptConfig,
) -> Self {
Self::new(syscfg, sys_clk, uart, pins, config, Some(irq_cfg))
}
/// Calls [Self::new] with the interrupt configuration to [None].
pub fn new_without_interrupt(
syscfg: &mut va108xx::Sysconfig, syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>, sys_clk: impl Into<Hertz>,
uart: UartInstance, uart: UartInstance,
pins: PinsInstance, pins: PinsInstance,
config: impl Into<Config>, config: impl Into<Config>,
) -> Self {
Self::new(syscfg, sys_clk, uart, pins, config, None)
}
/// Create a new UART peripheral with an interrupt configuration.
///
/// # Arguments
///
/// - `syscfg`: The system configuration register block
/// - `sys_clk`: The system clock frequency
/// - `uart`: The concrete UART peripheral instance.
/// - `pins`: UART TX and RX pin tuple.
/// - `config`: UART specific configuration parameters like baudrate.
/// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan
/// is to use TX or RX functionality relying on interrupts. If only the blocking API without
/// any interrupt support is used, this can be [None].
pub fn new(
syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>,
uart: UartInstance,
pins: PinsInstance,
config: impl Into<Config>,
opt_irq_cfg: Option<InterruptConfig>,
) -> Self { ) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
Uart { let uart = Uart {
inner: UartBase { inner: UartBase {
uart, uart,
tx: Tx::new(unsafe { UartInstance::steal() }), tx: Tx::new(unsafe { UartInstance::steal() }),
@ -608,7 +674,21 @@ where
}, },
pins, pins,
} }
.init(config.into(), sys_clk.into()) .init(config.into(), sys_clk.into());
if let Some(irq_cfg) = opt_irq_cfg {
if irq_cfg.route {
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
unsafe { pac::Irqsel::steal() }
.uart0(UartInstance::IDX as usize)
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
}
if irq_cfg.enable_in_nvic {
// Safety: User has specifically configured this.
unsafe { enable_nvic_interrupt(irq_cfg.id) };
}
}
uart
} }
/// This function assumes that the peripheral clock was alredy enabled /// This function assumes that the peripheral clock was alredy enabled
@ -683,14 +763,45 @@ where
} }
} }
#[inline(always)]
pub fn enable_rx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.rxenable().set_bit());
}
#[inline(always)]
pub fn disable_rx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.rxenable().clear_bit());
}
#[inline(always)]
pub fn enable_rx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_rx().set_bit();
w.irq_rx_to().set_bit();
w.irq_rx_status().set_bit()
});
}
#[inline(always)]
pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_rx().clear_bit();
w.irq_rx_to().clear_bit();
w.irq_rx_status().clear_bit()
});
}
/// 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)]
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.
@ -698,23 +809,33 @@ impl<Uart: Instance> Rx<Uart> {
/// # Safety /// # Safety
/// ///
/// 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)]
pub unsafe fn uart(&self) -> &Uart { pub unsafe fn uart(&self) -> &Uart {
&self.0 &self.uart
} }
#[inline] #[inline]
pub fn clear_fifo(&self) { pub fn clear_fifo(&self) {
self.0.fifo_clr().write(|w| w.rxfifo().set_bit()); self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn disable_interrupts(&mut self) {
disable_rx_interrupts(unsafe { Uart::reg_block() });
}
#[inline]
pub fn enable_interrupts(&mut self) {
enable_rx_interrupts(unsafe { Uart::reg_block() });
} }
#[inline] #[inline]
pub fn enable(&mut self) { pub fn enable(&mut self) {
self.0.enable().modify(|_, w| w.rxenable().set_bit()); enable_rx(unsafe { Uart::reg_block() });
} }
#[inline] #[inline]
pub fn disable(&mut self) { pub fn disable(&mut self) {
self.0.enable().modify(|_, w| w.rxenable().clear_bit()); disable_rx(unsafe { Uart::reg_block() });
} }
/// Low level function to read a word from the UART FIFO. /// Low level function to read a word from the UART FIFO.
@ -725,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())
@ -741,20 +862,16 @@ 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( pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
self, RxWithInterrupt::new(self)
sysconfig: &mut pac::Sysconfig,
irqsel: &mut pac::Irqsel,
interrupt: pac::Interrupt,
) -> RxWithIrq<Uart> {
RxWithIrq::new(self, sysconfig, irqsel, interrupt)
} }
#[inline(always)]
pub fn release(self) -> Uart { pub fn release(self) -> Uart {
self.0 self.uart
} }
} }
@ -811,14 +928,57 @@ impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
} }
} }
#[inline(always)]
pub fn enable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().set_bit());
}
#[inline(always)]
pub fn disable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().clear_bit());
}
#[inline(always)]
pub fn enable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().set_bit();
w.irq_tx_status().set_bit();
w.irq_tx_empty().set_bit()
});
}
#[inline(always)]
pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().clear_bit();
w.irq_tx_status().clear_bit();
w.irq_tx_empty().clear_bit()
});
}
/// 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
///
/// # Safety
///
/// Circumvents the HAL safety guarantees.
#[inline(always)]
pub unsafe fn steal() -> Self {
Self {
uart: Uart::steal(),
}
}
#[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.
@ -826,23 +986,47 @@ impl<Uart: Instance> Tx<Uart> {
/// # Safety /// # Safety
/// ///
/// 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)]
pub 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.
///
/// - The IRQ_TX interrupt is generated when the TX FIFO is at least half empty.
/// - The IRQ_TX_STATUS interrupt is generated when write data is lost due to a FIFO overflow
/// - The IRQ_TX_EMPTY interrupt is generated when the TX FIFO is empty and the TXBUSY signal
/// is 0
#[inline]
pub fn enable_interrupts(&self) {
// Safety: We own the UART structure
enable_tx_interrupts(unsafe { Uart::reg_block() });
}
/// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
///
/// [Self::enable_interrupts] documents the interrupts.
#[inline]
pub fn disable_interrupts(&self) {
// Safety: We own the UART structure
disable_tx_interrupts(unsafe { Uart::reg_block() });
} }
/// Low level function to write a word to the UART FIFO. /// Low level function to write a word to the UART FIFO.
@ -853,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);
@ -868,7 +1052,11 @@ 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> {
TxAsync::new(self)
} }
} }
@ -925,51 +1113,38 @@ impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
/// ///
/// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You /// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You
/// can simply use [Self::start] to prepare the peripheral and then call the /// can simply use [Self::start] to prepare the peripheral and then call the
/// [Self::irq_handler] in the interrupt service routine. /// [Self::on_interrupt] in the interrupt service routine.
/// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You /// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You
/// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and /// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and
/// then call the [Self::irq_handler_max_size_or_timeout_based] in the interrupt service /// then call the [Self::on_interrupt_max_size_or_timeout_based] in the interrupt service
/// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to /// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to
/// start reading the next packet. /// start reading the next packet.
pub struct RxWithIrq<Uart> { pub struct RxWithInterrupt<Uart>(Rx<Uart>);
pub rx: Rx<Uart>,
pub interrupt: pac::Interrupt,
}
impl<Uart: Instance> RxWithIrq<Uart> { impl<Uart: Instance> RxWithInterrupt<Uart> {
pub fn new( pub fn new(rx: Rx<Uart>) -> Self {
rx: Rx<Uart>, Self(rx)
syscfg: &mut pac::Sysconfig,
irqsel: &mut pac::Irqsel,
interrupt: pac::Interrupt,
) -> Self {
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
irqsel
.uart0(Uart::IDX as usize)
.write(|w| unsafe { w.bits(interrupt as u32) });
Self { rx, interrupt }
} }
/// This function should be called once at initialization time if the regular /// This function should be called once at initialization time if the regular
/// [Self::irq_handler] is used to read the UART receiver to enable and start the receiver. /// [Self::on_interrupt] is used to read the UART receiver to enable and start the receiver.
pub fn start(&mut self) { pub fn start(&mut self) {
self.rx.enable(); self.0.enable();
self.enable_rx_irq_sources(true); self.enable_rx_irq_sources(true);
unsafe { enable_interrupt(self.interrupt) };
} }
#[inline(always)] #[inline(always)]
pub fn uart(&self) -> &Uart { pub fn uart(&self) -> &Uart {
&self.rx.0 &self.0.uart
} }
/// This function is used together with the [Self::irq_handler_max_size_or_timeout_based] /// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]
/// function to read packets with a maximum size or variable sized packets by using the /// function to read packets with a maximum size or variable sized packets by using the
/// receive timeout of the hardware. /// receive timeout of the hardware.
/// ///
/// This function should be called once at initialization to initiate the context state /// This function should be called once at initialization to initiate the context state
/// and to [Self::start] the receiver. After that, it should be called after each /// and to [Self::start] the receiver. After that, it should be called after each
/// completed [Self::irq_handler_max_size_or_timeout_based] call to restart the reception /// completed [Self::on_interrupt_max_size_or_timeout_based] call to restart the reception
/// of a packet. /// of a packet.
pub fn read_fixed_len_or_timeout_based_using_irq( pub fn read_fixed_len_or_timeout_based_using_irq(
&mut self, &mut self,
@ -1006,7 +1181,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
pub fn cancel_transfer(&mut self) { pub fn cancel_transfer(&mut self) {
self.disable_rx_irq_sources(); self.disable_rx_irq_sources();
self.rx.clear_fifo(); self.0.clear_fifo();
} }
/// This function should be called in the user provided UART interrupt handler. /// This function should be called in the user provided UART interrupt handler.
@ -1017,7 +1192,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// This function will not disable the RX interrupts, so you don't need to call any other /// This function will not disable the RX interrupts, so you don't need to call any other
/// API after calling this function to continue emptying the FIFO. RX errors are handled /// API after calling this function to continue emptying the FIFO. RX errors are handled
/// as partial errors and are returned as part of the [IrqResult]. /// as partial errors and are returned as part of the [IrqResult].
pub fn irq_handler(&mut self, buf: &mut [u8; 16]) -> IrqResult { pub fn on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult {
let mut result = IrqResult::default(); let mut result = IrqResult::default();
let irq_end = self.uart().irq_end().read(); let irq_end = self.uart().irq_end().read();
@ -1040,7 +1215,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
if irq_end.irq_rx_to().bit_is_set() { if irq_end.irq_rx_to().bit_is_set() {
loop { loop {
// 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 read_result = self.rx.read(); let read_result = self.0.read();
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) { if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
buf[result.bytes_read] = byte; buf[result.bytes_read] = byte;
result.bytes_read += 1; result.bytes_read += 1;
@ -1074,7 +1249,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// If passed buffer is equal to or larger than the specified maximum length, an /// If passed buffer is equal to or larger than the specified maximum length, an
/// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors /// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors
/// and returned inside the [IrqResultMaxSizeOrTimeout] structure. /// and returned inside the [IrqResultMaxSizeOrTimeout] structure.
pub fn irq_handler_max_size_or_timeout_based( pub fn on_interrupt_max_size_or_timeout_based(
&mut self, &mut self,
context: &mut IrqContextTimeoutOrMaxSize, context: &mut IrqContextTimeoutOrMaxSize,
buf: &mut [u8], buf: &mut [u8],
@ -1123,7 +1298,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
if context.rx_idx == context.max_len { if context.rx_idx == context.max_len {
break; break;
} }
let read_result = self.rx.read(); let read_result = self.0.read();
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) { if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
buf[context.rx_idx] = byte; buf[context.rx_idx] = byte;
context.rx_idx += 1; context.rx_idx += 1;
@ -1149,7 +1324,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
fn read_handler( fn read_handler(
&self, &self,
errors: &mut Option<IrqUartError>, errors: &mut Option<UartErrors>,
read_res: &nb::Result<u8, RxError>, read_res: &nb::Result<u8, RxError>,
) -> Option<u8> { ) -> Option<u8> {
match read_res { match read_res {
@ -1157,7 +1332,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
Err(nb::Error::WouldBlock) => None, Err(nb::Error::WouldBlock) => None,
Err(nb::Error::Other(e)) => { Err(nb::Error::Other(e)) => {
// Ensure `errors` is Some(IrqUartError), initializing if it's None // Ensure `errors` is Some(IrqUartError), initializing if it's None
let err = errors.get_or_insert(IrqUartError::default()); let err = errors.get_or_insert(UartErrors::default());
// Now we can safely modify fields inside `err` // Now we can safely modify fields inside `err`
match e { match e {
@ -1170,14 +1345,14 @@ impl<Uart: Instance> RxWithIrq<Uart> {
} }
} }
fn check_for_errors(&self, errors: &mut Option<IrqUartError>) { fn check_for_errors(&self, errors: &mut Option<UartErrors>) {
let rx_status = self.uart().rxstatus().read(); let rx_status = self.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()
|| rx_status.rxpar().bit_is_set() || rx_status.rxpar().bit_is_set()
{ {
let err = errors.get_or_insert(IrqUartError::default()); let err = errors.get_or_insert(UartErrors::default());
if rx_status.rxovr().bit_is_set() { if rx_status.rxovr().bit_is_set() {
err.overflow = true; err.overflow = true;
@ -1197,7 +1372,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
context: &mut IrqContextTimeoutOrMaxSize, context: &mut IrqContextTimeoutOrMaxSize,
) { ) {
self.disable_rx_irq_sources(); self.disable_rx_irq_sources();
self.rx.disable(); self.0.disable();
res.bytes_read = context.rx_idx; res.bytes_read = context.rx_idx;
res.complete = true; res.complete = true;
context.mode = IrqReceptionMode::Idle; context.mode = IrqReceptionMode::Idle;
@ -1210,246 +1385,12 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// The user must ensure that these instances are not used to create multiple overlapping /// The user must ensure that these instances are not used to create multiple overlapping
/// UART drivers. /// UART drivers.
pub unsafe fn release(self) -> Uart { pub unsafe fn release(self) -> Uart {
self.rx.release() self.0.release()
} }
} }
/* pub mod tx_asynch;
pub use tx_asynch::*;
impl<UART: Instance, PINS> UartWithIrq<UART, PINS> { pub mod rx_asynch;
/// See [`UartWithIrqBase::read_fixed_len_using_irq`] doc pub use rx_asynch::*;
pub fn read_fixed_len_using_irq(
&mut self,
max_len: usize,
enb_timeout_irq: bool,
) -> Result<(), Error> {
self.irq_base
.read_fixed_len_using_irq(max_len, enb_timeout_irq)
}
pub fn cancel_transfer(&mut self) {
self.irq_base.cancel_transfer()
}
/// See [`UartWithIrqBase::irq_handler`] doc
pub fn irq_handler(&mut self, res: &mut IrqResult, buf: &mut [u8]) -> Result<(), Error> {
self.irq_base.irq_handler(res, buf)
}
pub fn release(self) -> (UART, PINS) {
(self.irq_base.release(), self.pins)
}
pub fn downgrade(self) -> (UartWithIrqBase<UART>, PINS) {
(self.irq_base, self.pins)
}
}
impl<Uart: Instance> UartWithIrqBase<Uart> {
fn init(self, sys_cfg: Option<&mut pac::Sysconfig>, irq_sel: Option<&mut pac::Irqsel>) -> Self {
if let Some(sys_cfg) = sys_cfg {
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel)
}
if let Some(irq_sel) = irq_sel {
if self.irq_info.irq_cfg.route {
irq_sel
.uart0(Uart::IDX as usize)
.write(|w| unsafe { w.bits(self.irq_info.irq_cfg.irq as u32) });
}
}
self
}
/// This initializes a non-blocking read transfer using the IRQ capabilities of the UART
/// peripheral.
///
/// The only required information is the maximum length for variable sized reception
/// or the expected length for fixed length reception. If variable sized packets are expected,
/// the timeout functionality of the IRQ should be enabled as well. After calling this function,
/// the [`irq_handler`](Self::irq_handler) function should be called in the user interrupt
/// handler to read the received packets and reinitiate another transfer if desired.
pub fn read_fixed_len_using_irq(
&mut self,
max_len: usize,
enb_timeout_irq: bool,
) -> Result<(), Error> {
if self.irq_info.mode != IrqReceptionMode::Idle {
return Err(Error::TransferPending);
}
self.irq_info.mode = IrqReceptionMode::Pending;
self.irq_info.rx_idx = 0;
self.irq_info.rx_len = max_len;
self.uart.enable_rx();
self.uart.enable_tx();
self.enable_rx_irq_sources(enb_timeout_irq);
if self.irq_info.irq_cfg.enable {
unsafe {
enable_interrupt(self.irq_info.irq_cfg.irq);
}
}
Ok(())
}
#[inline]
fn enable_rx_irq_sources(&mut self, timeout: bool) {
self.uart.uart.irq_enb().modify(|_, w| {
if timeout {
w.irq_rx_to().set_bit();
}
w.irq_rx_status().set_bit();
w.irq_rx().set_bit()
});
}
#[inline]
fn disable_rx_irq_sources(&mut self) {
self.uart.uart.irq_enb().modify(|_, w| {
w.irq_rx_to().clear_bit();
w.irq_rx_status().clear_bit();
w.irq_rx().clear_bit()
});
}
#[inline]
pub fn enable_tx(&mut self) {
self.uart.enable_tx()
}
#[inline]
pub fn disable_tx(&mut self) {
self.uart.disable_tx()
}
pub fn cancel_transfer(&mut self) {
// Disable IRQ
cortex_m::peripheral::NVIC::mask(self.irq_info.irq_cfg.irq);
self.disable_rx_irq_sources();
self.uart.clear_tx_fifo();
self.irq_info.rx_idx = 0;
self.irq_info.rx_len = 0;
}
/// Default IRQ handler which can be used to read the packets arriving on the UART peripheral.
///
/// If passed buffer is equal to or larger than the specified maximum length, an
/// [`Error::BufferTooShort`] will be returned
pub fn irq_handler(&mut self, res: &mut IrqResult, buf: &mut [u8]) -> Result<(), Error> {
if buf.len() < self.irq_info.rx_len {
return Err(Error::BufferTooShort);
}
let irq_end = self.uart.uart.irq_end().read();
let enb_status = self.uart.uart.enable().read();
let rx_enabled = enb_status.rxenable().bit_is_set();
let _tx_enabled = enb_status.txenable().bit_is_set();
let read_handler =
|res: &mut IrqResult, read_res: nb::Result<u8, Error>| -> Result<Option<u8>, Error> {
match read_res {
Ok(byte) => Ok(Some(byte)),
Err(nb::Error::WouldBlock) => Ok(None),
Err(nb::Error::Other(e)) => match e {
Error::Overrun => {
res.set_result(IrqResultMask::Overflow);
Err(Error::IrqError)
}
Error::FramingError => {
res.set_result(IrqResultMask::FramingError);
Err(Error::IrqError)
}
Error::ParityError => {
res.set_result(IrqResultMask::ParityError);
Err(Error::IrqError)
}
_ => {
res.set_result(IrqResultMask::Unknown);
Err(Error::IrqError)
}
},
}
};
if irq_end.irq_rx().bit_is_set() {
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..core::cmp::min(
self.uart.uart.rxfifoirqtrg().read().bits() as usize,
self.irq_info.rx_len,
) {
buf[self.irq_info.rx_idx] = (self.uart.uart.data().read().bits() & 0xff) as u8;
self.irq_info.rx_idx += 1;
}
// While there is data in the FIFO, write it into the reception buffer
loop {
if self.irq_info.rx_idx == self.irq_info.rx_len {
self.irq_completion_handler(res);
return Ok(());
}
if let Some(byte) = read_handler(res, self.uart.read())? {
buf[self.irq_info.rx_idx] = byte;
self.irq_info.rx_idx += 1;
} else {
break;
}
}
}
// RX transfer not complete, check for RX errors
if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled {
// Read status register again, might have changed since reading received data
let rx_status = self.uart.uart.rxstatus().read();
res.clear_result();
if rx_status.rxovr().bit_is_set() {
res.set_result(IrqResultMask::Overflow);
}
if rx_status.rxfrm().bit_is_set() {
res.set_result(IrqResultMask::FramingError);
}
if rx_status.rxpar().bit_is_set() {
res.set_result(IrqResultMask::ParityError);
}
if rx_status.rxbrk().bit_is_set() {
res.set_result(IrqResultMask::Break);
}
if rx_status.rxto().bit_is_set() {
// A timeout has occured but there might be some leftover data in the FIFO,
// so read that data as well
while let Some(byte) = read_handler(res, self.uart.read())? {
buf[self.irq_info.rx_idx] = byte;
self.irq_info.rx_idx += 1;
}
self.irq_completion_handler(res);
res.set_result(IrqResultMask::Timeout);
return Ok(());
}
// If it is not a timeout, it's an error
if res.raw_res != 0 {
self.disable_rx_irq_sources();
return Err(Error::IrqError);
}
}
// Clear the interrupt status bits
self.uart
.uart
.irq_clr()
.write(|w| unsafe { w.bits(irq_end.bits()) });
Ok(())
}
fn irq_completion_handler(&mut self, res: &mut IrqResult) {
self.disable_rx_irq_sources();
self.uart.disable_rx();
res.bytes_read = self.irq_info.rx_idx;
res.clear_result();
res.set_result(IrqResultMask::Complete);
self.irq_info.mode = IrqReceptionMode::Idle;
self.irq_info.rx_idx = 0;
self.irq_info.rx_len = 0;
}
pub fn release(self) -> Uart {
self.uart.release()
}
}
*/

View File

@ -0,0 +1,419 @@
//! # Async UART reception functionality for the VA108xx family.
//!
//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
//! [embedded_io_async::Read] trait.
//! 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.
//! However, it provides four interrupt handlers:
//!
//! - [on_interrupt_uart_a]
//! - [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
//! [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]
//! structure returned by the interrupt handlers.
//!
//! # Example
//!
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs)
use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io::ErrorType;
use heapless::spsc::Consumer;
use portable_atomic::AtomicBool;
use va108xx as pac;
use super::{Instance, Rx, RxError, UartErrors};
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
struct RxFuture {
uart_idx: usize,
}
impl RxFuture {
pub fn new<Uart: Instance>(_rx: &mut Rx<Uart>) -> Self {
RX_READ_ACTIVE[Uart::IDX as usize].store(true, Ordering::Relaxed);
Self {
uart_idx: Uart::IDX as usize,
}
}
}
impl Future for RxFuture {
type Output = Result<(), RxError>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_RX_WAKERS[self.uart_idx].register(cx.waker());
if RX_HAS_DATA[self.uart_idx].load(Ordering::Relaxed) {
return core::task::Poll::Ready(Ok(()));
}
core::task::Poll::Pending
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AsyncUartErrors {
/// Queue has overflowed, data might have been lost.
pub queue_overflow: bool,
/// UART errors.
pub uart_errors: UartErrors,
}
fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErrors> {
let rx_status = uart.rxstatus().read();
if rx_status.rxovr().bit_is_set()
|| rx_status.rxfrm().bit_is_set()
|| rx_status.rxpar().bit_is_set()
{
let mut errors_val = UartErrors::default();
if rx_status.rxovr().bit_is_set() {
errors_val.overflow = true;
}
if rx_status.rxfrm().bit_is_set() {
errors_val.framing = true;
}
if rx_status.rxpar().bit_is_set() {
errors_val.parity = true;
}
return Some(errors_val);
}
None
}
fn on_interrupt_rx_common_post_processing<Uart: Instance>(
uart: &Uart,
rx_enabled: bool,
read_some_data: bool,
irq_end: u32,
) -> Option<UartErrors> {
if read_some_data {
RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed);
if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) {
UART_RX_WAKERS[Uart::IDX as usize].wake();
}
}
let mut errors = None;
// Check for RX errors
if rx_enabled {
errors = on_interrupt_handle_rx_errors(uart);
}
// Clear the interrupt status bits
uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
errors
}
/// Interrupt handler for UART A.
///
/// 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_a_overwriting<const N: usize>(
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(
unsafe { pac::Uarta::steal() },
prod,
shared_consumer,
)
}
/// Interrupt handler for UART B.
///
/// 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>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(
unsafe { pac::Uartb::steal() },
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 mut read_some_data = false;
let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_end.irq_rx().bit_is_set() {
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..available_bytes {
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
cons_ref.as_mut().unwrap().dequeue();
});
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
// Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() {
while uart.rxstatus().read().rdavl().bit_is_set() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
cons_ref.as_mut().unwrap().dequeue();
});
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
let uart_errors =
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors {
queue_overflow,
uart_errors: uart_errors.unwrap_or_default(),
});
}
Ok(())
}
/// Interrupt handler for UART A.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_uart_a<const N: usize>(
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod)
}
/// Interrupt handler for UART B.
///
/// 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>,
) -> Result<(), AsyncUartErrors> {
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 enb_status = uart.enable().read();
let rx_enabled = enb_status.rxenable().bit_is_set();
let mut read_some_data = false;
let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_end.irq_rx().bit_is_set() {
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..available_bytes {
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
// Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() {
while uart.rxstatus().read().rdavl().bit_is_set() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
let uart_errors =
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors {
queue_overflow,
uart_errors: uart_errors.unwrap_or_default(),
});
}
Ok(())
}
struct ActiveReadGuard(usize);
impl Drop for ActiveReadGuard {
fn drop(&mut self) {
RX_READ_ACTIVE[self.0].store(false, Ordering::Relaxed);
}
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, data will be lost.
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> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
/// Create a new asynchronous receiver.
///
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
/// is filled by the interrupt handler.
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
// Enable those together.
critical_section::with(|_| {
rx.enable_interrupts();
rx.enable();
});
Self { rx, queue }
}
}
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> {
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
if self.queue.len() == 0 {
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
}
let _guard = ActiveReadGuard(Uart::IDX as usize);
let mut handle_data_in_queue = |consumer: &mut heapless::spsc::Consumer<'static, u8, N>| {
let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) {
// We own the consumer and we checked that the amount of data is guaranteed to be available.
*byte = unsafe { consumer.dequeue_unchecked() };
}
data_to_read
};
let fut = RxFuture::new(&mut self.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(&mut self.queue);
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
Ok(handle_data_in_queue(&mut self.queue))
}
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
/// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers.
pub struct RxAsyncSharedConsumer<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
queue: &'static Mutex<RefCell<Option<Consumer<'static, u8, N>>>>,
}
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncSharedConsumer<Uart, N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
/// Create a new asynchronous receiver.
///
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
/// which is filled by the interrupt handler. The shared property allows using it in the
/// interrupt handler to overwrite old data.
pub fn new(
mut rx: Rx<Uart>,
queue: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Self {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
// Enable those together.
critical_section::with(|_| {
rx.enable_interrupts();
rx.enable();
});
Self { rx, queue }
}
}
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedConsumer<Uart, N> {
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
// empty, we can read data immediately.
critical_section::with(|cs| {
let queue = self.queue.borrow(cs);
if queue.borrow().as_ref().unwrap().len() == 0 {
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
}
});
let _guard = ActiveReadGuard(Uart::IDX as usize);
let mut handle_data_in_queue = || {
critical_section::with(|cs| {
let mut consumer_ref = self.queue.borrow(cs).borrow_mut();
let consumer = consumer_ref.as_mut().unwrap();
let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) {
// We own the consumer and we checked that the amount of data is guaranteed to be available.
*byte = unsafe { consumer.dequeue_unchecked() };
}
data_to_read
})
};
let fut = RxFuture::new(&mut self.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue();
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
let read_data = handle_data_in_queue();
Ok(read_data)
}
}

View File

@ -0,0 +1,264 @@
//! # Async UART transmission functionality for the VA108xx family.
//!
//! 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
//! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it provides two interrupt handlers:
//!
//! - [on_interrupt_uart_a_tx]
//! - [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
//!
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs)
use core::{cell::RefCell, future::Future};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io_async::Write;
use portable_atomic::AtomicBool;
use super::*;
static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
// Completion flag. Kept outside of the context structure as an atomic to avoid
// critical section.
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
/// 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 A TX interrupts for
/// asynchronous operations to work.
pub fn on_interrupt_uart_a_tx() {
on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
}
/// 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();
// IRQ is not related to TX.
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
return;
}
let tx_status = uart.txstatus().read();
let unexpected_overrun = tx_status.wrlost().bit_is_set();
let mut context = critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow()
});
context.tx_overrun = unexpected_overrun;
if context.progress >= context.slice.len && !tx_status.wrbusy().bit_is_set() {
uart.irq_enb().modify(|_, w| {
w.irq_tx().clear_bit();
w.irq_tx_empty().clear_bit();
w.irq_tx_status().clear_bit()
});
uart.enable().modify(|_, w| w.txenable().clear_bit());
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow_mut() = context;
});
// Transfer is done.
TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
UART_TX_WAKERS[Uart::IDX as usize].wake();
return;
}
// Safety: We documented that the user provided slice must outlive the future, so we convert
// the raw pointer back to the slice here.
let slice = unsafe { core::slice::from_raw_parts(context.slice.data, context.slice.len) };
while context.progress < context.slice.len {
let wrrdy = uart.txstatus().read().wrrdy().bit_is_set();
if !wrrdy {
break;
}
// Safety: TX structure is owned by the future which does not write into the the data
// register, so we can assume we are the only one writing to the data register.
uart.data()
.write(|w| unsafe { w.bits(slice[context.progress] as u32) });
context.progress += 1;
}
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow_mut() = context;
});
}
#[derive(Debug, Copy, Clone)]
pub struct TxContext {
progress: usize,
tx_overrun: bool,
slice: RawBufSlice,
}
#[allow(clippy::new_without_default)]
impl TxContext {
pub const fn new() -> Self {
Self {
progress: 0,
tx_overrun: false,
slice: RawBufSlice::new_empty(),
}
}
}
#[derive(Debug, Copy, Clone)]
struct RawBufSlice {
data: *const u8,
len: usize,
}
/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
unsafe impl Send for RawBufSlice {}
impl RawBufSlice {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
#[allow(dead_code)]
const unsafe fn new(data: &[u8]) -> Self {
Self {
data: data.as_ptr(),
len: data.len(),
}
}
const fn new_empty() -> Self {
Self {
data: core::ptr::null(),
len: 0,
}
}
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn set(&mut self, data: &[u8]) {
self.data = data.as_ptr();
self.len = data.len();
}
}
pub struct TxFuture {
uart_idx: usize,
}
impl TxFuture {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn new<Uart: Instance>(tx: &mut Tx<Uart>, data: &[u8]) -> Self {
TX_DONE[Uart::IDX as usize].store(false, core::sync::atomic::Ordering::Relaxed);
tx.disable_interrupts();
tx.disable();
tx.clear_fifo();
let uart_tx = unsafe { tx.uart() };
let init_fill_count = core::cmp::min(data.len(), 16);
// We fill the FIFO.
for data in data.iter().take(init_fill_count) {
uart_tx.data().write(|w| unsafe { w.bits(*data as u32) });
}
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.slice.set(data);
context.progress = init_fill_count;
// Ensure those are enabled inside a critical section at the same time. Can lead to
// weird glitches otherwise.
tx.enable_interrupts();
tx.enable();
});
Self {
uart_idx: Uart::IDX as usize,
}
}
}
impl Future for TxFuture {
type Output = Result<usize, TxOverrunError>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_TX_WAKERS[self.uart_idx].register(cx.waker());
if TX_DONE[self.uart_idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
let progress = critical_section::with(|cs| {
TX_CONTEXTS[self.uart_idx].borrow(cs).borrow().progress
});
return core::task::Poll::Ready(Ok(progress));
}
core::task::Poll::Pending
}
}
impl Drop for TxFuture {
fn drop(&mut self) {
let reg_block = match self.uart_idx {
0 => unsafe { pac::Uarta::reg_block() },
1 => unsafe { pac::Uartb::reg_block() },
_ => unreachable!(),
};
disable_tx_interrupts(reg_block);
disable_tx(reg_block);
}
}
pub struct TxAsync<Uart: Instance> {
tx: Tx<Uart>,
}
impl<Uart: Instance> TxAsync<Uart> {
pub fn new(tx: Tx<Uart>) -> Self {
Self { tx }
}
pub fn release(self) -> Tx<Uart> {
self.tx
}
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("TX overrun error")]
pub struct TxOverrunError;
impl embedded_io_async::Error for TxOverrunError {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
}
}
impl<Uart: Instance> embedded_io::ErrorType for TxAsync<Uart> {
type Error = TxOverrunError;
}
impl<Uart: Instance> Write for TxAsync<Uart> {
/// Write a buffer asynchronously.
///
/// This implementation is not side effect free, and a started future might have already
/// written part of the passed buffer.
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
fut.await
}
}

View File

@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.4.0] ## [v0.4.0] 2025-02-12
- Re-generated PAC with `svd2rust` v0.35.0 - Re-generated PAC with `svd2rust` v0.35.0

View File

@ -1,8 +1,10 @@
#![doc = "Peripheral access API for VA108XX microcontrollers (generated using svd2rust v0.35.0 (dac8766 2025-02-08))\n\nYou can find an overview of the generated API [here].\n\nAPI features to be included in the [next] #![doc = "Peripheral access API for VA108XX microcontrollers (generated using svd2rust v0.35.0 (e10f920 2025-02-12))\n\nYou can find an overview of the generated API [here].\n\nAPI features to be included in the [next]
svd2rust release can be generated by cloning the svd2rust [repository], checking out the above commit, and running `cargo doc --open`.\n\n[here]: https://docs.rs/svd2rust/0.35.0/svd2rust/#peripheral-api\n[next]: https://github.com/rust-embedded/svd2rust/blob/master/CHANGELOG.md#unreleased\n[repository]: https://github.com/rust-embedded/svd2rust"] svd2rust release can be generated by cloning the svd2rust [repository], checking out the above commit, and running `cargo doc --open`.\n\n[here]: https://docs.rs/svd2rust/0.35.0/svd2rust/#peripheral-api\n[next]: https://github.com/rust-embedded/svd2rust/blob/master/CHANGELOG.md#unreleased\n[repository]: https://github.com/rust-embedded/svd2rust"]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![no_std] #![no_std]
// Manually inserted.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::marker::PhantomData; use core::marker::PhantomData;
use core::ops::Deref; use core::ops::Deref;
#[doc = r"Number available in the NVIC for configuring priority"] #[doc = r"Number available in the NVIC for configuring priority"]

View File

@ -19,7 +19,8 @@ bitfield = ">=0.17, <=0.18"
max116xx-10bit = "0.3" max116xx-10bit = "0.3"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = ">=0.8, <0.9" path = "../va108xx-hal"
version = "0.9"
features = ["rt"] features = ["rt"]
[features] [features]

View File

@ -31,7 +31,7 @@ fn main() -> ! {
rprintln!("-- Vorago Accelerometer Example --"); rprintln!("-- Vorago Accelerometer Example --");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
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);
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let (sck, mosi, miso) = ( let (sck, mosi, miso) = (
pinsa.pa20.into_funsel_2(), pinsa.pa20.into_funsel_2(),
pinsa.pa19.into_funsel_2(), pinsa.pa19.into_funsel_2(),

View File

@ -13,7 +13,7 @@ use va108xx_hal::{
gpio::{FilterType, InterruptEdge, PinsA}, gpio::{FilterType, InterruptEdge, PinsA},
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
timer::{default_ms_irq_handler, set_up_ms_tick, IrqCfg}, timer::{default_ms_irq_handler, set_up_ms_tick, InterruptConfig},
}; };
use vorago_reb1::button::Button; use vorago_reb1::button::Button;
use vorago_reb1::leds::Leds; use vorago_reb1::leds::Leds;
@ -35,28 +35,29 @@ fn main() -> ! {
rtt_init_print!(); rtt_init_print!();
rprintln!("-- Vorago Button IRQ Example --"); rprintln!("-- Vorago Button IRQ Example --");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let edge_irq = match PRESS_MODE { let edge_irq = match PRESS_MODE {
PressMode::Toggle => InterruptEdge::HighToLow, PressMode::Toggle => InterruptEdge::HighToLow,
PressMode::Keep => InterruptEdge::BothEdges, PressMode::Keep => InterruptEdge::BothEdges,
}; };
// 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()).edge_irq( let mut button = Button::new(pinsa.pa11.into_floating_input());
button.configure_edge_interrupt(
edge_irq, edge_irq,
IrqCfg::new(pac::interrupt::OC15, true, true), InterruptConfig::new(pac::interrupt::OC15, true, true),
Some(&mut dp.sysconfig), Some(&mut dp.sysconfig),
Some(&mut dp.irqsel), 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 = button.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);
} }
set_up_ms_tick( set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true), InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),

View File

@ -61,7 +61,7 @@ fn main() -> ! {
} }
} }
LibType::Hal => { LibType::Hal => {
let pins = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let pins = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led1 = pins.pa10.into_readable_push_pull_output(); let mut led1 = pins.pa10.into_readable_push_pull_output();
let mut led2 = pins.pa7.into_readable_push_pull_output(); let mut led2 = pins.pa7.into_readable_push_pull_output();
let mut led3 = pins.pa6.into_readable_push_pull_output(); let mut led3 = pins.pa6.into_readable_push_pull_output();
@ -87,27 +87,25 @@ fn main() -> ! {
} }
} }
LibType::Bsp => { LibType::Bsp => {
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
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(),
pinsa.pa6.into_push_pull_output(), pinsa.pa6.into_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);
loop { for _ in 0..10 {
for _ in 0..10 { // Blink all LEDs quickly
// Blink all LEDs quickly for led in leds.iter_mut() {
for led in leds.iter_mut() { led.toggle();
led.toggle();
}
delay.delay_ms(500);
} }
// Now use a wave pattern delay.delay_ms(500);
loop { }
for led in leds.iter_mut() { // Now use a wave pattern
led.toggle(); loop {
delay.delay_ms(200); for led in leds.iter_mut() {
} led.toggle();
delay.delay_ms(200);
} }
} }
} }

View File

@ -22,9 +22,9 @@ use va108xx_hal::{
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
spi::{Spi, SpiBase, SpiConfig}, spi::{Spi, SpiBase, SpiConfig},
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, IrqCfg}, timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, InterruptConfig},
}; };
use va108xx_hal::{port_mux, FunSel, PortSel}; 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,
@ -112,7 +112,7 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let tim0 = set_up_ms_tick( let tim0 = set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true), InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
SYS_CLK, SYS_CLK,
@ -123,7 +123,7 @@ fn main() -> ! {
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0); cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
} }
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta); let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let spi_cfg = SpiConfig::default() let spi_cfg = SpiConfig::default()
.clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap()) .clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap())
.mode(MODE_0) .mode(MODE_0)
@ -135,10 +135,10 @@ fn main() -> ! {
); );
if MUX_MODE == MuxMode::PortB19to17 { if MUX_MODE == MuxMode::PortB19to17 {
port_mux(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok(); port_function_select(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok();
port_mux(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok(); port_function_select(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok();
port_mux(&mut dp.ioconfig, PortSel::PortB, 17, FunSel::Sel1).ok(); port_function_select(&mut dp.ioconfig, PortSel::PortB, 17, FunSel::Sel1).ok();
port_mux(&mut dp.ioconfig, PortSel::PortB, 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();

View File

@ -7,9 +7,10 @@
use embedded_hal::digital::InputPin; 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},
pac, IrqCfg, pac, InterruptConfig,
}; };
#[derive(Debug)]
pub struct Button { pub struct Button {
button: Pin<PA11, InputFloating>, button: Pin<PA11, InputFloating>,
} }
@ -30,37 +31,34 @@ impl Button {
} }
/// Configures an IRQ on edge. /// Configures an IRQ on edge.
pub fn edge_irq( pub fn configure_edge_interrupt(
mut self, &mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>, syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>, irqsel: Option<&mut pac::Irqsel>,
) -> Self { ) {
self.button = self self.button
.button .configure_edge_interrupt(edge_type, irq_cfg, syscfg, irqsel);
.interrupt_edge(edge_type, irq_cfg, syscfg, irqsel);
self
} }
/// Configures an IRQ on level. /// Configures an IRQ on level.
pub fn level_irq( pub fn configure_level_interrupt(
mut self, &mut self,
level: InterruptLevel, level: InterruptLevel,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>, syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>, irqsel: Option<&mut pac::Irqsel>,
) -> Self { ) {
self.button = self.button.interrupt_level(level, irq_cfg, syscfg, irqsel); self.button
self .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.
/// ///
/// Please note that you still have to set a clock divisor yourself using the /// Please note that you still have to set a clock divisor yourself using the
/// [`va108xx_hal::clock::set_clk_div_register`] function in order for this to work. /// [`va108xx_hal::clock::set_clk_div_register`] function in order for this to work.
pub fn filter_type(mut self, filter: FilterType, clksel: FilterClkSel) -> Self { pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.button = self.button.filter_type(filter, clksel); self.button.configure_filter_type(filter, clksel);
self
} }
} }

View File

@ -363,8 +363,8 @@
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"device": "Cortex-M0", "device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched", "svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "rust: cargo build uart irq", "preLaunchTask": "uart-echo-rtic-example",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-rtic", "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic",
"interface": "jtag", "interface": "jtag",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"rttConfig": { "rttConfig": {
@ -499,5 +499,77 @@
] ]
} }
}, },
{
"type": "cortex-debug",
"request": "launch",
"name": "Async GPIO",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "async-gpio",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-gpio",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Async UART TX",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "async-uart-tx",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-tx",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Async UART RX",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "async-uart-rx",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-rx",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
] ]
} }

View File

@ -266,6 +266,36 @@
"embassy-example" "embassy-example"
] ]
}, },
{
"label": "async-gpio",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-gpio"
]
},
{
"label": "async-uart-tx",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-uart-tx"
]
},
{
"label": "async-uart-rx",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-uart-rx"
]
},
{ {
"label": "bootloader", "label": "bootloader",
"type": "shell", "type": "shell",
@ -289,4 +319,4 @@
] ]
} }
] ]
} }