Compare commits
1 Commits
vorago-reb
...
5c17d85a5e
Author | SHA1 | Date | |
---|---|---|---|
5c17d85a5e |
@@ -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, InterruptConfig},
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, IrqCfg},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[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, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), 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(
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
|
@@ -8,11 +8,6 @@ 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"
|
||||||
@@ -27,7 +22,7 @@ embassy-executor = { version = "0.7", features = [
|
|||||||
"executor-interrupt"
|
"executor-interrupt"
|
||||||
]}
|
]}
|
||||||
|
|
||||||
va108xx-hal = "0.9"
|
va108xx-hal = { path = "../../va108xx-hal" }
|
||||||
va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
|
va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@@ -1,258 +0,0 @@
|
|||||||
//! 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();
|
|
||||||
}
|
|
@@ -1,171 +0,0 @@
|
|||||||
//! 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);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,97 +0,0 @@
|
|||||||
//! 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();
|
|
||||||
}
|
|
@@ -52,7 +52,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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();
|
||||||
|
@@ -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.9"
|
va108xx-hal = "0.8"
|
||||||
vorago-reb1 = { path = "../../vorago-reb1" }
|
vorago-reb1 = { path = "../../vorago-reb1" }
|
||||||
|
@@ -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, InterruptConfig},
|
timer::{default_ms_irq_handler, set_up_ms_tick, IrqCfg},
|
||||||
};
|
};
|
||||||
use vorago_reb1::button::Button;
|
use vorago_reb1::button::Button;
|
||||||
use vorago_reb1::leds::Leds;
|
use vorago_reb1::leds::Leds;
|
||||||
@@ -61,24 +61,23 @@ 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, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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());
|
let mut button = Button::new(pinsa.pa11.into_floating_input()).edge_irq(
|
||||||
button.configure_edge_interrupt(
|
|
||||||
edge_irq,
|
edge_irq,
|
||||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
IrqCfg::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.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
button = button.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(
|
||||||
@@ -90,7 +89,7 @@ mod app {
|
|||||||
led.off();
|
led.off();
|
||||||
}
|
}
|
||||||
set_up_ms_tick(
|
set_up_ms_tick(
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
|
@@ -23,13 +23,12 @@ mod app {
|
|||||||
gpio::PinsA,
|
gpio::PinsA,
|
||||||
pac,
|
pac,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
uart::{self, RxWithInterrupt, Tx},
|
uart::{self, RxWithIrq, Tx},
|
||||||
InterruptConfig,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
rx: RxWithInterrupt<pac::Uarta>,
|
rx: RxWithIrq<pac::Uarta>,
|
||||||
tx: Tx<pac::Uarta>,
|
tx: Tx<pac::Uarta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,20 +47,19 @@ 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, dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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_with_interrupt(
|
let irq_uart = uart::Uart::new(
|
||||||
&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();
|
let mut rx = rx.into_rx_with_irq(&mut dp.sysconfig, &mut dp.irqsel, pac::interrupt::OC3);
|
||||||
|
|
||||||
rx.start();
|
rx.start();
|
||||||
|
|
||||||
@@ -92,7 +90,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.on_interrupt(&mut buf);
|
let result = cx.local.rx.irq_handler(&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 {
|
||||||
|
@@ -35,7 +35,11 @@ mod app {
|
|||||||
|
|
||||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
let porta = PinsA::new(&mut cx.device.sysconfig, cx.device.porta);
|
let porta = PinsA::new(
|
||||||
|
&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();
|
||||||
|
@@ -16,7 +16,7 @@ embedded-io = "0.6"
|
|||||||
cortex-m-semihosting = "0.5.0"
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.9"
|
version = "0.8"
|
||||||
features = ["rt", "defmt"]
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
[dependencies.vorago-reb1]
|
[dependencies.vorago-reb1]
|
||||||
|
@@ -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},
|
||||||
InterruptConfig,
|
IrqCfg,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[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(
|
||||||
InterruptConfig::new(interrupt::OC0, true, true),
|
IrqCfg::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, dp.porta);
|
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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();
|
||||||
|
@@ -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, InterruptConfig,
|
CountdownTimer, Event, IrqCfg,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -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,
|
||||||
InterruptConfig::new(pac::Interrupt::OC1, true, false),
|
IrqCfg::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,
|
||||||
InterruptConfig::new(pac::Interrupt::OC2, true, false),
|
IrqCfg::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,
|
||||||
InterruptConfig::new(pac::Interrupt::OC3, true, false),
|
IrqCfg::new(pac::Interrupt::OC3, true, false),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
|
@@ -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, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
let mut pwm = pwm::PwmPin::new(
|
let mut pwm = pwm::PwmPin::new(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
|
@@ -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},
|
||||||
InterruptConfig,
|
IrqCfg,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[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(
|
||||||
InterruptConfig::new(interrupt::OC0, true, true),
|
IrqCfg::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, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb);
|
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), 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 {
|
||||||
|
@@ -12,9 +12,7 @@ use va108xx_hal::{
|
|||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer::{
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, IrqCfg, MS_COUNTER},
|
||||||
default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, InterruptConfig, MS_COUNTER,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -67,7 +65,7 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
LibType::Hal => {
|
LibType::Hal => {
|
||||||
set_up_ms_tick(
|
set_up_ms_tick(
|
||||||
InterruptConfig::new(interrupt::OC0, true, true),
|
IrqCfg::new(interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
@@ -77,7 +75,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,
|
||||||
InterruptConfig::new(interrupt::OC1, true, true),
|
IrqCfg::new(interrupt::OC1, true, true),
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
Some(&mut dp.sysconfig),
|
Some(&mut dp.sysconfig),
|
||||||
);
|
);
|
||||||
|
@@ -24,18 +24,12 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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 (mut tx, mut rx) = uart.split();
|
let uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz());
|
||||||
|
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.
|
||||||
|
@@ -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, InterruptConfig};
|
use va108xx_hal::{pac, uart};
|
||||||
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::RxWithInterrupt<pac::Uarta>,
|
uart_rx: uart::RxWithIrq<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,21 +110,19 @@ 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, dp.porta);
|
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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_with_interrupt(
|
let irq_uart = uart::Uart::new(
|
||||||
&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();
|
||||||
// Unwrap is okay, we explicitely set the interrupt ID.
|
let mut rx = rx.into_rx_with_irq(&mut dp.sysconfig, &mut dp.irqsel, pac::interrupt::OC0);
|
||||||
let mut rx = rx.into_rx_with_irq();
|
|
||||||
|
|
||||||
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
||||||
|
|
||||||
@@ -177,7 +175,7 @@ mod app {
|
|||||||
match cx
|
match cx
|
||||||
.local
|
.local
|
||||||
.uart_rx
|
.uart_rx
|
||||||
.on_interrupt_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
|
.irq_handler_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
if RX_DEBUGGING {
|
if RX_DEBUGGING {
|
||||||
|
@@ -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_nvic_interrupt, pac,
|
enable_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_nvic_interrupt(timekeeper_irq);
|
enable_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_nvic_interrupt(alarm_irq);
|
enable_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) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -10,49 +10,13 @@ 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.
|
||||||
- `Uart::with_with_interrupt` and `Uart::new_without_interrupt`
|
- Simplified PWM module implementation.
|
||||||
|
|
||||||
## [v0.8.0] 2024-09-30
|
## [v0.8.0] 2024-09-30
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "va108xx-hal"
|
name = "va108xx-hal"
|
||||||
version = "0.9.0"
|
version = "0.8.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,29 +16,18 @@ 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.4", default-features = false, features = ["critical-section"] }
|
va108xx = { version = "0.3", 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"]
|
||||||
|
@@ -25,6 +25,12 @@ 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
|
||||||
@@ -59,11 +65,3 @@ 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/)
|
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
|
||||||
cargo +nightly doc --all-features --open
|
|
@@ -11,7 +11,6 @@ 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,
|
||||||
@@ -40,27 +39,13 @@ 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 => {
|
FilterClkSel::Clk1 => syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) }),
|
||||||
syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) });
|
FilterClkSel::Clk2 => syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) }),
|
||||||
}
|
FilterClkSel::Clk3 => syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) }),
|
||||||
FilterClkSel::Clk2 => {
|
FilterClkSel::Clk4 => syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) }),
|
||||||
syscfg.ioconfig_clkdiv2().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::Clk3 => {
|
FilterClkSel::Clk7 => syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) }),
|
||||||
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) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,449 +0,0 @@
|
|||||||
//! # Async GPIO functionality for the VA108xx family.
|
|
||||||
//!
|
|
||||||
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
|
||||||
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
|
|
||||||
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
|
|
||||||
//! which must be provided for async support to work. However, it provides 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(())
|
|
||||||
}
|
|
||||||
}
|
|
@@ -59,9 +59,8 @@
|
|||||||
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, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
|
use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// DynPinMode configurations
|
// DynPinMode configurations
|
||||||
@@ -69,7 +68,6 @@ use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptCo
|
|||||||
|
|
||||||
/// 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,
|
||||||
@@ -77,8 +75,7 @@ pub enum DynDisabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Value-level `enum` for input configurations
|
/// Value-level `enum` for input configurations
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum DynInput {
|
pub enum DynInput {
|
||||||
Floating,
|
Floating,
|
||||||
PullDown,
|
PullDown,
|
||||||
@@ -86,8 +83,7 @@ pub enum DynInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Value-level `enum` for output configurations
|
/// Value-level `enum` for output configurations
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum DynOutput {
|
pub enum DynOutput {
|
||||||
PushPull,
|
PushPull,
|
||||||
OpenDrain,
|
OpenDrain,
|
||||||
@@ -105,10 +101,9 @@ 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, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("Invalid pin type for operation: {0:?}")]
|
pub struct InvalidPinTypeError;
|
||||||
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 {
|
||||||
@@ -121,8 +116,7 @@ impl embedded_hal::digital::Error for InvalidPinTypeError {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Value-level `enum` representing pin modes
|
/// Value-level `enum` representing pin modes
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(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),
|
||||||
@@ -157,16 +151,14 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Value-level `enum` for pin groups
|
/// Value-level `enum` for pin groups
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(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(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(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,
|
||||||
@@ -180,15 +172,16 @@ 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.
|
||||||
#[derive(Debug)]
|
pub(crate) struct DynRegisters {
|
||||||
pub(crate) struct DynRegisters(DynPinId);
|
id: 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.0
|
self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +194,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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +206,6 @@ 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,
|
||||||
@@ -238,7 +230,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.0
|
self.regs.id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin mode
|
/// Return a copy of the pin mode
|
||||||
@@ -257,11 +249,6 @@ 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);
|
||||||
@@ -356,7 +343,7 @@ impl DynPin {
|
|||||||
|
|
||||||
pub(crate) fn irq_enb(
|
pub(crate) fn irq_enb(
|
||||||
&mut self,
|
&mut self,
|
||||||
irq_cfg: crate::InterruptConfig,
|
irq_cfg: crate::IrqCfg,
|
||||||
syscfg: Option<&mut va108xx::Sysconfig>,
|
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||||
irqsel: Option<&mut va108xx::Irqsel>,
|
irqsel: Option<&mut va108xx::Irqsel>,
|
||||||
) {
|
) {
|
||||||
@@ -371,19 +358,16 @@ 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.id as u32) });
|
.write(|w| unsafe { w.bits(irq_cfg.irq 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.id as u32) });
|
.write(|w| unsafe { w.bits(irq_cfg.irq 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.
|
||||||
@@ -398,7 +382,7 @@ impl DynPin {
|
|||||||
self.regs.delay(delay_1, delay_2);
|
self.regs.delay(delay_1, delay_2);
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,7 +400,7 @@ impl DynPin {
|
|||||||
self.regs.pulse_mode(enable, default_state);
|
self.regs.pulse_mode(enable, default_state);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,7 +416,7 @@ impl DynPin {
|
|||||||
self.regs.filter_type(filter, clksel);
|
self.regs.filter_type(filter, clksel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,7 +424,7 @@ impl DynPin {
|
|||||||
pub fn interrupt_edge(
|
pub fn interrupt_edge(
|
||||||
&mut self,
|
&mut self,
|
||||||
edge_type: InterruptEdge,
|
edge_type: InterruptEdge,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
syscfg: Option<&mut pac::Sysconfig>,
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
irqsel: Option<&mut pac::Irqsel>,
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
@@ -450,7 +434,7 @@ impl DynPin {
|
|||||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +442,7 @@ impl DynPin {
|
|||||||
pub fn interrupt_level(
|
pub fn interrupt_level(
|
||||||
&mut self,
|
&mut self,
|
||||||
level_type: InterruptLevel,
|
level_type: InterruptLevel,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
syscfg: Option<&mut pac::Sysconfig>,
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
irqsel: Option<&mut pac::Irqsel>,
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) -> Result<(), InvalidPinTypeError> {
|
) -> Result<(), InvalidPinTypeError> {
|
||||||
@@ -468,7 +452,7 @@ impl DynPin {
|
|||||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +463,7 @@ impl DynPin {
|
|||||||
self.regs.toggle();
|
self.regs.toggle();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,7 +473,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(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -499,7 +483,7 @@ impl DynPin {
|
|||||||
self.regs.write_pin(bit);
|
self.regs.write_pin(bit);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InvalidPinTypeError(self.mode)),
|
_ => Err(InvalidPinTypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,21 +511,12 @@ 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.0 == I::DYN && self.mode == M::DYN {
|
if self.regs.id == 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(self.mode))
|
Err(InvalidPinTypeError)
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,22 +22,14 @@
|
|||||||
//!
|
//!
|
||||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("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;
|
||||||
|
@@ -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, InputPinAsync};
|
use super::DynPin;
|
||||||
use crate::{
|
use crate::{
|
||||||
pac::{Irqsel, Porta, Portb, Sysconfig},
|
pac::{Irqsel, Porta, Portb, Sysconfig},
|
||||||
typelevel::Sealed,
|
typelevel::Sealed,
|
||||||
InterruptConfig,
|
IrqCfg,
|
||||||
};
|
};
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
@@ -119,11 +119,8 @@ 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 {
|
||||||
@@ -151,7 +148,6 @@ 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>,
|
||||||
}
|
}
|
||||||
@@ -181,17 +177,13 @@ 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 {}
|
||||||
@@ -218,7 +210,6 @@ 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>,
|
||||||
}
|
}
|
||||||
@@ -313,7 +304,6 @@ 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 {
|
||||||
@@ -331,7 +321,6 @@ 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)>,
|
||||||
@@ -353,10 +342,6 @@ 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> {
|
||||||
@@ -465,7 +450,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
|
|
||||||
fn irq_enb(
|
fn irq_enb(
|
||||||
&mut self,
|
&mut self,
|
||||||
irq_cfg: crate::InterruptConfig,
|
irq_cfg: crate::IrqCfg,
|
||||||
syscfg: Option<&mut va108xx::Sysconfig>,
|
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||||
irqsel: Option<&mut va108xx::Irqsel>,
|
irqsel: Option<&mut va108xx::Irqsel>,
|
||||||
) {
|
) {
|
||||||
@@ -586,16 +571,10 @@ 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>> {
|
||||||
/// Convert the pin into an async pin. The pin can be converted back by calling
|
pub fn interrupt_edge(
|
||||||
/// [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: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
syscfg: Option<&mut Sysconfig>,
|
syscfg: Option<&mut Sysconfig>,
|
||||||
irqsel: Option<&mut Irqsel>,
|
irqsel: Option<&mut Irqsel>,
|
||||||
) {
|
) {
|
||||||
@@ -603,10 +582,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 configure_level_interrupt(
|
pub fn interrupt_level(
|
||||||
&mut self,
|
&mut self,
|
||||||
level_type: InterruptLevel,
|
level_type: InterruptLevel,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
syscfg: Option<&mut Sysconfig>,
|
syscfg: Option<&mut Sysconfig>,
|
||||||
irqsel: Option<&mut Irqsel>,
|
irqsel: Option<&mut Irqsel>,
|
||||||
) {
|
) {
|
||||||
@@ -642,7 +621,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: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
syscfg: Option<&mut Sysconfig>,
|
syscfg: Option<&mut Sysconfig>,
|
||||||
irqsel: Option<&mut Irqsel>,
|
irqsel: Option<&mut Irqsel>,
|
||||||
) {
|
) {
|
||||||
@@ -653,7 +632,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: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
syscfg: Option<&mut Sysconfig>,
|
syscfg: Option<&mut Sysconfig>,
|
||||||
irqsel: Option<&mut Irqsel>,
|
irqsel: Option<&mut Irqsel>,
|
||||||
) {
|
) {
|
||||||
@@ -665,7 +644,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 configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
pub fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
self.inner.regs.filter_type(filter, clksel);
|
self.inner.regs.filter_type(filter, clksel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -752,8 +731,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]
|
||||||
@@ -768,6 +747,7 @@ 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| {
|
||||||
@@ -776,7 +756,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`
|
||||||
$(
|
$(
|
||||||
@@ -793,8 +773,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) -> $Port {
|
pub fn release(self) -> (Option<va108xx::Ioconfig>, $Port) {
|
||||||
self.port
|
(self.iocfg, self.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -73,6 +73,13 @@ 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
|
||||||
///
|
///
|
||||||
@@ -304,7 +311,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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +323,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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,50 +18,42 @@ 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, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("clock too slow for fast I2C mode")]
|
pub struct ClockTooSlowForFastI2c;
|
||||||
pub struct ClockTooSlowForFastI2cError;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[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 {
|
||||||
//#[error("Invalid timing parameters")]
|
InvalidTimingParams,
|
||||||
//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, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[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.
|
||||||
#[error("clock too slow for fast I2C mode: {0}")]
|
ClkTooSlow(ClockTooSlowForFastI2c),
|
||||||
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 {
|
||||||
@@ -74,7 +66,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::DataTooLarge | Error::InsufficientDataReceived | Error::InvalidTimingParams => {
|
||||||
embedded_hal::i2c::ErrorKind::Other
|
embedded_hal::i2c::ErrorKind::Other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,21 +82,18 @@ 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),
|
||||||
@@ -145,12 +134,9 @@ 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,
|
||||||
@@ -174,7 +160,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, InvalidTimingParamsError> {
|
) -> Result<Self, Error> {
|
||||||
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
|
||||||
@@ -184,7 +170,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(InvalidTimingParamsError);
|
return Err(Error::InvalidTimingParams);
|
||||||
}
|
}
|
||||||
Ok(TimingCfg {
|
Ok(TimingCfg {
|
||||||
tr: first_16_bits.0,
|
tr: first_16_bits.0,
|
||||||
@@ -225,7 +211,6 @@ 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,
|
||||||
@@ -314,7 +299,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, ClockTooSlowForFastI2cError> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
enable_peripheral_clock(syscfg, I2c::PERIPH_SEL);
|
enable_peripheral_clock(syscfg, I2c::PERIPH_SEL);
|
||||||
|
|
||||||
let mut i2c_base = I2cBase {
|
let mut i2c_base = I2cBase {
|
||||||
@@ -392,12 +377,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) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,22 +402,19 @@ impl<I2c: Instance> I2cBase<I2c> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
|
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2c> {
|
||||||
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(ClockTooSlowForFastI2cError);
|
return Err(ClockTooSlowForFastI2c);
|
||||||
}
|
}
|
||||||
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(
|
pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2c> {
|
||||||
&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()
|
||||||
@@ -478,7 +460,7 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: MasterConfig,
|
cfg: MasterConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
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,
|
||||||
@@ -1008,7 +990,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: SlaveConfig,
|
cfg: SlaveConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
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,
|
||||||
@@ -1170,7 +1152,7 @@ impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
|
|||||||
i2c: I2c,
|
i2c: I2c,
|
||||||
cfg: SlaveConfig,
|
cfg: SlaveConfig,
|
||||||
speed_mode: I2cSpeed,
|
speed_mode: I2cSpeed,
|
||||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
) -> Result<Self, ClockTooSlowForFastI2c> {
|
||||||
Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)
|
Self::new_generic(sys_cfg, sys_clk, i2c, cfg, speed_mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,6 @@ 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,
|
||||||
@@ -25,14 +24,12 @@ 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,
|
||||||
@@ -49,39 +46,31 @@ pub enum PeripheralSelect {
|
|||||||
Gpio = 24,
|
Gpio = 24,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic interrupt config which can be used to specify whether the HAL driver will
|
/// Generic IRQ 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 want to
|
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
|
||||||
/// perform those steps themselves.
|
/// this steps themselves
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct InterruptConfig {
|
pub struct IrqCfg {
|
||||||
/// 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 id: pac::Interrupt,
|
pub irq: 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. If an interrupt is used for
|
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC
|
||||||
/// multiple purposes, the user can enable the interrupts themselves.
|
pub enable: bool,
|
||||||
pub enable_in_nvic: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterruptConfig {
|
impl IrqCfg {
|
||||||
pub fn new(id: pac::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
|
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self {
|
||||||
InterruptConfig {
|
IrqCfg { irq, route, enable }
|
||||||
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_function_select(
|
pub fn port_mux(
|
||||||
ioconfig: &mut pac::Ioconfig,
|
ioconfig: &mut pac::Ioconfig,
|
||||||
port: PortSel,
|
port: PortSel,
|
||||||
pin: u8,
|
pin: u8,
|
||||||
@@ -115,7 +104,7 @@ pub fn port_function_select(
|
|||||||
///
|
///
|
||||||
/// 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_nvic_interrupt(irq: pac::Interrupt) {
|
pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
|
||||||
unsafe {
|
unsafe {
|
||||||
cortex_m::peripheral::NVIC::unmask(irq);
|
cortex_m::peripheral::NVIC::unmask(irq);
|
||||||
}
|
}
|
||||||
@@ -123,6 +112,6 @@ pub unsafe fn enable_nvic_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_nvic_interrupt(irq: pac::Interrupt) {
|
pub fn disable_interrupt(irq: pac::Interrupt) {
|
||||||
cortex_m::peripheral::NVIC::mask(irq);
|
cortex_m::peripheral::NVIC::mask(irq);
|
||||||
}
|
}
|
||||||
|
@@ -15,9 +15,7 @@ use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
|||||||
|
|
||||||
const DUTY_MAX: u16 = u16::MAX;
|
const DUTY_MAX: u16 = u16::MAX;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct PwmCommon {
|
||||||
#[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,
|
||||||
@@ -82,7 +80,7 @@ where
|
|||||||
pin
|
pin
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downgrade(self) -> ReducedPwmPin<Mode> {
|
pub fn reduce(self) -> ReducedPwmPin<Mode> {
|
||||||
self.inner
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +213,22 @@ 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]
|
||||||
@@ -279,24 +293,6 @@ 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 {
|
||||||
|
@@ -37,7 +37,6 @@ 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,
|
||||||
@@ -51,7 +50,6 @@ 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,
|
||||||
@@ -60,7 +58,6 @@ 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,
|
||||||
@@ -574,13 +571,10 @@ impl SpiClkConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug)]
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,7 +786,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;
|
||||||
@@ -806,7 +800,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
|
||||||
}
|
}
|
||||||
@@ -815,7 +809,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;
|
||||||
@@ -829,7 +823,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
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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::InterruptConfig;
|
pub use crate::IrqCfg;
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||||
enable_nvic_interrupt,
|
enable_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,7 +79,6 @@ 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,
|
||||||
@@ -109,7 +108,6 @@ 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,
|
||||||
@@ -288,7 +286,7 @@ pub type TimRegBlock = tim0::RegisterBlock;
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Users should only implement the [Self::tim_id] function. No default function
|
/// Users should only implement the [`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.
|
||||||
@@ -321,7 +319,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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +330,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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +362,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<InterruptConfig>,
|
irq_cfg: Option<IrqCfg>,
|
||||||
sys_clk: Hertz,
|
sys_clk: Hertz,
|
||||||
rst_val: u32,
|
rst_val: u32,
|
||||||
last_cnt: u32,
|
last_cnt: u32,
|
||||||
@@ -417,13 +415,13 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn listen(
|
pub fn listen(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: Event,
|
event: Event,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
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.id);
|
cortex_m::peripheral::NVIC::mask(irq_cfg.irq);
|
||||||
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 {
|
||||||
@@ -432,7 +430,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.id as u32) });
|
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.listening = true;
|
self.listening = true;
|
||||||
@@ -522,8 +520,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_in_nvic {
|
if irq_cfg.enable {
|
||||||
unsafe { enable_nvic_interrupt(irq_cfg.id) };
|
unsafe { enable_interrupt(irq_cfg.irq) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.tim
|
self.tim
|
||||||
@@ -721,7 +719,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: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
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>,
|
||||||
|
@@ -7,11 +7,12 @@
|
|||||||
//! - [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::InterruptConfig;
|
pub use crate::IrqCfg;
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::enable_peripheral_clock,
|
clock::enable_peripheral_clock,
|
||||||
enable_nvic_interrupt,
|
enable_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,
|
||||||
@@ -22,13 +23,6 @@ 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
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@@ -54,35 +48,30 @@ impl Pins<pac::Uartb> for (Pin<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {}
|
|||||||
// Regular Definitions
|
// Regular Definitions
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("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, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[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, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("rx error: {0}")]
|
Rx(RxError),
|
||||||
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
|
||||||
@@ -238,7 +227,6 @@ 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,
|
||||||
@@ -264,19 +252,17 @@ 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<UartErrors>,
|
pub errors: Option<IrqUartError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<UartErrors>,
|
pub errors: Option<IrqUartError>,
|
||||||
pub bytes_read: usize,
|
pub bytes_read: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,22 +309,20 @@ 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)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
pub struct IrqUartError {
|
||||||
pub struct UartErrors {
|
|
||||||
overflow: bool,
|
overflow: bool,
|
||||||
framing: bool,
|
framing: bool,
|
||||||
parity: bool,
|
parity: bool,
|
||||||
other: bool,
|
other: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UartErrors {
|
impl IrqUartError {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn overflow(&self) -> bool {
|
pub fn overflow(&self) -> bool {
|
||||||
self.overflow
|
self.overflow
|
||||||
@@ -360,7 +344,7 @@ impl UartErrors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UartErrors {
|
impl IrqUartError {
|
||||||
#[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
|
||||||
@@ -389,16 +373,6 @@ 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 {
|
||||||
@@ -406,13 +380,11 @@ 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 {
|
||||||
Self::ptr() as *const _
|
Uarta::ptr() as *const _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,13 +393,11 @@ 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 {
|
||||||
Self::ptr() as *const _
|
Uarta::ptr() as *const _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,51 +592,15 @@ where
|
|||||||
UartInstance: Instance,
|
UartInstance: Instance,
|
||||||
PinsInstance: Pins<UartInstance>,
|
PinsInstance: Pins<UartInstance>,
|
||||||
{
|
{
|
||||||
/// 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,
|
|
||||||
sys_clk: impl Into<Hertz>,
|
|
||||||
uart: UartInstance,
|
|
||||||
pins: PinsInstance,
|
|
||||||
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(
|
pub fn new(
|
||||||
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>,
|
||||||
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);
|
||||||
let uart = Uart {
|
Uart {
|
||||||
inner: UartBase {
|
inner: UartBase {
|
||||||
uart,
|
uart,
|
||||||
tx: Tx::new(unsafe { UartInstance::steal() }),
|
tx: Tx::new(unsafe { UartInstance::steal() }),
|
||||||
@@ -674,21 +608,7 @@ 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
|
||||||
@@ -763,45 +683,14 @@ 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> {
|
pub struct Rx<Uart>(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.
|
||||||
@@ -809,33 +698,23 @@ 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.uart
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&self) {
|
||||||
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
|
self.0.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) {
|
||||||
enable_rx(unsafe { Uart::reg_block() });
|
self.0.enable().modify(|_, w| w.rxenable().set_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable(&mut self) {
|
pub fn disable(&mut self) {
|
||||||
disable_rx(unsafe { Uart::reg_block() });
|
self.0.enable().modify(|_, w| w.rxenable().clear_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Low level function to read a word from the UART FIFO.
|
/// Low level function to read a word from the UART FIFO.
|
||||||
@@ -846,7 +725,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.uart.rxstatus().read().rdavl().bit_is_clear() {
|
if self.0.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())
|
||||||
@@ -862,16 +741,20 @@ 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.uart.data().read().bits()
|
self.0.data().read().bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
|
pub fn into_rx_with_irq(
|
||||||
RxWithInterrupt::new(self)
|
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.uart
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -928,57 +811,14 @@ 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> {
|
pub struct Tx<Uart>(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.
|
||||||
@@ -986,47 +826,23 @@ 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.uart
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&self) {
|
||||||
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
|
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enable(&mut self) {
|
pub fn enable(&mut self) {
|
||||||
// Safety: We own the UART structure
|
self.0.enable().modify(|_, w| w.txenable().set_bit());
|
||||||
enable_tx(unsafe { Uart::reg_block() });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn disable(&mut self) {
|
pub fn disable(&mut self) {
|
||||||
// Safety: We own the UART structure
|
self.0.enable().modify(|_, w| w.txenable().clear_bit());
|
||||||
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.
|
||||||
@@ -1037,7 +853,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.uart.txstatus().read().wrrdy().bit_is_clear() {
|
if self.0.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);
|
||||||
@@ -1052,11 +868,7 @@ impl<Uart: Instance> Tx<Uart> {
|
|||||||
/// API.
|
/// API.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_fifo_unchecked(&self, data: u32) {
|
pub fn write_fifo_unchecked(&self, data: u32) {
|
||||||
self.uart.data().write(|w| unsafe { w.bits(data) });
|
self.0.data().write(|w| unsafe { w.bits(data) });
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_async(self) -> TxAsync<Uart> {
|
|
||||||
TxAsync::new(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1113,38 +925,51 @@ 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::on_interrupt] in the interrupt service routine.
|
/// [Self::irq_handler] 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::on_interrupt_max_size_or_timeout_based] in the interrupt service
|
/// then call the [Self::irq_handler_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 RxWithInterrupt<Uart>(Rx<Uart>);
|
pub struct RxWithIrq<Uart> {
|
||||||
|
pub rx: Rx<Uart>,
|
||||||
|
pub interrupt: pac::Interrupt,
|
||||||
|
}
|
||||||
|
|
||||||
impl<Uart: Instance> RxWithInterrupt<Uart> {
|
impl<Uart: Instance> RxWithIrq<Uart> {
|
||||||
pub fn new(rx: Rx<Uart>) -> Self {
|
pub fn new(
|
||||||
Self(rx)
|
rx: Rx<Uart>,
|
||||||
|
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::on_interrupt] is used to read the UART receiver to enable and start the receiver.
|
/// [Self::irq_handler] is used to read the UART receiver to enable and start the receiver.
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
self.0.enable();
|
self.rx.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.0.uart
|
&self.rx.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]
|
/// This function is used together with the [Self::irq_handler_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::on_interrupt_max_size_or_timeout_based] call to restart the reception
|
/// completed [Self::irq_handler_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,
|
||||||
@@ -1181,7 +1006,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
|||||||
|
|
||||||
pub fn cancel_transfer(&mut self) {
|
pub fn cancel_transfer(&mut self) {
|
||||||
self.disable_rx_irq_sources();
|
self.disable_rx_irq_sources();
|
||||||
self.0.clear_fifo();
|
self.rx.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.
|
||||||
@@ -1192,7 +1017,7 @@ impl<Uart: Instance> RxWithInterrupt<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 on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult {
|
pub fn irq_handler(&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();
|
||||||
@@ -1215,7 +1040,7 @@ impl<Uart: Instance> RxWithInterrupt<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.0.read();
|
let read_result = self.rx.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;
|
||||||
@@ -1249,7 +1074,7 @@ impl<Uart: Instance> RxWithInterrupt<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 on_interrupt_max_size_or_timeout_based(
|
pub fn irq_handler_max_size_or_timeout_based(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut IrqContextTimeoutOrMaxSize,
|
context: &mut IrqContextTimeoutOrMaxSize,
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
@@ -1298,7 +1123,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
|||||||
if context.rx_idx == context.max_len {
|
if context.rx_idx == context.max_len {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let read_result = self.0.read();
|
let read_result = self.rx.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;
|
||||||
@@ -1324,7 +1149,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
|||||||
|
|
||||||
fn read_handler(
|
fn read_handler(
|
||||||
&self,
|
&self,
|
||||||
errors: &mut Option<UartErrors>,
|
errors: &mut Option<IrqUartError>,
|
||||||
read_res: &nb::Result<u8, RxError>,
|
read_res: &nb::Result<u8, RxError>,
|
||||||
) -> Option<u8> {
|
) -> Option<u8> {
|
||||||
match read_res {
|
match read_res {
|
||||||
@@ -1332,7 +1157,7 @@ impl<Uart: Instance> RxWithInterrupt<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(UartErrors::default());
|
let err = errors.get_or_insert(IrqUartError::default());
|
||||||
|
|
||||||
// Now we can safely modify fields inside `err`
|
// Now we can safely modify fields inside `err`
|
||||||
match e {
|
match e {
|
||||||
@@ -1345,14 +1170,14 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_errors(&self, errors: &mut Option<UartErrors>) {
|
fn check_for_errors(&self, errors: &mut Option<IrqUartError>) {
|
||||||
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(UartErrors::default());
|
let err = errors.get_or_insert(IrqUartError::default());
|
||||||
|
|
||||||
if rx_status.rxovr().bit_is_set() {
|
if rx_status.rxovr().bit_is_set() {
|
||||||
err.overflow = true;
|
err.overflow = true;
|
||||||
@@ -1372,7 +1197,7 @@ impl<Uart: Instance> RxWithInterrupt<Uart> {
|
|||||||
context: &mut IrqContextTimeoutOrMaxSize,
|
context: &mut IrqContextTimeoutOrMaxSize,
|
||||||
) {
|
) {
|
||||||
self.disable_rx_irq_sources();
|
self.disable_rx_irq_sources();
|
||||||
self.0.disable();
|
self.rx.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;
|
||||||
@@ -1385,12 +1210,246 @@ impl<Uart: Instance> RxWithInterrupt<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.0.release()
|
self.rx.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod tx_asynch;
|
/*
|
||||||
pub use tx_asynch::*;
|
|
||||||
|
|
||||||
pub mod rx_asynch;
|
impl<UART: Instance, PINS> UartWithIrq<UART, PINS> {
|
||||||
pub use rx_asynch::*;
|
/// See [`UartWithIrqBase::read_fixed_len_using_irq`] doc
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
@@ -1,419 +0,0 @@
|
|||||||
//! # 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)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,264 +0,0 @@
|
|||||||
//! # 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
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
## [v0.4.0] 2025-02-12
|
## [v0.4.0]
|
||||||
|
|
||||||
- Re-generated PAC with `svd2rust` v0.35.0
|
- Re-generated PAC with `svd2rust` v0.35.0
|
||||||
|
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
#![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]
|
#![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]
|
||||||
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"]
|
||||||
|
@@ -8,12 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
## [v0.7.0] 2025-02-13
|
|
||||||
|
|
||||||
- Bumped `va108xx-hal` dependency to 0.9
|
|
||||||
- Minor adjustments to `Button` API.
|
|
||||||
- `Button`, `Led` and `Leds` now simply wrap a type using a tuple struct.
|
|
||||||
|
|
||||||
## [v0.6.0] 2024-09-30
|
## [v0.6.0] 2024-09-30
|
||||||
|
|
||||||
- Added M95M01 EEPROM module/API
|
- Added M95M01 EEPROM module/API
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vorago-reb1"
|
name = "vorago-reb1"
|
||||||
version = "0.7.0"
|
version = "0.6.0"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Board Support Crate for the Vorago REB1 development board"
|
description = "Board Support Crate for the Vorago REB1 development board"
|
||||||
@@ -19,7 +19,7 @@ bitfield = ">=0.17, <=0.18"
|
|||||||
max116xx-10bit = "0.3"
|
max116xx-10bit = "0.3"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.9"
|
version = ">=0.8, <0.9"
|
||||||
features = ["rt"]
|
features = ["rt"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@@ -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, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, 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(),
|
||||||
|
@@ -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, InterruptConfig},
|
timer::{default_ms_irq_handler, set_up_ms_tick, IrqCfg},
|
||||||
};
|
};
|
||||||
use vorago_reb1::button::Button;
|
use vorago_reb1::button::Button;
|
||||||
use vorago_reb1::leds::Leds;
|
use vorago_reb1::leds::Leds;
|
||||||
@@ -35,29 +35,28 @@ 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, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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());
|
let mut button = Button::new(pinsa.pa11.into_floating_input()).edge_irq(
|
||||||
button.configure_edge_interrupt(
|
|
||||||
edge_irq,
|
edge_irq,
|
||||||
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
IrqCfg::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.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
|
button = button.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(
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
Some(&mut dp.irqsel),
|
Some(&mut dp.irqsel),
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
|
@@ -61,7 +61,7 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LibType::Hal => {
|
LibType::Hal => {
|
||||||
let pins = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let pins = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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,13 +87,14 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LibType::Bsp => {
|
LibType::Bsp => {
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), 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() {
|
||||||
@@ -111,3 +112,4 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@@ -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, InterruptConfig},
|
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, IrqCfg},
|
||||||
};
|
};
|
||||||
use va108xx_hal::{port_function_select, FunSel, PortSel};
|
use va108xx_hal::{port_mux, 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(
|
||||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
IrqCfg::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, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, 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_function_select(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok();
|
port_mux(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok();
|
||||||
port_function_select(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok();
|
port_mux(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok();
|
||||||
port_function_select(&mut dp.ioconfig, PortSel::PortB, 17, FunSel::Sel1).ok();
|
port_mux(&mut dp.ioconfig, PortSel::PortB, 17, FunSel::Sel1).ok();
|
||||||
port_function_select(&mut dp.ioconfig, PortSel::PortB, 16, FunSel::Sel1).ok();
|
port_mux(&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();
|
||||||
|
@@ -7,56 +7,60 @@
|
|||||||
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, InterruptConfig,
|
pac, IrqCfg,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct Button {
|
||||||
pub struct Button(pub Pin<PA11, InputFloating>);
|
button: Pin<PA11, InputFloating>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Button {
|
impl Button {
|
||||||
pub fn new(pin: Pin<PA11, InputFloating>) -> Button {
|
pub fn new(pin: Pin<PA11, InputFloating>) -> Button {
|
||||||
Button(pin)
|
Button { button: pin }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pressed(&mut self) -> bool {
|
pub fn pressed(&mut self) -> bool {
|
||||||
self.0.is_low().ok().unwrap()
|
self.button.is_low().ok().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn released(&mut self) -> bool {
|
pub fn released(&mut self) -> bool {
|
||||||
self.0.is_high().ok().unwrap()
|
self.button.is_high().ok().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures an IRQ on edge.
|
/// Configures an IRQ on edge.
|
||||||
pub fn configure_edge_interrupt(
|
pub fn edge_irq(
|
||||||
&mut self,
|
mut self,
|
||||||
edge_type: InterruptEdge,
|
edge_type: InterruptEdge,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
syscfg: Option<&mut pac::Sysconfig>,
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
irqsel: Option<&mut pac::Irqsel>,
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) {
|
) -> Self {
|
||||||
self.0
|
self.button = self
|
||||||
.configure_edge_interrupt(edge_type, irq_cfg, syscfg, irqsel);
|
.button
|
||||||
|
.interrupt_edge(edge_type, irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures an IRQ on level.
|
/// Configures an IRQ on level.
|
||||||
pub fn configure_level_interrupt(
|
pub fn level_irq(
|
||||||
&mut self,
|
mut self,
|
||||||
level: InterruptLevel,
|
level: InterruptLevel,
|
||||||
irq_cfg: InterruptConfig,
|
irq_cfg: IrqCfg,
|
||||||
syscfg: Option<&mut pac::Sysconfig>,
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
irqsel: Option<&mut pac::Irqsel>,
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
) {
|
) -> Self {
|
||||||
self.0
|
self.button = self.button.interrupt_level(level, irq_cfg, syscfg, irqsel);
|
||||||
.configure_level_interrupt(level, irq_cfg, syscfg, irqsel);
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
pub fn filter_type(mut self, filter: FilterType, clksel: FilterClkSel) -> Self {
|
||||||
self.0.configure_filter_type(filter, clksel);
|
self.button = self.button.filter_type(filter, clksel);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,12 +15,15 @@ pub type LD2 = Pin<PA10, PushPullOutput>;
|
|||||||
pub type LD3 = Pin<PA7, PushPullOutput>;
|
pub type LD3 = Pin<PA7, PushPullOutput>;
|
||||||
pub type LD4 = Pin<PA6, PushPullOutput>;
|
pub type LD4 = Pin<PA6, PushPullOutput>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct Leds {
|
||||||
pub struct Leds(pub [Led; 3]);
|
leds: [Led; 3],
|
||||||
|
}
|
||||||
|
|
||||||
impl Leds {
|
impl Leds {
|
||||||
pub fn new(led_pin1: LD2, led_pin2: LD3, led_pin3: LD4) -> Leds {
|
pub fn new(led_pin1: LD2, led_pin2: LD3, led_pin3: LD4) -> Leds {
|
||||||
Leds([led_pin1.into(), led_pin2.into(), led_pin3.into()])
|
Leds {
|
||||||
|
leds: [led_pin1.into(), led_pin2.into(), led_pin3.into()],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,13 +31,13 @@ impl core::ops::Deref for Leds {
|
|||||||
type Target = [Led];
|
type Target = [Led];
|
||||||
|
|
||||||
fn deref(&self) -> &[Led] {
|
fn deref(&self) -> &[Led] {
|
||||||
&self.0
|
&self.leds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::ops::DerefMut for Leds {
|
impl core::ops::DerefMut for Leds {
|
||||||
fn deref_mut(&mut self) -> &mut [Led] {
|
fn deref_mut(&mut self) -> &mut [Led] {
|
||||||
&mut self.0
|
&mut self.leds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,25 +45,28 @@ impl core::ops::Index<usize> for Leds {
|
|||||||
type Output = Led;
|
type Output = Led;
|
||||||
|
|
||||||
fn index(&self, i: usize) -> &Led {
|
fn index(&self, i: usize) -> &Led {
|
||||||
&self.0[i]
|
&self.leds[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::ops::IndexMut<usize> for Leds {
|
impl core::ops::IndexMut<usize> for Leds {
|
||||||
fn index_mut(&mut self, i: usize) -> &mut Led {
|
fn index_mut(&mut self, i: usize) -> &mut Led {
|
||||||
&mut self.0[i]
|
&mut self.leds[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct Led {
|
||||||
pub struct Led(pub DynPin);
|
pin: DynPin,
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! ctor {
|
macro_rules! ctor {
|
||||||
($($ldx:ident),+) => {
|
($($ldx:ident),+) => {
|
||||||
$(
|
$(
|
||||||
impl From<$ldx> for Led {
|
impl From<$ldx> for Led {
|
||||||
fn from(led: $ldx) -> Self {
|
fn from(led: $ldx) -> Self {
|
||||||
Led(led.into())
|
Led {
|
||||||
|
pin: led.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+
|
)+
|
||||||
@@ -73,18 +79,18 @@ impl Led {
|
|||||||
/// Turns the LED off. Setting the pin high actually turns the LED off
|
/// Turns the LED off. Setting the pin high actually turns the LED off
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn off(&mut self) {
|
pub fn off(&mut self) {
|
||||||
self.0.set_high().ok();
|
self.pin.set_high().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns the LED on. Setting the pin low actually turns the LED on
|
/// Turns the LED on. Setting the pin low actually turns the LED on
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn on(&mut self) {
|
pub fn on(&mut self) {
|
||||||
self.0.set_low().ok();
|
self.pin.set_low().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggles the LED
|
/// Toggles the LED
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn toggle(&mut self) {
|
pub fn toggle(&mut self) {
|
||||||
self.0.toggle_with_toggle_reg().ok();
|
self.pin.toggle_with_toggle_reg().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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": "uart-echo-rtic-example",
|
"preLaunchTask": "rust: cargo build uart irq",
|
||||||
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic",
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-rtic",
|
||||||
"interface": "jtag",
|
"interface": "jtag",
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
@@ -499,77 +499,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
@@ -266,36 +266,6 @@
|
|||||||
"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",
|
||||||
|
Reference in New Issue
Block a user