28 Commits

Author SHA1 Message Date
770d6cb905 date fix CHANGELOG 2025-02-18 15:16:14 +01:00
9878f3b493 Merge pull request 'update VS Code files' (#59) from update-vscode-files into main
Reviewed-on: #59
2025-02-17 11:38:03 +01:00
1b07d0f258 update VS Code files 2025-02-17 11:36:40 +01:00
d785f8ab88 Merge pull request 'fix for UART example' (#58) from example-fix into main
Reviewed-on: #58
2025-02-17 11:35:40 +01:00
527243ab96 fix for UART example 2025-02-17 11:35:11 +01:00
617ba3cca0 Merge pull request 'PAC changelog' (#57) from pac-changelog into main
Reviewed-on: #57
2025-02-17 11:33:22 +01:00
167cb97f7d PAC changelog 2025-02-17 11:33:07 +01:00
857b233881 Merge pull request 'changelog HAL' (#56) from changelog-hal into main
Reviewed-on: #56
2025-02-17 11:32:47 +01:00
9dcb423976 changelog HAL 2025-02-17 11:30:52 +01:00
186ae6d059 Merge pull request 'UART update' (#54) from uart-update into main
Reviewed-on: #54
2025-02-17 11:28:46 +01:00
54c949421e added async support for UART 2025-02-17 11:28:33 +01:00
910ed58fdf Merge pull request 'CI update' (#55) from ci-update into main
Reviewed-on: #55
2025-02-17 11:26:12 +01:00
ddf50376ec CI update 2025-02-17 11:17:05 +01:00
8fc9d12046 Merge pull request 'Async GPIO implementation' (#53) from add-async-gpio into main
Reviewed-on: #53
2025-02-17 10:57:03 +01:00
0bcf611e46 Async GPIO implementation 2025-02-15 18:51:03 +01:00
7f6b1a7ba5 Merge pull request 'defmt and debug feature for PAC' (#52) from update-pac-demt-debug-feature into main
Reviewed-on: #52
2025-02-14 16:42:50 +01:00
c39694e3cc update CI 2025-02-14 16:39:28 +01:00
3b4dd9d5c3 defmt and debug feature for PAC 2025-02-14 16:34:26 +01:00
3fe3b833a6 Merge pull request 'HAL update' (#51) from hal-update into main
Reviewed-on: #51
2025-02-14 16:31:15 +01:00
14ad647773 HAL update + CHANGELOG 2025-02-14 15:31:19 +01:00
f9d94a9d7e Merge pull request 'Updated GPIO impl' (#50) from update-gpio-impl-2 into main
Reviewed-on: #50
2025-02-14 14:42:43 +01:00
0d2fcd346b Updated GPIO impl 2025-02-14 14:40:16 +01:00
9306bb07a9 Merge pull request 'simplified PWM impl' (#49) from simplify-pwm-impl into main
Reviewed-on: #49
2025-02-14 14:37:09 +01:00
4fa1b17f20 simplified PWM impl 2025-02-13 19:05:40 +01:00
68fbeec9fe Merge pull request 'minor doc improvements' (#47) from minor-docs-improvements into main
Reviewed-on: #47
2025-02-13 18:51:18 +01:00
a8fbe2abb5 minor doc improvements 2025-02-13 18:50:53 +01:00
f37c3f2806 Merge pull request 'minor doc improvements' (#46) from minor-docs-improvements into main
Reviewed-on: #46
2025-02-13 18:26:04 +01:00
0a7642213f minor doc improvements 2025-02-13 18:24:57 +01:00
113 changed files with 3663 additions and 1188 deletions

View File

@ -10,8 +10,10 @@ jobs:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
targets: "thumbv7em-none-eabihf" targets: "thumbv7em-none-eabihf"
- run: cargo check --target thumbv7em-none-eabihf --release - run: cargo check --target thumbv7em-none-eabihf
- run: cargo check --target thumbv7em-none-eabihf --examples --release - run: cargo check --target thumbv7em-none-eabihf --examples
- run: cargo check -p va416xx --target thumbv7em-none-eabihf --all-features
- run: cargo check -p va416xx-hal --target thumbv7em-none-eabihf --examples --features "defmt va41630"
test: test:
name: Run Tests name: Run Tests

View File

@ -9,32 +9,27 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
embedded-io = "0.6" embedded-io = "0.6"
embedded-hal-async = "1"
embedded-io-async = "0.6"
rtt-target = { version = "0.5" } rtt-target = { version = "0.5" }
heapless = "0.8"
panic-rtt-target = { version = "0.1" } panic-rtt-target = { version = "0.1" }
static_cell = "2"
critical-section = "1" critical-section = "1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
ringbuf = { version = "0.4", default-features = false }
embassy-sync = "0.6"
embassy-time = "0.4"
embassy-executor = { version = "0.7", features = [
"arch-cortex-m",
"executor-thread",
"executor-interrupt"
]}
embassy-sync = { version = "0.6" }
embassy-time = { version = "0.4" }
va416xx-embassy = { path = "../../va416xx-embassy", default-features = false } va416xx-embassy = { path = "../../va416xx-embassy", default-features = false }
[dependencies.ringbuf]
version = "0.4"
default-features = false
[dependencies.once_cell]
version = "1"
default-features = false
features = ["critical-section"]
[dependencies.embassy-executor]
version = "0.7"
features = [
"arch-cortex-m",
"executor-thread",
"executor-interrupt",
]
[dependencies.va416xx-hal] [dependencies.va416xx-hal]
path = "../../va416xx-hal" path = "../../va416xx-hal"
features = ["va41630"] features = ["va41630"]

View File

@ -0,0 +1,371 @@
//! This example demonstrates the usage of async GPIO operations on VA416xx.
//!
//! You need to tie the PA0 to the PA1 pin for this example to work. You can optionally also tie
//! more pin combinations together and test other ports by setting the appropriate
//! [CHECK_XXX_TO_XXX] constants to true.
#![no_std]
#![no_main]
use embassy_example::EXTCLK_FREQ;
use embassy_executor::Spawner;
use embassy_sync::channel::{Receiver, Sender};
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
use embassy_time::{Duration, Instant, Timer};
use embedded_hal_async::digital::Wait;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::clock::ClkgenExt;
use va416xx_hal::gpio::{
on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, PinsC, PinsD,
PinsE, PinsF, PinsG, Port,
};
use va416xx_hal::time::Hertz;
use va416xx_hal::{
gpio::{DynPin, PinsA},
pac::{self, interrupt},
};
const CHECK_PA0_TO_PA1: bool = true;
const CHECK_PB0_TO_PB1: bool = true;
const CHECK_PC14_TO_PC15: bool = true;
const CHECK_PD2_TO_PD3: bool = true;
const CHECK_PE0_TO_PE1: bool = true;
const CHECK_PF0_TO_PF1: bool = true;
#[derive(Clone, Copy)]
pub struct GpioCmd {
cmd_type: GpioCmdType,
after_delay: u32,
}
impl GpioCmd {
pub fn new(cmd_type: GpioCmdType, after_delay: u32) -> Self {
Self {
cmd_type,
after_delay,
}
}
}
#[derive(Clone, Copy)]
pub enum GpioCmdType {
SetHigh,
SetLow,
RisingEdge,
FallingEdge,
CloseTask,
}
// Declare a bounded channel of 3 u32s.
static CHANNEL_PA0_TO_PA1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
static CHANNEL_PB0_TO_PB1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
static CHANNEL_PC14_TO_PC15: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
static CHANNEL_PD2_TO_PD3: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
static CHANNEL_PE0_TO_PE1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
static CHANNEL_PF0_TO_PF1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
#[embassy_executor::main]
async fn main(spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA416xx Async GPIO Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze(&mut dp.sysconfig)
.unwrap();
// Safety: Only called once here.
unsafe {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
)
};
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let portb = PinsB::new(&mut dp.sysconfig, dp.portb);
let portc = PinsC::new(&mut dp.sysconfig, dp.portc);
let portd = PinsD::new(&mut dp.sysconfig, dp.portd);
let porte = PinsE::new(&mut dp.sysconfig, dp.porte);
let portf = PinsF::new(&mut dp.sysconfig, dp.portf);
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = portg.pg5.into_readable_push_pull_output();
if CHECK_PA0_TO_PA1 {
let out_pin = porta.pa0.into_readable_push_pull_output();
let in_pin = porta.pa1.into_floating_input();
let out_pin = out_pin.downgrade();
let in_pin = InputPinAsync::new(in_pin).unwrap();
spawner
.spawn(output_task(
"PA0 to PA1",
out_pin,
CHANNEL_PA0_TO_PA1.receiver(),
))
.unwrap();
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_TO_PA1.sender(), in_pin).await;
rprintln!("Example PA0 to PA1 done");
}
if CHECK_PB0_TO_PB1 {
let out_pin = portb.pb0.into_readable_push_pull_output();
let in_pin = portb.pb1.into_floating_input();
let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner
.spawn(output_task(
"PB0 to PB1",
out_pin,
CHANNEL_PB0_TO_PB1.receiver(),
))
.unwrap();
check_pin_to_pin_async_ops("PB0 to PB1", CHANNEL_PB0_TO_PB1.sender(), in_pin).await;
rprintln!("Example PB0 to PB1 done");
}
if CHECK_PC14_TO_PC15 {
let out_pin = portc.pc14.into_readable_push_pull_output();
let in_pin = portc.pc15.into_floating_input();
let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner
.spawn(output_task(
"PC14 to PC15",
out_pin,
CHANNEL_PC14_TO_PC15.receiver(),
))
.unwrap();
check_pin_to_pin_async_ops("PC14 to PC15", CHANNEL_PC14_TO_PC15.sender(), in_pin).await;
rprintln!("Example PC14 to PC15 done");
}
if CHECK_PD2_TO_PD3 {
let out_pin = portd.pd2.into_readable_push_pull_output();
let in_pin = portd.pd3.into_floating_input();
let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner
.spawn(output_task(
"PD2 to PD3",
out_pin,
CHANNEL_PD2_TO_PD3.receiver(),
))
.unwrap();
check_pin_to_pin_async_ops("PD2 to PD3", CHANNEL_PD2_TO_PD3.sender(), in_pin).await;
rprintln!("Example PD2 to PD3 done");
}
if CHECK_PE0_TO_PE1 {
let out_pin = porte.pe0.into_readable_push_pull_output();
let in_pin = porte.pe1.into_floating_input();
let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner
.spawn(output_task(
"PE0 to PE1",
out_pin,
CHANNEL_PE0_TO_PE1.receiver(),
))
.unwrap();
check_pin_to_pin_async_ops("PE0 to PE1", CHANNEL_PE0_TO_PE1.sender(), in_pin).await;
rprintln!("Example PE0 to PE1 done");
}
if CHECK_PF0_TO_PF1 {
let out_pin = portf.pf0.into_readable_push_pull_output();
let in_pin = portf.pf1.into_floating_input();
let out_pin = out_pin.downgrade();
let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap();
spawner
.spawn(output_task(
"PF0 to PF1",
out_pin,
CHANNEL_PF0_TO_PF1.receiver(),
))
.unwrap();
check_pin_to_pin_async_ops("PF0 to PF1", CHANNEL_PF0_TO_PF1.sender(), in_pin).await;
rprintln!("Example PF0 to PF1 done");
}
rprintln!("Example done, toggling LED0");
loop {
led.toggle();
Timer::after(Duration::from_millis(500)).await;
}
}
async fn check_pin_to_pin_async_ops(
ctx: &'static str,
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
mut async_input: impl Wait,
) {
rprintln!(
"{}: sending SetHigh command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
async_input.wait_for_high().await.unwrap();
rprintln!(
"{}: Input pin is high now ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending SetLow command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
async_input.wait_for_low().await.unwrap();
rprintln!(
"{}: Input pin is low now ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending RisingEdge command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
async_input.wait_for_rising_edge().await.unwrap();
rprintln!(
"{}: input pin had rising edge ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending Falling command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
.await;
async_input.wait_for_falling_edge().await.unwrap();
rprintln!(
"{}: input pin had a falling edge ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending Falling command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
.await;
async_input.wait_for_any_edge().await.unwrap();
rprintln!(
"{}: input pin had a falling (any) edge ({} ms)",
ctx,
Instant::now().as_millis()
);
rprintln!(
"{}: sending Falling command ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
async_input.wait_for_any_edge().await.unwrap();
rprintln!(
"{}: input pin had a rising (any) edge ({} ms)",
ctx,
Instant::now().as_millis()
);
sender.send(GpioCmd::new(GpioCmdType::CloseTask, 0)).await;
}
#[embassy_executor::task(pool_size = 8)]
async fn output_task(
ctx: &'static str,
mut out: DynPin,
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
) {
loop {
let next_cmd = receiver.receive().await;
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
match next_cmd.cmd_type {
GpioCmdType::SetHigh => {
rprintln!("{}: Set output high", ctx);
out.set_high().unwrap();
}
GpioCmdType::SetLow => {
rprintln!("{}: Set output low", ctx);
out.set_low().unwrap();
}
GpioCmdType::RisingEdge => {
rprintln!("{}: Rising edge", ctx);
if !out.is_low().unwrap() {
out.set_low().unwrap();
}
out.set_high().unwrap();
}
GpioCmdType::FallingEdge => {
rprintln!("{}: Falling edge", ctx);
if !out.is_high().unwrap() {
out.set_high().unwrap();
}
out.set_low().unwrap();
}
GpioCmdType::CloseTask => {
rprintln!("{}: Closing task", ctx);
break;
}
}
}
}
#[interrupt]
#[allow(non_snake_case)]
fn PORTA1() {
on_interrupt_for_async_gpio_for_port(Port::A).unwrap();
}
#[interrupt]
#[allow(non_snake_case)]
fn PORTB1() {
on_interrupt_for_async_gpio_for_port(Port::B).unwrap();
}
#[interrupt]
#[allow(non_snake_case)]
fn PORTC15() {
on_interrupt_for_async_gpio_for_port(Port::C).unwrap();
}
#[interrupt]
#[allow(non_snake_case)]
fn PORTD3() {
on_interrupt_for_async_gpio_for_port(Port::D).unwrap();
}
#[interrupt]
#[allow(non_snake_case)]
fn PORTE1() {
on_interrupt_for_async_gpio_for_port(Port::E).unwrap();
}
#[interrupt]
#[allow(non_snake_case)]
fn PORTF1() {
on_interrupt_for_async_gpio_for_port(Port::F).unwrap();
}

View File

@ -0,0 +1,111 @@
//! Asynchronous UART reception example application.
//!
//! This application receives data on two UARTs permanently using a ring buffer.
//! The ring buffer are read them asynchronously.
//! It uses PORTG0 as TX pin and PORTG1 as RX pin, which is the UART0 on the PEB1 board.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PORTG0 and TX to PORTG1.
//! 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_example::EXTCLK_FREQ;
use embassy_executor::Spawner;
use embassy_time::Instant;
use embedded_io::Write;
use embedded_io_async::Read;
use heapless::spsc::{Producer, Queue};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{
gpio::PinsG,
pac::{self, interrupt},
prelude::*,
time::Hertz,
uart::{
self,
rx_asynch::{on_interrupt_rx, RxAsync},
Bank,
},
};
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));
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async UART RX Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze(&mut dp.sysconfig)
.unwrap();
// Safety: Only called once here.
unsafe {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
);
}
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = portg.pg5.into_readable_push_pull_output();
let tx = portg.pg0.into_funsel_1();
let rx = portg.pg1.into_funsel_1();
let uarta = uart::Uart::new(&mut dp.sysconfig, dp.uart0, (tx, rx), 115200.Hz(), &clocks);
let (mut tx_uart_a, rx_uart_a) = uarta.split();
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
// Pass the producer to the interrupt handler.
critical_section::with(|cs| {
*PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod_uart_a);
});
// TODO: Add example for RxAsyncOverwriting using another UART.
let mut async_uart_rx = RxAsync::new(rx_uart_a, cons_uart_a);
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART A: {}", Instant::now().as_secs());
led.toggle();
let read_bytes = async_uart_rx.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();
}
}
#[interrupt]
#[allow(non_snake_case)]
fn UART0_RX() {
let mut prod =
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_rx(Bank::Uart0, &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);
}
}

View File

@ -0,0 +1,96 @@
//! Asynchronous UART transmission example application.
//!
//! This application receives sends 4 strings with different sizes permanently.
//! It uses PORTG0 as TX pin and PORTG1 as RX pin, which is the UART0 on the PEB1 board.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PORTG0 and TX to PORTG1.
//! 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 embassy_example::EXTCLK_FREQ;
use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker};
use embedded_io_async::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{
gpio::PinsG,
pac::{self, interrupt},
prelude::*,
time::Hertz,
uart::{
self,
tx_asynch::{on_interrupt_tx, TxAsync},
Bank,
},
};
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();
// Initialize the systick interrupt & obtain the token to prove that we did
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
.freeze(&mut dp.sysconfig)
.unwrap();
// Safety: Only called once here.
unsafe {
va416xx_embassy::init(
&mut dp.sysconfig,
&dp.irq_router,
dp.tim15,
dp.tim14,
&clocks,
);
}
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = portg.pg5.into_readable_push_pull_output();
let tx = portg.pg0.into_funsel_1();
let rx = portg.pg1.into_funsel_1();
let uarta = uart::Uart::new(&mut dp.sysconfig, dp.uart0, (tx, rx), 115200.Hz(), &clocks);
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());
led.toggle();
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 UART0_TX() {
on_interrupt_tx(Bank::Uart0);
}

View File

@ -16,7 +16,6 @@ use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex; use embassy_sync::blocking_mutex::Mutex;
use embassy_time::{Duration, Ticker}; use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write; use embedded_io::Write;
use panic_rtt_target as _; use panic_rtt_target as _;
use ringbuf::{ use ringbuf::{
@ -32,7 +31,8 @@ use va416xx_hal::{
uart, uart,
}; };
pub type SharedUart = Mutex<CriticalSectionRawMutex, RefCell<Option<uart::RxWithIrq<pac::Uart0>>>>; pub type SharedUart =
Mutex<CriticalSectionRawMutex, RefCell<Option<uart::RxWithInterrupt<pac::Uart0>>>>;
static RX: SharedUart = Mutex::new(RefCell::new(None)); static RX: SharedUart = Mutex::new(RefCell::new(None));
const BAUDRATE: u32 = 115200; const BAUDRATE: u32 = 115200;
@ -79,10 +79,10 @@ async fn main(spawner: Spawner) {
let rx = portg.pg1.into_funsel_1(); let rx = portg.pg1.into_funsel_1();
let uart0 = uart::Uart::new( let uart0 = uart::Uart::new(
&mut dp.sysconfig,
dp.uart0, dp.uart0,
(tx, rx), (tx, rx),
Hertz::from_raw(BAUDRATE), Hertz::from_raw(BAUDRATE),
&mut dp.sysconfig,
&clocks, &clocks,
); );
let (mut tx, rx) = uart0.split(); let (mut tx, rx) = uart0.split();
@ -118,7 +118,7 @@ async fn main(spawner: Spawner) {
async fn blinky(mut led: Pin<PG5, OutputReadablePushPull>) { async fn blinky(mut led: Pin<PG5, OutputReadablePushPull>) {
let mut ticker = Ticker::every(Duration::from_millis(500)); let mut ticker = Ticker::every(Duration::from_millis(500));
loop { loop {
led.toggle().ok(); led.toggle();
ticker.next().await; ticker.next().await;
} }
} }

View File

@ -3,7 +3,6 @@
use embassy_example::EXTCLK_FREQ; use embassy_example::EXTCLK_FREQ;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker}; use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{gpio::PinsG, pac, prelude::*, time::Hertz}; use va416xx_hal::{gpio::PinsG, pac, prelude::*, time::Hertz};
@ -59,6 +58,6 @@ async fn main(_spawner: Spawner) {
loop { loop {
ticker.next().await; ticker.next().await;
rprintln!("Current time: {}", Instant::now().as_secs()); rprintln!("Current time: {}", Instant::now().as_secs());
led.toggle().ok(); led.toggle();
} }
} }

View File

@ -10,7 +10,6 @@ const EXTCLK_FREQ: Hertz = Hertz::from_raw(40_000_000);
mod app { mod app {
use super::*; use super::*;
use cortex_m::asm; use cortex_m::asm;
use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtic_monotonics::systick::prelude::*; use rtic_monotonics::systick::prelude::*;
use rtic_monotonics::Monotonic; use rtic_monotonics::Monotonic;
@ -64,7 +63,7 @@ mod app {
)] )]
async fn blinky(cx: blinky::Context) { async fn blinky(cx: blinky::Context) {
loop { loop {
cx.local.led.toggle().ok(); cx.local.led.toggle();
Mono::delay(200.millis()).await; Mono::delay(200.millis()).await;
} }
} }

View File

@ -3,7 +3,6 @@
#![no_std] #![no_std]
use cortex_m_rt::entry; use cortex_m_rt::entry;
use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{gpio::PinsG, pac}; use va416xx_hal::{gpio::PinsG, pac};
@ -18,6 +17,6 @@ fn main() -> ! {
let mut led = portg.pg5.into_readable_push_pull_output(); let mut led = portg.pg5.into_readable_push_pull_output();
loop { loop {
cortex_m::asm::delay(2_000_000); cortex_m::asm::delay(2_000_000);
led.toggle().ok(); led.toggle();
} }
} }

View File

@ -49,9 +49,12 @@ fn main() -> ! {
enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router); enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
// Safety: The DMA control block has an alignment rule of 128 and we constructed it directly // Safety: The DMA control block has an alignment rule of 128 and we constructed it directly
// statically. // statically.
let dma = Dma::new(&mut dp.sysconfig, dp.dma, DmaCfg::default(), unsafe { let dma = Dma::new(
core::ptr::addr_of_mut!(DMA_CTRL_BLOCK) &mut dp.sysconfig,
}) dp.dma,
DmaCfg::default(),
core::ptr::addr_of_mut!(DMA_CTRL_BLOCK),
)
.expect("error creating DMA"); .expect("error creating DMA");
let (mut dma0, _, _, _) = dma.split(); let (mut dma0, _, _, _) = dma.split();
let mut delay_ms = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks); let mut delay_ms = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);

View File

@ -33,10 +33,10 @@ fn main() -> ! {
let rx = gpiob.pg1.into_funsel_1(); let rx = gpiob.pg1.into_funsel_1();
let uart0 = uart::Uart::new( let uart0 = uart::Uart::new(
&mut dp.sysconfig,
dp.uart0, dp.uart0,
(tx, rx), (tx, rx),
Hertz::from_raw(115200), Hertz::from_raw(115200),
&mut dp.sysconfig,
&clocks, &clocks,
); );
let (mut tx, mut rx) = uart0.split(); let (mut tx, mut rx) = uart0.split();

View File

@ -127,7 +127,7 @@ mod app {
#[local] #[local]
struct Local { struct Local {
uart_rx: uart::RxWithIrq<pac::Uart0>, uart_rx: uart::RxWithInterrupt<pac::Uart0>,
uart_tx: uart::Tx<pac::Uart0>, uart_tx: uart::Tx<pac::Uart0>,
rx_context: IrqContextTimeoutOrMaxSize, rx_context: IrqContextTimeoutOrMaxSize,
rom_spi: Option<pac::Spi3>, rom_spi: Option<pac::Spi3>,
@ -171,10 +171,10 @@ mod app {
let rx = gpiog.pg1.into_funsel_1(); let rx = gpiog.pg1.into_funsel_1();
let uart0 = Uart::new( let uart0 = Uart::new(
&mut cx.device.sysconfig,
cx.device.uart0, cx.device.uart0,
(tx, rx), (tx, rx),
Hertz::from_raw(UART_BAUDRATE), Hertz::from_raw(UART_BAUDRATE),
&mut cx.device.sysconfig,
&clocks, &clocks,
); );
let (tx, rx) = uart0.split(); let (tx, rx) = uart0.split();

View File

@ -50,7 +50,7 @@ use embassy_time_queue_utils::Queue;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use va416xx_hal::{ use va416xx_hal::{
clock::Clocks, clock::Clocks,
enable_interrupt, enable_nvic_interrupt,
irq_router::enable_and_init_irq_router, irq_router::enable_and_init_irq_router,
pac::{self, interrupt}, pac::{self, interrupt},
pwm::ValidTim, pwm::ValidTim,
@ -207,7 +207,7 @@ impl TimerDriver {
.write(|w| unsafe { w.bits(u32::MAX) }); .write(|w| unsafe { w.bits(u32::MAX) });
// Switch on. Timekeeping should always be done. // Switch on. Timekeeping should always be done.
unsafe { unsafe {
enable_interrupt(TimekeeperTim::IRQ); enable_nvic_interrupt(TimekeeperTim::IRQ);
} }
timekeeper_tim_regs timekeeper_tim_regs
.ctrl() .ctrl()
@ -224,7 +224,7 @@ impl TimerDriver {
}); });
// Enable general interrupts. The IRQ enable of the peripheral remains cleared. // Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe { unsafe {
enable_interrupt(AlarmTim::IRQ); enable_nvic_interrupt(AlarmTim::IRQ);
} }
} }

View File

@ -8,6 +8,43 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.4.0]
## Changed
- 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.
- 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`.
- 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
methods.
- Asynchronous GPIO support.
- Asynchronous UART TX support.
- Asynchronous UART RX support.
- Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks.
- `Uart::with_with_interrupt` and `Uart::new_without_interrupt`
# [v0.3.0] 2024-30-09 # [v0.3.0] 2024-30-09
## Changed ## Changed

View File

@ -16,26 +16,34 @@ critical-section = "1"
nb = "1" nb = "1"
paste = "1" paste = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-hal-async = "1"
embedded-hal = "1" embedded-hal = "1"
embedded-io = "0.6" embedded-io = "0.6"
embedded-io-async = "0.6"
num_enum = { version = "0.7", default-features = false } num_enum = { version = "0.7", default-features = false }
typenum = "1" typenum = "1"
bitflags = "2" bitflags = "2"
bitfield = "0.17" bitfield = "0.17"
defmt = { version = "0.3", optional = true }
fugit = "0.3" fugit = "0.3"
delegate = "0.12" delegate = "0.12"
heapless = "0.8"
void = { version = "1", default-features = false } void = { version = "1", default-features = false }
thiserror = { version = "2", default-features = false }
portable-atomic = "1"
embassy-sync = "0.6"
defmt = { version = "0.3", optional = true }
[dependencies.va416xx] [dependencies.va416xx]
default-features = false default-features = false
version = "0.3" path = "../va416xx"
version = "0.4"
features = ["critical-section"] features = ["critical-section"]
[features] [features]
default = ["rt", "revb"] default = ["rt", "revb"]
rt = ["va416xx/rt"] rt = ["va416xx/rt"]
defmt = ["dep:defmt", "fugit/defmt"] defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
va41630 = ["device-selected"] va41630 = ["device-selected"]
va41620 = ["device-selected"] va41620 = ["device-selected"]

View File

@ -30,12 +30,6 @@ rustup target add thumbv7em-none-eabihf
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
@ -71,3 +65,11 @@ is contained within the
7. Flashing the board might work differently for different boards and there is usually 7. Flashing the board might work differently for different boards and there is usually
more than one way. You can find example instructions in primary README. more than one way. You can find example instructions in primary README.
## Embedded Rust
If you have not done this yet, it is recommended to read some of the excellent resources
available to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)

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

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

View File

@ -74,34 +74,28 @@ bitflags::bitflags! {
} }
} }
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("ADC empty error")]
pub struct AdcEmptyError; pub struct AdcEmptyError;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("invalid channel range error")]
pub struct InvalidChannelRangeError; pub struct InvalidChannelRangeError;
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("buffer too small")]
pub struct BufferTooSmallError; pub struct BufferTooSmallError;
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AdcRangeReadError { pub enum AdcRangeReadError {
InvalidChannelRange(InvalidChannelRangeError), #[error("invalid channel range: {0}")]
BufferTooSmall(BufferTooSmallError), InvalidChannelRange(#[from] InvalidChannelRangeError),
} #[error("buffer too small: {0}")]
BufferTooSmall(#[from] BufferTooSmallError),
impl From<InvalidChannelRangeError> for AdcRangeReadError {
fn from(value: InvalidChannelRangeError) -> Self {
AdcRangeReadError::InvalidChannelRange(value)
}
}
impl From<BufferTooSmallError> for AdcRangeReadError {
fn from(value: BufferTooSmallError) -> Self {
AdcRangeReadError::BufferTooSmall(value)
}
} }
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]

View File

@ -19,7 +19,8 @@ use crate::time::Hertz;
pub const HBO_FREQ: Hertz = Hertz::from_raw(20_000_000); pub const HBO_FREQ: Hertz = Hertz::from_raw(20_000_000);
pub const XTAL_OSC_TSTART_MS: u32 = 15; pub const XTAL_OSC_TSTART_MS: u32 = 15;
#[derive(Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect { pub enum PeripheralSelect {
Spi0 = 0, Spi0 = 0,
Spi1 = 1, Spi1 = 1,
@ -57,6 +58,7 @@ pub enum PeripheralSelect {
pub type PeripheralClock = PeripheralSelect; pub type PeripheralClock = 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,

View File

@ -5,7 +5,7 @@
//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs) //! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs)
use crate::{ use crate::{
clock::{PeripheralClock, PeripheralSelect}, clock::{PeripheralClock, PeripheralSelect},
enable_interrupt, pac, enable_nvic_interrupt, pac,
prelude::*, prelude::*,
}; };
@ -77,12 +77,15 @@ pub enum RPower {
Every1024 = 0b1111, Every1024 = 0b1111,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub struct InvalidCtrlBlockAddr; #[error("Invalid DMA control block address")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidCtrlBlockAddrError;
bitfield::bitfield! { bitfield::bitfield! {
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ChannelConfig(u32); pub struct ChannelConfig(u32);
impl Debug; impl Debug;
u32; u32;
@ -111,6 +114,7 @@ bitfield::bitfield! {
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DmaChannelControl { pub struct DmaChannelControl {
pub src_end_ptr: u32, pub src_end_ptr: u32,
pub dest_end_ptr: u32, pub dest_end_ptr: u32,
@ -160,9 +164,9 @@ impl DmaCtrlBlock {
/// The passed address must be 128-byte aligned. The user must also take care of specifying /// The passed address must be 128-byte aligned. The user must also take care of specifying
/// a valid memory address for the DMA control block which is accessible by the system as well. /// a valid memory address for the DMA control block which is accessible by the system as well.
/// For example, the control block can be placed in the SRAM1. /// For example, the control block can be placed in the SRAM1.
pub fn new_at_addr(addr: u32) -> Result<*mut DmaCtrlBlock, InvalidCtrlBlockAddr> { pub fn new_at_addr(addr: u32) -> Result<*mut DmaCtrlBlock, InvalidCtrlBlockAddrError> {
if addr & BASE_PTR_ADDR_MASK > 0 { if addr & BASE_PTR_ADDR_MASK > 0 {
return Err(InvalidCtrlBlockAddr); return Err(InvalidCtrlBlockAddrError);
} }
let ctrl_block_ptr = addr as *mut DmaCtrlBlock; let ctrl_block_ptr = addr as *mut DmaCtrlBlock;
unsafe { core::ptr::write(ctrl_block_ptr, DmaCtrlBlock::default()) } unsafe { core::ptr::write(ctrl_block_ptr, DmaCtrlBlock::default()) }
@ -175,19 +179,21 @@ pub struct Dma {
ctrl_block: *mut DmaCtrlBlock, ctrl_block: *mut DmaCtrlBlock,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DmaTransferInitError { pub enum DmaTransferInitError {
SourceDestLenMissmatch { #[error("source and destination buffer length mismatch: {src_len} != {dest_len}")]
src_len: usize, SourceDestLenMissmatch { src_len: usize, dest_len: usize },
dest_len: usize,
},
/// Overflow when calculating the source or destination end address. /// Overflow when calculating the source or destination end address.
#[error("address overflow")]
AddrOverflow, AddrOverflow,
/// Transfer size larger than 1024 units. /// Transfer size larger than 1024 units.
#[error("transfer size too large: {0}, 1024 is the allowed maximum")]
TransferSizeTooLarge(usize), TransferSizeTooLarge(usize),
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DmaCfg { pub struct DmaCfg {
pub bufferable: bool, pub bufferable: bool,
pub cacheable: bool, pub cacheable: bool,
@ -260,7 +266,7 @@ impl DmaChannel {
/// ///
/// This function is `unsafe` because it can break mask-based critical sections. /// This function is `unsafe` because it can break mask-based critical sections.
pub unsafe fn enable_done_interrupt(&mut self) { pub unsafe fn enable_done_interrupt(&mut self) {
enable_interrupt(self.done_interrupt); enable_nvic_interrupt(self.done_interrupt);
} }
/// Enables the DMA_ACTIVE interrupt for the DMA channel. /// Enables the DMA_ACTIVE interrupt for the DMA channel.
@ -269,7 +275,7 @@ impl DmaChannel {
/// ///
/// This function is `unsafe` because it can break mask-based critical sections. /// This function is `unsafe` because it can break mask-based critical sections.
pub unsafe fn enable_active_interrupt(&mut self) { pub unsafe fn enable_active_interrupt(&mut self) {
enable_interrupt(self.active_interrupt); enable_nvic_interrupt(self.active_interrupt);
} }
/// Prepares a 8-bit DMA transfer from memory to memory. /// Prepares a 8-bit DMA transfer from memory to memory.
@ -493,11 +499,11 @@ impl Dma {
dma: pac::Dma, dma: pac::Dma,
cfg: DmaCfg, cfg: DmaCfg,
ctrl_block: *mut DmaCtrlBlock, ctrl_block: *mut DmaCtrlBlock,
) -> Result<Self, InvalidCtrlBlockAddr> { ) -> Result<Self, InvalidCtrlBlockAddrError> {
// The conversion to u32 is safe here because we are on a 32-bit system. // The conversion to u32 is safe here because we are on a 32-bit system.
let raw_addr = ctrl_block as u32; let raw_addr = ctrl_block as u32;
if raw_addr & BASE_PTR_ADDR_MASK > 0 { if raw_addr & BASE_PTR_ADDR_MASK > 0 {
return Err(InvalidCtrlBlockAddr); return Err(InvalidCtrlBlockAddrError);
} }
syscfg.enable_peripheral_clock(PeripheralClock::Dma); syscfg.enable_peripheral_clock(PeripheralClock::Dma);
syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dma); syscfg.assert_periph_reset_for_two_cycles(PeripheralSelect::Dma);

View File

@ -1,4 +1,4 @@
use crate::{enable_interrupt, pac}; use crate::{enable_nvic_interrupt, pac};
#[inline(always)] #[inline(always)]
pub fn enable_rom_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) { pub fn enable_rom_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
@ -26,7 +26,7 @@ pub fn enable_ram1_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
#[inline(always)] #[inline(always)]
pub fn enable_sbe_irq() { pub fn enable_sbe_irq() {
unsafe { unsafe {
enable_interrupt(pac::Interrupt::EDAC_SBE); enable_nvic_interrupt(pac::Interrupt::EDAC_SBE);
} }
} }
@ -35,7 +35,7 @@ pub fn enable_sbe_irq() {
#[inline(always)] #[inline(always)]
pub fn enable_mbe_irq() { pub fn enable_mbe_irq() {
unsafe { unsafe {
enable_interrupt(pac::Interrupt::EDAC_MBE); enable_nvic_interrupt(pac::Interrupt::EDAC_MBE);
} }
} }

View File

@ -0,0 +1,448 @@
//! # Async GPIO functionality for the VA416xx family.
//!
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
//! which must be provided for async support to work. However, it provides the
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
//!
//! # Example
//!
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
use core::future::Future;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal_async::digital::Wait;
use portable_atomic::AtomicBool;
use va416xx::{self as pac};
use crate::enable_nvic_interrupt;
use super::{
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
NUM_PINS_PORT_A_TO_F,
};
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_C: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_D: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_E: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static WAKERS_FOR_PORT_F: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_C: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_D: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_E: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
static EDGE_DETECTION_PORT_F: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("port G does not support async functionality")]
pub struct PortGDoesNotSupportAsyncError;
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AsyncDynPinError {
#[error("invalid pin type: {0}")]
InvalidPinType(#[from] InvalidPinTypeError),
#[error("port g does not support async functionality: {0}")]
PortGDoesNotSupportAsync(#[from] PortGDoesNotSupportAsyncError),
}
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
///
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
/// matching the [Port] argument.
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
/// as well as update the static edge detection structures. This allows the pin future tocomplete
/// complete async operations.
pub fn on_interrupt_for_async_gpio_for_port(
port: Port,
) -> Result<(), PortGDoesNotSupportAsyncError> {
let periphs = unsafe { pac::Peripherals::steal() };
let (irq_enb, edge_status, wakers, edge_detection) = match port {
Port::A => (
periphs.porta.irq_enb().read().bits(),
periphs.porta.edge_status().read().bits(),
&WAKERS_FOR_PORT_A,
&EDGE_DETECTION_PORT_A,
),
Port::B => (
periphs.portb.irq_enb().read().bits(),
periphs.portb.edge_status().read().bits(),
&WAKERS_FOR_PORT_B,
&EDGE_DETECTION_PORT_B,
),
Port::C => (
periphs.portc.irq_enb().read().bits(),
periphs.portc.edge_status().read().bits(),
&WAKERS_FOR_PORT_C,
&EDGE_DETECTION_PORT_C,
),
Port::D => (
periphs.portd.irq_enb().read().bits(),
periphs.portd.edge_status().read().bits(),
&WAKERS_FOR_PORT_D,
&EDGE_DETECTION_PORT_D,
),
Port::E => (
periphs.porte.irq_enb().read().bits(),
periphs.porte.edge_status().read().bits(),
&WAKERS_FOR_PORT_E,
&EDGE_DETECTION_PORT_E,
),
Port::F => (
periphs.portf.irq_enb().read().bits(),
periphs.portf.edge_status().read().bits(),
&WAKERS_FOR_PORT_F,
&EDGE_DETECTION_PORT_F,
),
Port::G => return Err(PortGDoesNotSupportAsyncError),
};
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
Ok(())
}
#[inline]
fn on_interrupt_for_port(
mut irq_enb: u32,
edge_status: u32,
wakers: &'static [AtomicWaker],
edge_detection: &'static [AtomicBool],
) {
while irq_enb != 0 {
let bit_pos = irq_enb.trailing_zeros() as usize;
let bit_mask = 1 << bit_pos;
wakers[bit_pos].wake();
if edge_status & bit_mask != 0 {
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
// Clear the processed bit
irq_enb &= !bit_mask;
}
}
}
/// Input pin future which implements the [Future] trait.
///
/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
/// struture is granted to allow writing custom async structures.
pub struct InputPinFuture {
pin_id: DynPinId,
waker_group: &'static [AtomicWaker],
edge_detection_group: &'static [AtomicBool],
}
impl InputPinFuture {
pub fn new_with_dyn_pin(
pin: &mut DynPin,
edge: InterruptEdge,
) -> Result<Self, AsyncDynPinError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()).into());
}
if pin.id().port() == Port::G {
return Err(PortGDoesNotSupportAsyncError.into());
}
let (waker_group, edge_detection_group) =
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
edge_detection_group[pin.id().num() as usize]
.store(false, core::sync::atomic::Ordering::Relaxed);
// Unwraps okay, checked for PORT G previously
pin.configure_edge_interrupt(edge).unwrap();
unsafe { enable_nvic_interrupt(pin.irq_id().unwrap()) };
pin.enable_interrupt();
Ok(Self {
pin_id: pin.id(),
waker_group,
edge_detection_group,
})
}
pub fn new_with_pin<I: PinId, C: InputConfig>(
pin: &mut Pin<I, pin::Input<C>>,
edge: InterruptEdge,
) -> Result<Self, PortGDoesNotSupportAsyncError> {
if pin.id().port() == Port::G {
return Err(PortGDoesNotSupportAsyncError);
}
let (waker_group, edge_detection_group) =
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
edge_detection_group[pin.id().num() as usize]
.store(false, core::sync::atomic::Ordering::Relaxed);
// Unwraps okay, checked for PORT G previously
pin.configure_edge_interrupt(edge);
unsafe { enable_nvic_interrupt(I::IRQ.unwrap()) };
pin.enable_interrupt();
Ok(Self {
pin_id: pin.id(),
waker_group,
edge_detection_group,
})
}
#[inline]
pub fn pin_group_to_waker_and_edge_detection_group(
group: Port,
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
match group {
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
Port::C => (WAKERS_FOR_PORT_C.as_ref(), EDGE_DETECTION_PORT_C.as_ref()),
Port::D => (WAKERS_FOR_PORT_D.as_ref(), EDGE_DETECTION_PORT_D.as_ref()),
Port::E => (WAKERS_FOR_PORT_E.as_ref(), EDGE_DETECTION_PORT_E.as_ref()),
Port::F => (WAKERS_FOR_PORT_F.as_ref(), EDGE_DETECTION_PORT_F.as_ref()),
_ => panic!("unexpected pin group G"),
}
}
}
impl Drop for InputPinFuture {
fn drop(&mut self) {
// The API ensures that we actually own the pin, so stealing it here is okay.
unsafe { DynPin::steal(self.pin_id) }.disable_interrupt();
}
}
impl Future for InputPinFuture {
type Output = ();
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
let idx = self.pin_id.num() as usize;
self.waker_group[idx].register(cx.waker());
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
return core::task::Poll::Ready(());
}
core::task::Poll::Pending
}
}
pub struct InputDynPinAsync {
pin: DynPin,
}
impl InputDynPinAsync {
/// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
/// for the asynchronous functionality to work.
pub fn new(pin: DynPin) -> Result<Self, AsyncDynPinError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()).into());
}
if pin.id().port() == Port::G {
return Err(PortGDoesNotSupportAsyncError.into());
}
Ok(Self { pin })
}
/// Asynchronously wait until the pin is high.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
// Unwrap okay, checked pin in constructor.
let fut =
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
if self.pin.is_high().unwrap() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin is low.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_low(&mut self) {
// Unwrap okay, checked pin in constructor.
let fut =
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
if self.pin.is_low().unwrap() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin sees a falling edge.
pub async fn wait_for_falling_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::HighToLow)
.unwrap()
.await;
}
/// Asynchronously wait until the pin sees a rising edge.
pub async fn wait_for_rising_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::LowToHigh)
.unwrap()
.await;
}
/// Asynchronously wait until the pin sees any edge (either rising or falling).
pub async fn wait_for_any_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::BothEdges)
.unwrap()
.await;
}
pub fn release(self) -> DynPin {
self.pin
}
}
impl embedded_hal::digital::ErrorType for InputDynPinAsync {
type Error = core::convert::Infallible;
}
impl Wait for InputDynPinAsync {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
self.wait_for_high().await;
Ok(())
}
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
self.wait_for_low().await;
Ok(())
}
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_rising_edge().await;
Ok(())
}
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_falling_edge().await;
Ok(())
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_any_edge().await;
Ok(())
}
}
pub struct InputPinAsync<I: PinId, C: InputConfig> {
pin: Pin<I, pin::Input<C>>,
}
impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
/// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
/// for the asynchronous functionality to work.
pub fn new(pin: Pin<I, pin::Input<C>>) -> Result<Self, PortGDoesNotSupportAsyncError> {
if pin.id().port() == Port::G {
return Err(PortGDoesNotSupportAsyncError);
}
Ok(Self { pin })
}
/// Asynchronously wait until the pin is high.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
// Unwrap okay, checked pin in constructor.
let fut = InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
if self.pin.is_high() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin is low.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_low(&mut self) {
let fut = InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
if self.pin.is_low() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin sees falling edge.
pub async fn wait_for_falling_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::HighToLow)
.unwrap()
.await;
}
/// Asynchronously wait until the pin sees rising edge.
pub async fn wait_for_rising_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::LowToHigh)
.unwrap()
.await;
}
/// Asynchronously wait until the pin sees any edge (either rising or falling).
pub async fn wait_for_any_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::BothEdges)
.unwrap()
.await;
}
pub fn release(self) -> Pin<I, pin::Input<C>> {
self.pin
}
}
impl<I: PinId, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
type Error = core::convert::Infallible;
}
impl<I: PinId, C: InputConfig> Wait for InputPinAsync<I, C> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
self.wait_for_high().await;
Ok(())
}
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
self.wait_for_low().await;
Ok(())
}
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_rising_edge().await;
Ok(())
}
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_falling_edge().await;
Ok(())
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_any_edge().await;
Ok(())
}
}

File diff suppressed because it is too large Load Diff

View File

@ -21,55 +21,53 @@
//! ## Examples //! ## Examples
//! //!
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs) //! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
#[derive(Debug, PartialEq, Eq)]
//==================================================================================================
// Errors, Definitions and Constants
//==================================================================================================
pub const NUM_PINS_PORT_A_TO_F: usize = 16;
pub const NUM_PINS_PORT_G: usize = 8;
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A_TO_F * 6 + NUM_PINS_PORT_G;
pub const NUM_GPIO_PINS_WITH_IRQ: usize = NUM_GPIO_PINS - NUM_PINS_PORT_G;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("pin is masked")]
pub struct IsMaskedError; pub struct IsMaskedError;
macro_rules! common_reg_if_functions { #[derive(Debug, PartialEq, Eq, Clone, Copy)]
() => { #[cfg_attr(feature = "defmt", derive(defmt::Format))]
paste::paste!( pub enum Port {
#[inline] A,
pub fn datamask(&self) -> bool { B,
self.regs.datamask() C,
} D,
E,
F,
G,
}
#[inline] #[derive(Debug, PartialEq, Eq)]
pub fn clear_datamask(self) -> Self { #[cfg_attr(feature = "defmt", derive(defmt::Format))]
self.regs.clear_datamask(); pub enum InterruptEdge {
self HighToLow,
} LowToHigh,
BothEdges,
}
#[inline] #[derive(Debug, PartialEq, Eq)]
pub fn set_datamask(self) -> Self { #[cfg_attr(feature = "defmt", derive(defmt::Format))]
self.regs.set_datamask(); pub enum InterruptLevel {
self Low = 0,
} High = 1,
}
#[inline] #[derive(Debug, PartialEq, Eq)]
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> { #[cfg_attr(feature = "defmt", derive(defmt::Format))]
self.regs.read_pin_masked() pub enum PinState {
} Low = 0,
High = 1,
#[inline]
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.regs.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.regs.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.regs.write_pin_masked(false)
}
fn irq_enb(&mut self) {
self.regs.enable_irq();
}
);
};
} }
pub mod pin; pub mod pin;
@ -78,4 +76,5 @@ pub use pin::*;
pub mod dynpin; pub mod dynpin;
pub use dynpin::*; pub use dynpin::*;
mod reg; pub mod asynch;
pub use asynch::*;

View File

@ -68,42 +68,19 @@
//! # Embedded HAL traits //! # Embedded HAL traits
//! //!
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`] //! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`], //! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin],
//! and [`StatefulOutputPin`]. //! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
use core::{convert::Infallible, marker::PhantomData, mem::transmute}; use core::{convert::Infallible, marker::PhantomData, mem::transmute};
pub use crate::clock::FilterClkSel; pub use crate::clock::FilterClkSel;
use crate::typelevel::Sealed; use crate::typelevel::Sealed;
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; use va416xx::{self as pac, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg};
use super::{ use super::{
reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode, DynAlternate, DynInput, DynOutput, DynPin, DynPinId, DynPinMode, InputPinAsync, InterruptEdge,
InterruptLevel, PinState, Port, PortGDoesNotSupportAsyncError,
}; };
//==================================================================================================
// Errors and Definitions
//==================================================================================================
#[derive(Debug, PartialEq, Eq)]
pub enum InterruptEdge {
HighToLow,
LowToHigh,
BothEdges,
}
#[derive(Debug, PartialEq, Eq)]
pub enum InterruptLevel {
Low = 0,
High = 1,
}
#[derive(Debug, PartialEq, Eq)]
pub enum PinState {
Low = 0,
High = 1,
}
//================================================================================================== //==================================================================================================
// Input configuration // Input configuration
//================================================================================================== //==================================================================================================
@ -292,10 +269,11 @@ impl<C: AlternateConfig> PinMode for Alternate<C> {
pub trait PinId: Sealed { pub trait PinId: Sealed {
/// Corresponding [DynPinId] /// Corresponding [DynPinId]
const DYN: DynPinId; const DYN: DynPinId;
const IRQ: Option<pac::Interrupt>;
} }
macro_rules! pin_id { macro_rules! pin_id {
($Group:ident, $Id:ident, $NUM:literal $(, $meta: meta)?) => { ($Port:ident, $Id:ident, $NUM:literal, $Irq:expr, $(, $meta: meta)?) => {
// Need paste macro to use ident in doc attribute // Need paste macro to use ident in doc attribute
paste::paste! { paste::paste! {
$(#[$meta])? $(#[$meta])?
@ -307,10 +285,8 @@ macro_rules! pin_id {
$(#[$meta])? $(#[$meta])?
impl PinId for $Id { impl PinId for $Id {
const DYN: DynPinId = DynPinId { const DYN: DynPinId = DynPinId::new(Port::$Port, $NUM);
group: DynGroup::$Group, const IRQ: Option<pac::Interrupt> = $Irq;
num: $NUM,
};
} }
} }
}; };
@ -321,9 +297,10 @@ 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> {
pub(in crate::gpio) regs: Registers<I>, inner: DynPin,
mode: PhantomData<M>, mode: PhantomData<(I, M)>,
} }
impl<I: PinId, M: PinMode> Pin<I, M> { impl<I: PinId, M: PinMode> Pin<I, M> {
@ -335,38 +312,48 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
/// at most one corresponding [`Pin`] in existence at any given time. /// at most one corresponding [`Pin`] in existence at any given time.
/// Violating this requirement is `unsafe`. /// Violating this requirement is `unsafe`.
#[inline] #[inline]
pub(crate) unsafe fn new() -> Pin<I, M> { pub(crate) const unsafe fn new() -> Pin<I, M> {
Pin { Pin {
regs: Registers::new(), inner: DynPin::new(I::DYN, M::DYN),
mode: PhantomData, mode: PhantomData,
} }
} }
#[inline]
pub const fn id(&self) -> DynPinId {
self.inner.id()
}
#[inline(always)]
pub const fn irq_id(&self) -> Option<pac::Interrupt> {
I::IRQ
}
/// Convert the pin to the requested [`PinMode`] /// Convert the pin to the requested [`PinMode`]
#[inline] #[inline]
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> { pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
// Only modify registers if we are actually changing pin mode // Only modify registers if we are actually changing pin mode
// This check should compile away // This check should compile away
if N::DYN != M::DYN { if N::DYN != M::DYN {
self.regs.change_mode::<N>(); self.inner.change_mode(N::DYN);
} }
// Safe because we drop the existing Pin // Safe because we drop the existing Pin
unsafe { Pin::new() } unsafe { Pin::new() }
} }
/// Configure the pin for function select 1. See Programmer Guide p.40 for the function table /// Configure the pin for function select 1. See Programmer Guide p. 286 for the function table
#[inline] #[inline]
pub fn into_funsel_1(self) -> Pin<I, AltFunc1> { pub fn into_funsel_1(self) -> Pin<I, AltFunc1> {
self.into_mode() self.into_mode()
} }
/// Configure the pin for function select 2. See Programmer Guide p.40 for the function table /// Configure the pin for function select 2. See Programmer Guide p. 286 for the function table
#[inline] #[inline]
pub fn into_funsel_2(self) -> Pin<I, AltFunc2> { pub fn into_funsel_2(self) -> Pin<I, AltFunc2> {
self.into_mode() self.into_mode()
} }
/// Configure the pin for function select 3. See Programmer Guide p.40 for the function table /// Configure the pin for function select 3. See Programmer Guide p. 286 for the function table
#[inline] #[inline]
pub fn into_funsel_3(self) -> Pin<I, AltFunc3> { pub fn into_funsel_3(self) -> Pin<I, AltFunc3> {
self.into_mode() self.into_mode()
@ -408,26 +395,76 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
self.into_mode() self.into_mode()
} }
common_reg_if_functions!();
#[inline] #[inline]
pub(crate) fn _set_high(&mut self) { pub fn is_low(&self) -> bool {
self.regs.write_pin(true) !self.inner.read_pin()
} }
#[inline] #[inline]
pub(crate) fn _set_low(&mut self) { pub fn is_high(&self) -> bool {
self.regs.write_pin(false) self.inner.read_pin()
} }
#[inline] #[inline]
pub(crate) fn _is_low(&self) -> bool { pub fn datamask(&self) -> bool {
!self.regs.read_pin() self.inner.datamask()
} }
#[inline] #[inline]
pub(crate) fn _is_high(&self) -> bool { pub fn clear_datamask(&mut self) {
self.regs.read_pin() self.inner.clear_datamask()
}
#[inline]
pub fn set_datamask(&mut self) {
self.inner.set_datamask()
}
#[inline]
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.inner.is_high_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.inner.is_low_masked()
}
#[inline]
pub fn downgrade(self) -> DynPin {
self.inner
}
// Those only serve for the embedded HAL implementations which have different mutability.
#[inline]
fn is_low_mut(&mut self) -> bool {
self.is_low()
}
#[inline]
fn is_high_mut(&mut self) -> bool {
self.is_high()
}
#[inline]
pub fn enable_interrupt(&mut self) {
self.inner.enable_interrupt();
}
#[inline]
pub fn disable_interrupt(&mut self) {
self.inner.disable_interrupt();
}
/// Configure the pin for an edge interrupt but does not enable the interrupt.
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
self.inner.configure_edge_interrupt(edge_type).unwrap();
}
/// Configure the pin for a level interrupt but does not enable the interrupt.
pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) {
self.inner.configure_level_interrupt(level_type).unwrap();
} }
} }
@ -519,58 +556,61 @@ impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
//================================================================================================== //==================================================================================================
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> { impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Self { /// Convert the pin into an async pin. The pin can be converted back by calling
self.regs.interrupt_edge(edge_type); /// [InputPinAsync::release]
self.irq_enb(); pub fn into_async_input(self) -> Result<InputPinAsync<I, C>, PortGDoesNotSupportAsyncError> {
self InputPinAsync::new(self)
}
pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self {
self.regs.interrupt_level(level_type);
self.irq_enb();
self
} }
} }
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> { impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
/// See p.53 of the programmers guide for more information. #[inline]
pub fn set_high(&mut self) {
self.inner.write_pin(true)
}
#[inline]
pub fn set_low(&mut self) {
self.inner.write_pin(false)
}
#[inline]
pub fn toggle(&mut self) {
self.inner.toggle().unwrap()
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_high_masked()
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_low_masked()
}
/// Possible delays in clock cycles: /// Possible delays in clock cycles:
/// - Delay 1: 1 /// - Delay 1: 1
/// - Delay 2: 2 /// - Delay 2: 2
/// - Delay 1 + Delay 2: 3 /// - Delay 1 + Delay 2: 3
#[inline] #[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self { pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.regs.delay(delay_1, delay_2); self.inner.configure_delay(delay_1, delay_2).unwrap();
self
} }
/// See p.52 of the programmers guide for more information.
/// When configured for pulse mode, a given pin will set the non-default state for exactly /// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state /// one clock cycle before returning to the configured default state
pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self { pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.regs.pulse_mode(enable, default_state); self.inner
self .configure_pulse_mode(enable, default_state)
} .unwrap();
pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Self {
self.regs.interrupt_edge(edge_type);
self.irq_enb();
self
}
pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self {
self.regs.interrupt_level(level_type);
self.irq_enb();
self
} }
} }
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> { impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// See p.37 and p.38 of the programmers guide for more information.
#[inline] #[inline]
pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self { pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.regs.filter_type(filter, clksel); self.inner.configure_filter_type(filter, clksel).unwrap();
self
} }
} }
@ -578,7 +618,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
// Embedded HAL traits // Embedded HAL traits
//================================================================================================== //==================================================================================================
impl<I, M> ErrorType for Pin<I, M> impl<I, M> embedded_hal::digital::ErrorType for Pin<I, M>
where where
I: PinId, I: PinId,
M: PinMode, M: PinMode,
@ -586,104 +626,69 @@ where
type Error = Infallible; type Error = Infallible;
} }
impl<I: PinId, C: OutputConfig> OutputPin for Pin<I, Output<C>> { impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
#[inline] #[inline]
fn set_high(&mut self) -> Result<(), Self::Error> { fn set_high(&mut self) -> Result<(), Self::Error> {
self._set_high(); self.set_high();
Ok(()) Ok(())
} }
#[inline] #[inline]
fn set_low(&mut self) -> Result<(), Self::Error> { fn set_low(&mut self) -> Result<(), Self::Error> {
self._set_low(); self.set_low();
Ok(()) Ok(())
} }
} }
impl<I, C> InputPin for Pin<I, Input<C>> impl<I, C> embedded_hal::digital::InputPin for Pin<I, Input<C>>
where where
I: PinId, I: PinId,
C: InputConfig, C: InputConfig,
{ {
#[inline] #[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> { fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_high()) Ok(self.is_high_mut())
} }
#[inline] #[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> { fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_low()) Ok(self.is_low_mut())
} }
} }
impl<I, C> StatefulOutputPin for Pin<I, Output<C>> impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
where where
I: PinId, I: PinId,
C: OutputConfig + ReadableOutput, C: OutputConfig + ReadableOutput,
{ {
#[inline] #[inline]
fn is_set_high(&mut self) -> Result<bool, Self::Error> { fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_high()) Ok(self.is_high())
} }
#[inline] #[inline]
fn is_set_low(&mut self) -> Result<bool, Self::Error> { fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_low()) Ok(self.is_low())
}
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self.toggle();
Ok(())
} }
} }
impl<I, C> InputPin for Pin<I, Output<C>> impl<I, C> embedded_hal::digital::InputPin for Pin<I, Output<C>>
where where
I: PinId, I: PinId,
C: OutputConfig + ReadableOutput, C: OutputConfig + ReadableOutput,
{ {
#[inline] #[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> { fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_high()) Ok(self.is_high_mut())
} }
#[inline] #[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> { fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self._is_low()) Ok(self.is_low_mut())
}
}
//==================================================================================================
// Registers
//==================================================================================================
/// Provide a safe register interface for [`Pin`]s
///
/// This `struct` takes ownership of a [`PinId`] and provides an API to
/// access the corresponding registers.
pub(in crate::gpio) struct Registers<I: PinId> {
id: PhantomData<I>,
}
// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that
// each pin is a singleton, so this implementation is safe.
unsafe impl<I: PinId> RegisterInterface for Registers<I> {
#[inline]
fn id(&self) -> DynPinId {
I::DYN
}
}
impl<I: PinId> Registers<I> {
/// Create a new instance of [`Registers`]
///
/// # Safety
///
/// Users must never create two simultaneous instances of this `struct` with
/// the same [`PinId`]
#[inline]
unsafe fn new() -> Self {
Registers { id: PhantomData }
}
/// Provide a type-level equivalent for the
/// [`RegisterInterface::change_mode`] method.
#[inline]
pub(in crate::gpio) fn change_mode<M: PinMode>(&mut self) {
RegisterInterface::change_mode(self, M::DYN);
} }
} }
@ -745,20 +750,31 @@ macro_rules! pins {
} }
} }
//$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+] macro_rules! declare_pins_with_irq {
//$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal, $meta: meta),)+] (
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]
) => {
pins!($Port, $PinsName, $($Id $(, $meta)?)+,);
$(
paste::paste! {
pin_id!($Group, $Id, $NUM, Some(pac::Interrupt::[<$Port:upper $NUM>]), $(, $meta)?);
}
)+
}
}
macro_rules! declare_pins { macro_rules! declare_pins {
( (
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+] $Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]
) => { ) => {
pins!($Port, $PinsName, $($Id $(, $meta)?)+,); pins!($Port, $PinsName, $($Id $(, $meta)?)+,);
$( $(
pin_id!($Group, $Id, $NUM $(, $meta)?); pin_id!($Group, $Id, $NUM, None, $(, $meta)?);
)+ )+
} }
} }
declare_pins!( declare_pins_with_irq!(
A, A,
PinsA, PinsA,
Porta, Porta,
@ -782,7 +798,7 @@ declare_pins!(
] ]
); );
declare_pins!( declare_pins_with_irq!(
B, B,
PinsB, PinsB,
Portb, Portb,
@ -806,7 +822,7 @@ declare_pins!(
] ]
); );
declare_pins!( declare_pins_with_irq!(
C, C,
PinsC, PinsC,
Portc, Portc,
@ -830,7 +846,7 @@ declare_pins!(
] ]
); );
declare_pins!( declare_pins_with_irq!(
D, D,
PinsD, PinsD,
Portd, Portd,
@ -854,7 +870,7 @@ declare_pins!(
] ]
); );
declare_pins!( declare_pins_with_irq!(
E, E,
PinsE, PinsE,
Porte, Porte,
@ -878,7 +894,7 @@ declare_pins!(
] ]
); );
declare_pins!( declare_pins_with_irq!(
F, F,
PinsF, PinsF,
Portf, Portf,

View File

@ -1,379 +0,0 @@
use crate::FunSel;
use super::{
dynpin::{self, DynGroup, DynPinId},
DynPinMode, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, IsMaskedError, PinState,
};
use va416xx::{ioconfig, porta, Ioconfig, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
/// Type definition to avoid confusion: These register blocks are identical
type PortRegisterBlock = porta::RegisterBlock;
//==================================================================================================
// ModeFields
//==================================================================================================
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
#[derive(Default)]
struct ModeFields {
dir: bool,
opendrn: bool,
pull_en: bool,
/// true for pullup, false for pulldown
pull_dir: bool,
funsel: u8,
enb_input: bool,
}
impl From<DynPinMode> for ModeFields {
#[inline]
fn from(mode: DynPinMode) -> Self {
let mut fields = Self::default();
use DynPinMode::*;
match mode {
Input(config) => {
use dynpin::DynInput::*;
fields.dir = false;
fields.funsel = FunSel::Sel0 as u8;
match config {
Floating => (),
PullUp => {
fields.pull_en = true;
fields.pull_dir = true;
}
PullDown => {
fields.pull_en = true;
}
}
}
Output(config) => {
use dynpin::DynOutput::*;
fields.dir = true;
fields.funsel = FunSel::Sel0 as u8;
match config {
PushPull => (),
OpenDrain => {
fields.opendrn = true;
}
ReadableOpenDrain => {
fields.enb_input = true;
fields.opendrn = true;
}
ReadablePushPull => {
fields.enb_input = true;
}
}
}
Alternate(config) => {
fields.funsel = config as u8;
}
}
fields
}
}
//==============================================================================
// RegisterInterface
//==============================================================================
pub type PortReg = ioconfig::Porta;
/// Provide a safe register interface for pin objects
///
/// [`PORT`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
/// interface is quite restrictive. Instead, it would be ideal if we could split
/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`].
///
/// [`PORT`] is a single, zero-sized marker `struct` that provides access to
/// every [`PORT`] register. Instead, we would like to create zero-sized marker
/// `struct`s for every pin, where each pin is only allowed to control its own
/// registers. Furthermore, each pin `struct` should be a singleton, so that
/// exclusive access to the `struct` also guarantees exclusive access to the
/// corresponding registers. Finally, the pin `struct`s should not have any
/// interior mutability. Together, these requirements would allow the pin
/// `struct`s to be both [`Send`] and [`Sync`].
///
/// This trait creates a safe API for accomplishing these goals. Implementers
/// supply a pin ID through the [`id`] function. The remaining functions provide
/// a safe API for accessing the registers associated with that pin ID. Any
/// modification of the registers requires `&mut self`, which destroys interior
/// mutability.
///
/// # Safety
///
/// Users should only implement the [`id`] function. No default function
/// implementations should be overridden. The implementing type must also have
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
/// pin ID is a singleton.
///
/// [`id`]: Self::id
pub(super) unsafe trait RegisterInterface {
/// Provide a [`DynPinId`] identifying the set of registers controlled by
/// this type.
fn id(&self) -> DynPinId;
/// Change the pin mode
#[inline]
fn change_mode(&mut self, mode: DynPinMode) {
let ModeFields {
dir,
funsel,
opendrn,
pull_dir,
pull_en,
enb_input,
} = mode.into();
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
iocfg.write(|w| {
w.opendrn().bit(opendrn);
w.pen().bit(pull_en);
w.plevel().bit(pull_dir);
w.iewo().bit(enb_input);
unsafe { w.funsel().bits(funsel) }
});
let mask = self.mask_32();
unsafe {
if dir {
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
// Clear output
portreg.clrout().write(|w| w.bits(mask));
} else {
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
}
}
}
#[inline]
fn port_reg(&self) -> &PortRegisterBlock {
match self.id().group {
DynGroup::A => unsafe { &(*Porta::ptr()) },
DynGroup::B => unsafe { &(*Portb::ptr()) },
DynGroup::C => unsafe { &(*Portc::ptr()) },
DynGroup::D => unsafe { &(*Portd::ptr()) },
DynGroup::E => unsafe { &(*Porte::ptr()) },
DynGroup::F => unsafe { &(*Portf::ptr()) },
DynGroup::G => unsafe { &(*Portg::ptr()) },
}
}
fn iocfg_port(&self) -> &PortReg {
let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() };
match self.id().group {
DynGroup::A => ioconfig.porta(self.id().num as usize),
DynGroup::B => ioconfig.portb0(self.id().num as usize),
DynGroup::C => ioconfig.portc0(self.id().num as usize),
DynGroup::D => ioconfig.portd0(self.id().num as usize),
DynGroup::E => ioconfig.porte0(self.id().num as usize),
DynGroup::F => ioconfig.portf0(self.id().num as usize),
DynGroup::G => ioconfig.portg0(self.id().num as usize),
}
}
#[inline]
fn mask_32(&self) -> u32 {
1 << self.id().num
}
#[inline]
fn enable_irq(&self) {
self.port_reg()
.irq_enb()
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
}
#[inline]
/// Read the logic level of an output pin
fn read_pin(&self) -> bool {
let portreg = self.port_reg();
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
}
// Get DATAMASK bit for this particular pin
#[inline(always)]
fn datamask(&self) -> bool {
let portreg = self.port_reg();
(portreg.datamask().read().bits() >> self.id().num) == 1
}
/// Read a pin but use the masked version but check whether the datamask for the pin is
/// cleared as well
#[inline(always)]
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
if !self.datamask() {
Err(IsMaskedError)
} else {
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
}
}
/// Write the logic level of an output pin
#[inline(always)]
fn write_pin(&mut self, bit: bool) {
// Safety: SETOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe {
if bit {
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
} else {
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
}
}
}
/// Write the logic level of an output pin but check whether the datamask for the pin is
/// cleared as well
#[inline]
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
if !self.datamask() {
Err(IsMaskedError)
} else {
// Safety: SETOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe {
if bit {
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
} else {
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
}
Ok(())
}
}
}
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
/// When using edge mode, it is possible to generate interrupts on both edges as well
#[inline]
fn interrupt_edge(&mut self, edge_type: InterruptEdge) {
unsafe {
self.port_reg()
.irq_sen()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
match edge_type {
InterruptEdge::HighToLow => {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
InterruptEdge::LowToHigh => {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
InterruptEdge::BothEdges => {
self.port_reg()
.irq_edge()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
}
/// Configure which edge or level type triggers an interrupt
#[inline]
fn interrupt_level(&mut self, level: InterruptLevel) {
unsafe {
self.port_reg()
.irq_sen()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
if level == InterruptLevel::Low {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} else {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
/// Only useful for input pins
#[inline]
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) {
self.iocfg_port().modify(|_, w| {
// Safety: Only write to register for this Pin ID
unsafe {
w.flttype().bits(filter as u8);
w.fltclk().bits(clksel as u8)
}
});
}
/// Set DATAMASK bit for this particular pin. 1 is the default
/// state of the bit and allows access of the corresponding bit
#[inline(always)]
fn set_datamask(&self) {
let portreg = self.port_reg();
unsafe {
portreg
.datamask()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
/// Clear DATAMASK bit for this particular pin. This prevents access
/// of the corresponding bit for output and input operations
#[inline(always)]
fn clear_datamask(&self) {
let portreg = self.port_reg();
unsafe {
portreg
.datamask()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}
/// Only useful for output pins
/// See p.52 of the programmers guide for more information.
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
fn pulse_mode(&self, enable: bool, default_state: PinState) {
let portreg = self.port_reg();
unsafe {
if enable {
portreg
.pulse()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.pulse()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
if default_state == PinState::Low {
portreg
.pulsebase()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} else {
portreg
.pulsebase()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
/// Only useful for output pins
fn delay(&self, delay_1: bool, delay_2: bool) {
let portreg = self.port_reg();
unsafe {
if delay_1 {
portreg
.delay1()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.delay1()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
if delay_2 {
portreg
.delay2()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.delay2()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}
}
}

View File

@ -28,37 +28,42 @@ pub enum FifoEmptyMode {
EndTransaction = 1, EndTransaction = 1,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClockTooSlowForFastI2c; #[error("clock too slow for fast I2C mode")]
pub struct ClockTooSlowForFastI2cError;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[error("invalid timing parameters")]
pub struct InvalidTimingParamsError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error { pub enum Error {
InvalidTimingParams, #[error("arbitration lost")]
ArbitrationLost, ArbitrationLost,
#[error("nack address")]
NackAddr, NackAddr,
/// Data not acknowledged in write operation /// Data not acknowledged in write operation
#[error("data not acknowledged in write operation")]
NackData, NackData,
/// Not enough data received in read operation /// Not enough data received in read operation
#[error("insufficient data received")]
InsufficientDataReceived, InsufficientDataReceived,
/// Number of bytes in transfer too large (larger than 0x7fe) /// Number of bytes in transfer too large (larger than 0x7fe)
#[error("data too large (larger than 0x7fe)")]
DataTooLarge, DataTooLarge,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InitError { pub enum InitError {
/// Wrong address used in constructor /// Wrong address used in constructor
#[error("wrong address mode")]
WrongAddrMode, WrongAddrMode,
/// APB1 clock is too slow for fast I2C mode. /// APB1 clock is too slow for fast I2C mode.
ClkTooSlow(ClockTooSlowForFastI2c), #[error("clock too slow for fast I2C mode: {0}")]
} ClkTooSlow(#[from] ClockTooSlowForFastI2cError),
impl From<ClockTooSlowForFastI2c> for InitError {
fn from(value: ClockTooSlowForFastI2c) -> Self {
Self::ClkTooSlow(value)
}
} }
impl embedded_hal::i2c::Error for Error { impl embedded_hal::i2c::Error for Error {
@ -71,7 +76,7 @@ impl embedded_hal::i2c::Error for Error {
Error::NackData => { Error::NackData => {
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data) embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data)
} }
Error::DataTooLarge | Error::InsufficientDataReceived | Error::InvalidTimingParams => { Error::DataTooLarge | Error::InsufficientDataReceived => {
embedded_hal::i2c::ErrorKind::Other embedded_hal::i2c::ErrorKind::Other
} }
} }
@ -153,9 +158,12 @@ impl Instance for pac::I2c2 {
// 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,
@ -179,7 +187,7 @@ impl TimingCfg {
pub fn new( pub fn new(
first_16_bits: TrTfThighTlow, first_16_bits: TrTfThighTlow,
second_16_bits: TsuStoTsuStaThdStaTBuf, second_16_bits: TsuStoTsuStaThdStaTBuf,
) -> Result<Self, Error> { ) -> Result<Self, InvalidTimingParamsError> {
if first_16_bits.0 > 0xf if first_16_bits.0 > 0xf
|| first_16_bits.1 > 0xf || first_16_bits.1 > 0xf
|| first_16_bits.2 > 0xf || first_16_bits.2 > 0xf
@ -189,7 +197,7 @@ impl TimingCfg {
|| second_16_bits.2 > 0xf || second_16_bits.2 > 0xf
|| second_16_bits.3 > 0xf || second_16_bits.3 > 0xf
{ {
return Err(Error::InvalidTimingParams); return Err(InvalidTimingParamsError);
} }
Ok(TimingCfg { Ok(TimingCfg {
tr: first_16_bits.0, tr: first_16_bits.0,
@ -230,6 +238,7 @@ impl Default for TimingCfg {
} }
} }
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MasterConfig { pub struct MasterConfig {
pub tx_fe_mode: FifoEmptyMode, pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode, pub rx_fe_mode: FifoEmptyMode,
@ -256,6 +265,8 @@ impl Default for MasterConfig {
impl Sealed for MasterConfig {} impl Sealed for MasterConfig {}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SlaveConfig { pub struct SlaveConfig {
pub tx_fe_mode: FifoEmptyMode, pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode, pub rx_fe_mode: FifoEmptyMode,
@ -318,7 +329,7 @@ impl<I2c: Instance> I2cBase<I2c> {
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
ms_cfg: Option<&MasterConfig>, ms_cfg: Option<&MasterConfig>,
sl_cfg: Option<&SlaveConfig>, sl_cfg: Option<&SlaveConfig>,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
syscfg.enable_peripheral_clock(I2c::PERIPH_SEL); syscfg.enable_peripheral_clock(I2c::PERIPH_SEL);
let mut i2c_base = I2cBase { let mut i2c_base = I2cBase {
@ -421,19 +432,22 @@ impl<I2c: Instance> I2cBase<I2c> {
}); });
} }
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2c> { fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
if speed_mode == I2cSpeed::Regular100khz { if speed_mode == I2cSpeed::Regular100khz {
Ok(((self.clock.raw() / CLK_100K.raw() / 20) - 1) as u8) Ok(((self.clock.raw() / CLK_100K.raw() / 20) - 1) as u8)
} else { } else {
if self.clock.raw() < MIN_CLK_400K.raw() { if self.clock.raw() < MIN_CLK_400K.raw() {
return Err(ClockTooSlowForFastI2c); return Err(ClockTooSlowForFastI2cError);
} }
Ok(((self.clock.raw() / CLK_400K.raw() / 25) - 1) as u8) Ok(((self.clock.raw() / CLK_400K.raw() / 25) - 1) as u8)
} }
} }
/// Configures the clock scale for a given speed mode setting /// Configures the clock scale for a given speed mode setting
pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2c> { pub fn cfg_clk_scale(
&mut self,
speed_mode: I2cSpeed,
) -> Result<(), ClockTooSlowForFastI2cError> {
let clk_div = self.calc_clk_div(speed_mode)?; let clk_div = self.calc_clk_div(speed_mode)?;
self.i2c self.i2c
.clkscale() .clkscale()
@ -472,7 +486,7 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
cfg: MasterConfig, cfg: MasterConfig,
clocks: &Clocks, clocks: &Clocks,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cMaster { Ok(I2cMaster {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?, i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?,
addr: PhantomData, addr: PhantomData,
@ -733,7 +747,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
cfg: SlaveConfig, cfg: SlaveConfig,
clocks: &Clocks, clocks: &Clocks,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cSlave { Ok(I2cSlave {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&cfg))?, i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&cfg))?,
addr: PhantomData, addr: PhantomData,
@ -895,7 +909,7 @@ impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
cfg: SlaveConfig, cfg: SlaveConfig,
clocks: &Clocks, clocks: &Clocks,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode) Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)
} }
} }

View File

@ -35,6 +35,7 @@ compile_error!(
" "
); );
use gpio::Port;
pub use va416xx as device; pub use va416xx as device;
pub use va416xx as pac; pub use va416xx as pac;
@ -63,6 +64,7 @@ pub mod adc;
pub mod dac; pub mod dac;
#[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 {
Sel0 = 0b00, Sel0 = 0b00,
Sel1 = 0b01, Sel1 = 0b01,
@ -70,20 +72,52 @@ pub enum FunSel {
Sel3 = 0b11, Sel3 = 0b11,
} }
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("invalid pin with number {0}")]
pub struct InvalidPinError(u8);
/// Can be used to manually manipulate the function select of port pins.
///
/// The function selection table can be found on p.286 of the programmers guide. Please note
/// that most of the structures and APIs in this library will automatically correctly configure
/// the pin or statically expect the correct pin type.
#[inline]
pub fn port_function_select(
ioconfig: &mut pac::Ioconfig,
port: Port,
pin: u8,
funsel: FunSel,
) -> Result<(), InvalidPinError> {
if (port == Port::G && pin >= 8) || pin >= 16 {
return Err(InvalidPinError(pin));
}
let reg_block = match port {
Port::A => ioconfig.porta(pin as usize),
Port::B => ioconfig.portb0(pin as usize),
Port::C => ioconfig.portc0(pin as usize),
Port::D => ioconfig.portd0(pin as usize),
Port::E => ioconfig.porte0(pin as usize),
Port::F => ioconfig.portf0(pin as usize),
Port::G => ioconfig.portg0(pin as usize),
};
reg_block.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
Ok(())
}
/// Enable a specific interrupt using the NVIC peripheral. /// Enable a specific interrupt using the NVIC peripheral.
/// ///
/// # Safety /// # Safety
/// ///
/// This function is `unsafe` because it can break mask-based critical sections. /// This function is `unsafe` because it can break mask-based critical sections.
#[inline] #[inline]
pub unsafe fn enable_interrupt(irq: pac::Interrupt) { pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
unsafe { cortex_m::peripheral::NVIC::unmask(irq);
cortex_m::peripheral::NVIC::unmask(irq);
}
} }
/// Disable a specific interrupt using the NVIC peripheral. /// Disable a specific interrupt using the NVIC peripheral.
#[inline] #[inline]
pub fn disable_interrupt(irq: pac::Interrupt) { pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
cortex_m::peripheral::NVIC::mask(irq); cortex_m::peripheral::NVIC::mask(irq);
} }

View File

@ -16,7 +16,9 @@ use crate::{clock::Clocks, gpio::DynPinId};
const DUTY_MAX: u16 = u16::MAX; const DUTY_MAX: u16 = u16::MAX;
pub struct PwmBase { #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct PwmCommon {
clock: Hertz, clock: Hertz,
/// For PWMB, this is the upper limit /// For PWMB, this is the upper limit
current_duty: u16, current_duty: u16,
@ -34,129 +36,13 @@ enum StatusSelPwm {
pub struct PwmA {} pub struct PwmA {}
pub struct PwmB {} pub struct PwmB {}
//==================================================================================================
// Common
//==================================================================================================
macro_rules! pwm_common_func {
() => {
#[inline]
fn enable_pwm_a(&mut self) {
self.reg
.reg_block()
.ctrl()
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
}
#[inline]
fn enable_pwm_b(&mut self) {
self.reg
.reg_block()
.ctrl()
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
}
#[inline]
pub fn get_period(&self) -> Hertz {
self.pwm_base.current_period
}
#[inline]
pub fn set_period(&mut self, period: impl Into<Hertz>) {
self.pwm_base.current_period = period.into();
// Avoid division by 0
if self.pwm_base.current_period.raw() == 0 {
return;
}
self.pwm_base.current_rst_val =
self.pwm_base.clock.raw() / self.pwm_base.current_period.raw();
self.reg
.reg_block()
.rst_value()
.write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) });
}
#[inline]
pub fn disable(&mut self) {
self.reg
.reg_block()
.ctrl()
.modify(|_, w| w.enable().clear_bit());
}
#[inline]
pub fn enable(&mut self) {
self.reg
.reg_block()
.ctrl()
.modify(|_, w| w.enable().set_bit());
}
#[inline]
pub fn period(&self) -> Hertz {
self.pwm_base.current_period
}
#[inline(always)]
pub fn duty(&self) -> u16 {
self.pwm_base.current_duty
}
};
}
macro_rules! pwmb_func {
() => {
pub fn pwmb_lower_limit(&self) -> u16 {
self.pwm_base.current_lower_limit
}
pub fn pwmb_upper_limit(&self) -> u16 {
self.pwm_base.current_duty
}
/// Set the lower limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is larger than
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
self.pwm_base.current_lower_limit = duty;
let pwmb_val: u64 = (self.pwm_base.current_rst_val as u64
* self.pwm_base.current_lower_limit as u64)
/ DUTY_MAX as u64;
self.reg
.reg_block()
.pwmb_value()
.write(|w| unsafe { w.bits(pwmb_val as u32) });
}
/// Set the higher limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is smaller than
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
self.pwm_base.current_duty = duty;
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
* self.pwm_base.current_duty as u64)
/ DUTY_MAX as u64;
self.reg
.reg_block()
.pwma_value()
.write(|w| unsafe { w.bits(pwma_val as u32) });
}
};
}
//================================================================================================== //==================================================================================================
// Strongly typed PWM pin // Strongly typed PWM pin
//================================================================================================== //==================================================================================================
pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> { pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> {
reg: TimAndPinRegister<Pin, Tim>, reg: TimAndPinRegister<Pin, Tim>,
pwm_base: PwmBase, inner: ReducedPwmPin<Mode>,
mode: PhantomData<Mode>, mode: PhantomData<Mode>,
} }
@ -172,13 +58,17 @@ where
initial_period: impl Into<Hertz> + Copy, initial_period: impl Into<Hertz> + Copy,
) -> Self { ) -> Self {
let mut pin = PwmPin { let mut pin = PwmPin {
pwm_base: PwmBase { inner: ReducedPwmPin::<Mode>::new(
current_duty: 0, Tim::ID,
current_lower_limit: 0, Pin::DYN,
current_period: initial_period.into(), PwmCommon {
current_rst_val: 0, clock: Tim::clock(clocks),
clock: Tim::clock(clocks), current_duty: 0,
}, current_lower_limit: 0,
current_period: initial_period.into(),
current_rst_val: 0,
},
),
reg: unsafe { TimAndPinRegister::new(pin_and_tim.0, pin_and_tim.1) }, reg: unsafe { TimAndPinRegister::new(pin_and_tim.0, pin_and_tim.1) },
mode: PhantomData, mode: PhantomData,
}; };
@ -190,11 +80,53 @@ where
pin pin
} }
pub fn downgrade(self) -> ReducedPwmPin<Mode> {
self.inner
}
pub fn release(self) -> (Pin, Tim) { pub fn release(self) -> (Pin, Tim) {
self.reg.release() self.reg.release()
} }
pwm_common_func!(); #[inline]
fn enable_pwm_a(&mut self) {
self.inner.enable_pwm_a();
}
#[inline]
fn enable_pwm_b(&mut self) {
self.inner.enable_pwm_b();
}
#[inline]
pub fn get_period(&self) -> Hertz {
self.inner.get_period()
}
#[inline]
pub fn set_period(&mut self, period: impl Into<Hertz>) {
self.inner.set_period(period);
}
#[inline]
pub fn disable(&mut self) {
self.inner.disable();
}
#[inline]
pub fn enable(&mut self) {
self.inner.enable();
}
#[inline]
pub fn period(&self) -> Hertz {
self.inner.period()
}
#[inline(always)]
pub fn duty(&self) -> u16 {
self.inner.duty()
}
} }
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB> impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB>
@ -204,7 +136,7 @@ where
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self { fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
let mut pwmb = Self { let mut pwmb = Self {
reg: other.reg, reg: other.reg,
pwm_base: other.pwm_base, inner: other.inner.into(),
mode: PhantomData, mode: PhantomData,
}; };
pwmb.enable_pwm_b(); pwmb.enable_pwm_b();
@ -219,7 +151,7 @@ where
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self { fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
let mut pwmb = Self { let mut pwmb = Self {
reg: other.reg, reg: other.reg,
pwm_base: other.pwm_base, inner: other.inner.into(),
mode: PhantomData, mode: PhantomData,
}; };
pwmb.enable_pwm_a(); pwmb.enable_pwm_a();
@ -267,33 +199,105 @@ where
/// Reduced version where type information is deleted /// Reduced version where type information is deleted
pub struct ReducedPwmPin<Mode = PwmA> { pub struct ReducedPwmPin<Mode = PwmA> {
reg: TimDynRegister, dyn_reg: TimDynRegister,
pwm_base: PwmBase, common: PwmCommon,
pin_id: DynPinId,
mode: PhantomData<Mode>, mode: PhantomData<Mode>,
} }
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PwmA> { impl<Mode> ReducedPwmPin<Mode> {
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self { pub(crate) fn new(tim_id: u8, pin_id: DynPinId, common: PwmCommon) -> Self {
ReducedPwmPin { Self {
reg: TimDynRegister::from(pwm_pin.reg), dyn_reg: TimDynRegister { tim_id, pin_id },
pwm_base: pwm_pin.pwm_base, common,
pin_id: PIN::DYN,
mode: PhantomData, mode: PhantomData,
} }
} }
#[inline]
fn enable_pwm_a(&mut self) {
self.dyn_reg
.reg_block()
.ctrl()
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
}
#[inline]
fn enable_pwm_b(&mut self) {
self.dyn_reg
.reg_block()
.ctrl()
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
}
#[inline]
pub fn get_period(&self) -> Hertz {
self.common.current_period
}
#[inline]
pub fn set_period(&mut self, period: impl Into<Hertz>) {
self.common.current_period = period.into();
// Avoid division by 0
if self.common.current_period.raw() == 0 {
return;
}
self.common.current_rst_val = self.common.clock.raw() / self.common.current_period.raw();
self.dyn_reg
.reg_block()
.rst_value()
.write(|w| unsafe { w.bits(self.common.current_rst_val) });
}
#[inline]
pub fn disable(&mut self) {
self.dyn_reg
.reg_block()
.ctrl()
.modify(|_, w| w.enable().clear_bit());
}
#[inline]
pub fn enable(&mut self) {
self.dyn_reg
.reg_block()
.ctrl()
.modify(|_, w| w.enable().set_bit());
}
#[inline]
pub fn period(&self) -> Hertz {
self.common.current_period
}
#[inline(always)]
pub fn duty(&self) -> u16 {
self.common.current_duty
}
} }
impl<MODE> ReducedPwmPin<MODE> { impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for ReducedPwmPin<PwmA>
pwm_common_func!(); 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 {
reg: other.reg, dyn_reg: other.dyn_reg,
pwm_base: other.pwm_base, common: other.common,
pin_id: other.pin_id,
mode: PhantomData, mode: PhantomData,
}; };
pwmb.enable_pwm_b(); pwmb.enable_pwm_b();
@ -304,9 +308,8 @@ impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> { impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
fn from(other: ReducedPwmPin<PwmB>) -> Self { fn from(other: ReducedPwmPin<PwmB>) -> Self {
let mut pwmb = Self { let mut pwmb = Self {
reg: other.reg, dyn_reg: other.dyn_reg,
pwm_base: other.pwm_base, common: other.common,
pin_id: other.pin_id,
mode: PhantomData, mode: PhantomData,
}; };
pwmb.enable_pwm_a(); pwmb.enable_pwm_a();
@ -318,15 +321,83 @@ impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
// PWMB implementations // PWMB implementations
//================================================================================================== //==================================================================================================
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PwmB> impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
where where
(PIN, TIM): ValidTimAndPin<PIN, TIM>, (Pin, Tim): ValidTimAndPin<Pin, Tim>,
{ {
pwmb_func!(); pub fn pwmb_lower_limit(&self) -> u16 {
self.inner.pwmb_lower_limit()
}
pub fn pwmb_upper_limit(&self) -> u16 {
self.inner.pwmb_upper_limit()
}
/// Set the lower limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is larger than
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
self.inner.set_pwmb_lower_limit(duty);
}
/// Set the higher limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is smaller than
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
self.inner.set_pwmb_upper_limit(duty);
}
} }
impl ReducedPwmPin<PwmB> { impl ReducedPwmPin<PwmB> {
pwmb_func!(); #[inline(always)]
pub fn pwmb_lower_limit(&self) -> u16 {
self.common.current_lower_limit
}
#[inline(always)]
pub fn pwmb_upper_limit(&self) -> u16 {
self.common.current_duty
}
/// Set the lower limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is larger than
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
#[inline(always)]
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
self.common.current_lower_limit = duty;
let pwmb_val: u64 = (self.common.current_rst_val as u64
* self.common.current_lower_limit as u64)
/ DUTY_MAX as u64;
self.dyn_reg
.reg_block()
.pwmb_value()
.write(|w| unsafe { w.bits(pwmb_val as u32) });
}
/// Set the higher limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is smaller than
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
self.common.current_duty = duty;
let pwma_val: u64 = (self.common.current_rst_val as u64 * self.common.current_duty as u64)
/ DUTY_MAX as u64;
self.dyn_reg
.reg_block()
.pwma_value()
.write(|w| unsafe { w.bits(pwma_val as u32) });
}
} }
//================================================================================================== //==================================================================================================
@ -349,11 +420,11 @@ impl embedded_hal::pwm::SetDutyCycle for ReducedPwmPin {
#[inline] #[inline]
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
self.pwm_base.current_duty = duty; self.common.current_duty = duty;
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64 let pwma_val: u64 = (self.common.current_rst_val as u64
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64)) * (DUTY_MAX as u64 - self.common.current_duty as u64))
/ DUTY_MAX as u64; / DUTY_MAX as u64;
self.reg self.dyn_reg
.reg_block() .reg_block()
.pwma_value() .pwma_value()
.write(|w| unsafe { w.bits(pwma_val as u32) }); .write(|w| unsafe { w.bits(pwma_val as u32) });
@ -369,15 +440,7 @@ impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::SetDutyCycle for PwmPin<Pin,
#[inline] #[inline]
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
self.pwm_base.current_duty = duty; self.inner.set_duty_cycle(duty)
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
/ DUTY_MAX as u64;
self.reg
.reg_block()
.pwma_value()
.write(|w| unsafe { w.bits(pwma_val as u32) });
Ok(())
} }
} }

View File

@ -39,6 +39,7 @@ pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
pub const BMSKIPDATA_MASK: u32 = 1 << 30; pub const BMSKIPDATA_MASK: u32 = 1 << 30;
#[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,
@ -52,6 +53,7 @@ pub enum HwChipSelectId {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiId { pub enum SpiId {
Spi0, Spi0,
Spi1, Spi1,
@ -61,6 +63,7 @@ pub enum SpiId {
} }
#[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,
@ -68,6 +71,57 @@ pub enum WordSize {
SixteenBits = 0x0f, SixteenBits = 0x0f,
} }
pub type SpiRegBlock = pac::spi0::RegisterBlock;
/// Common trait implemented by all PAC peripheral access structures. The register block
/// format is the same for all SPI blocks.
pub trait Instance: Deref<Target = SpiRegBlock> {
const IDX: u8;
const PERIPH_SEL: PeripheralSelect;
fn ptr() -> *const SpiRegBlock;
}
impl Instance for pac::Spi0 {
const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
impl Instance for pac::Spi1 {
const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
impl Instance for pac::Spi2 {
const IDX: u8 = 2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
impl Instance for pac::Spi3 {
const IDX: u8 = 3;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi3;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
//================================================================================================== //==================================================================================================
// Pin type definitions // Pin type definitions
//================================================================================================== //==================================================================================================
@ -239,6 +293,7 @@ pub trait TransferConfigProvider {
/// This struct contains all configuration parameter which are transfer specific /// This struct contains all configuration parameter which are transfer specific
/// and might change for transfers to different SPI slaves /// and might change for transfers to different SPI slaves
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfigWithHwcs<HwCs> { pub struct TransferConfigWithHwcs<HwCs> {
pub hw_cs: Option<HwCs>, pub hw_cs: Option<HwCs>,
pub cfg: TransferConfig, pub cfg: TransferConfig,
@ -247,6 +302,7 @@ pub struct TransferConfigWithHwcs<HwCs> {
/// Type erased variant of the transfer configuration. This is required to avoid generics in /// Type erased variant of the transfer configuration. This is required to avoid generics in
/// the SPI constructor. /// the SPI constructor.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfig { pub struct TransferConfig {
pub clk_cfg: Option<SpiClkConfig>, pub clk_cfg: Option<SpiClkConfig>,
pub mode: Option<Mode>, pub mode: Option<Mode>,
@ -334,6 +390,8 @@ impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfigWithHwcs<HwCs>
} }
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details /// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiConfig { pub struct SpiConfig {
clk: SpiClkConfig, clk: SpiClkConfig,
// SPI mode configuration // SPI mode configuration
@ -432,57 +490,6 @@ impl WordProvider for u16 {
} }
} }
pub type SpiRegBlock = pac::spi0::RegisterBlock;
/// Common trait implemented by all PAC peripheral access structures. The register block
/// format is the same for all SPI blocks.
pub trait Instance: Deref<Target = SpiRegBlock> {
const IDX: u8;
const PERIPH_SEL: PeripheralSelect;
fn ptr() -> *const SpiRegBlock;
}
impl Instance for pac::Spi0 {
const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
impl Instance for pac::Spi1 {
const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
impl Instance for pac::Spi2 {
const IDX: u8 = 2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
impl Instance for pac::Spi3 {
const IDX: u8 = 3;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi3;
#[inline(always)]
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
//================================================================================================== //==================================================================================================
// Spi // Spi
//================================================================================================== //==================================================================================================
@ -533,6 +540,7 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
pins: Pins, pins: Pins,
} }
#[inline(always)]
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) { pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
match mode { match mode {
embedded_hal::spi::MODE_0 => (false, false), embedded_hal::spi::MODE_0 => (false, false),
@ -575,10 +583,14 @@ impl SpiClkConfig {
} }
} }
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
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,
} }

View File

@ -24,8 +24,7 @@ use crate::gpio::{
use crate::time::Hertz; use crate::time::Hertz;
use crate::typelevel::Sealed; use crate::typelevel::Sealed;
use crate::{disable_interrupt, prelude::*}; use crate::{disable_nvic_interrupt, enable_nvic_interrupt, pac, prelude::*};
use crate::{enable_interrupt, pac};
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0)); pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
@ -78,6 +77,7 @@ pub const unsafe fn get_tim_raw(tim_idx: usize) -> &'static pac::tim0::RegisterB
//================================================================================================== //==================================================================================================
#[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,
@ -405,7 +405,7 @@ pub type TimRegBlock = pac::tim0::RegisterBlock;
/// ///
/// # Safety /// # Safety
/// ///
/// Users should only implement the [`tim_id`] function. No default function /// Users should only implement the [Self::tim_id] function. No default function
/// implementations should be overridden. The implementing type must also have /// implementations should be overridden. The implementing type must also have
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each /// "control" over the corresponding pin ID, i.e. it must guarantee that a each
/// pin ID is a singleton. /// pin ID is a singleton.
@ -502,10 +502,10 @@ unsafe impl<Pin: TimPin, Tim: ValidTim> TimRegInterface for TimAndPinRegister<Pi
} }
} }
pub(super) struct TimDynRegister { pub(crate) struct TimDynRegister {
tim_id: u8, pub(crate) tim_id: u8,
#[allow(dead_code)] #[allow(dead_code)]
pin_id: DynPinId, pub(crate) pin_id: DynPinId,
} }
impl<Pin: TimPin, Tim: ValidTim> From<TimAndPinRegister<Pin, Tim>> for TimDynRegister { impl<Pin: TimPin, Tim: ValidTim> From<TimAndPinRegister<Pin, Tim>> for TimDynRegister {
@ -589,7 +589,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
pub fn listen(&mut self) { pub fn listen(&mut self) {
self.listening = true; self.listening = true;
self.enable_interrupt(); self.enable_interrupt();
unsafe { enable_interrupt(Tim::IRQ) } unsafe { enable_nvic_interrupt(Tim::IRQ) }
} }
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the /// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
@ -617,7 +617,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
pub fn unlisten(&mut self) { pub fn unlisten(&mut self) {
self.listening = true; self.listening = true;
self.disable_interrupt(); self.disable_interrupt();
disable_interrupt(Tim::IRQ); disable_nvic_interrupt(Tim::IRQ);
} }
#[inline(always)] #[inline(always)]

View File

@ -1,7 +1,7 @@
//! # API for the UART peripheral //! # API for the UART peripheral
//! //!
//! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures. //! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures.
//! The RX structure also has a dedicated [RxWithIrq] variant which allows reading the receiver //! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver
//! using interrupts. //! using interrupts.
//! //!
//! ## Examples //! ## Examples
@ -18,7 +18,7 @@ use fugit::RateExtU32;
use crate::clock::{Clocks, PeripheralSelect, SyscfgExt}; use crate::clock::{Clocks, PeripheralSelect, SyscfgExt};
use crate::gpio::PF13; use crate::gpio::PF13;
use crate::time::Hertz; use crate::time::Hertz;
use crate::{disable_interrupt, enable_interrupt}; use crate::{disable_nvic_interrupt, enable_nvic_interrupt};
use crate::{ use crate::{
gpio::{ gpio::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2, AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2,
@ -30,6 +30,14 @@ use crate::{
#[cfg(not(feature = "va41628"))] #[cfg(not(feature = "va41628"))]
use crate::gpio::{PC15, PF8}; use crate::gpio::{PC15, PF8};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
Uart0 = 0,
Uart1 = 1,
Uart2 = 2,
}
//================================================================================================== //==================================================================================================
// Type-Level support // Type-Level support
//================================================================================================== //==================================================================================================
@ -248,6 +256,7 @@ impl From<Hertz> for Config {
//================================================================================================== //==================================================================================================
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqContextTimeoutOrMaxSize { pub struct IrqContextTimeoutOrMaxSize {
rx_idx: usize, rx_idx: usize,
mode: IrqReceptionMode, mode: IrqReceptionMode,
@ -273,17 +282,19 @@ impl IrqContextTimeoutOrMaxSize {
/// This struct is used to return the default IRQ handler result to the user /// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)] #[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqResult { pub struct IrqResult {
pub bytes_read: usize, pub bytes_read: usize,
pub errors: Option<IrqUartError>, pub errors: Option<UartErrors>,
} }
/// This struct is used to return the default IRQ handler result to the user /// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)] #[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqResultMaxSizeOrTimeout { pub struct IrqResultMaxSizeOrTimeout {
complete: bool, complete: bool,
timeout: bool, timeout: bool,
pub errors: Option<IrqUartError>, pub errors: Option<UartErrors>,
pub bytes_read: usize, pub bytes_read: usize,
} }
@ -330,20 +341,22 @@ impl IrqResultMaxSizeOrTimeout {
} }
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum IrqReceptionMode { enum IrqReceptionMode {
Idle, Idle,
Pending, Pending,
} }
#[derive(Default, Debug, Copy, Clone)] #[derive(Default, Debug, Copy, Clone)]
pub struct IrqUartError { #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UartErrors {
overflow: bool, overflow: bool,
framing: bool, framing: bool,
parity: bool, parity: bool,
other: bool, other: bool,
} }
impl IrqUartError { impl UartErrors {
#[inline(always)] #[inline(always)]
pub fn overflow(&self) -> bool { pub fn overflow(&self) -> bool {
self.overflow self.overflow
@ -365,7 +378,7 @@ impl IrqUartError {
} }
} }
impl IrqUartError { impl UartErrors {
#[inline(always)] #[inline(always)]
pub fn error(&self) -> bool { pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity self.overflow || self.framing || self.parity
@ -386,6 +399,7 @@ pub struct BufferTooShortError {
pub trait Instance: Deref<Target = uart_base::RegisterBlock> { pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
const IDX: u8; const IDX: u8;
const PERIPH_SEL: PeripheralSelect; const PERIPH_SEL: PeripheralSelect;
const PTR: *const uart_base::RegisterBlock;
const IRQ_RX: pac::Interrupt; const IRQ_RX: pac::Interrupt;
const IRQ_TX: pac::Interrupt; const IRQ_TX: pac::Interrupt;
@ -395,7 +409,21 @@ pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
/// ///
/// This circumvents the safety guarantees of the HAL. /// This circumvents the safety guarantees of the HAL.
unsafe fn steal() -> Self; unsafe fn steal() -> Self;
fn ptr() -> *const uart_base::RegisterBlock;
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock {
Self::PTR
}
/// Retrieve the type erased peripheral register block.
///
/// # 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 Uart0 { impl Instance for Uart0 {
@ -403,12 +431,13 @@ impl Instance for Uart0 {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART0_RX; const IRQ_RX: pac::Interrupt = pac::Interrupt::UART0_RX;
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART0_TX; const IRQ_TX: pac::Interrupt = pac::Interrupt::UART0_TX;
const PTR: *const uart_base::RegisterBlock = Self::PTR;
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uart0 Self::steal()
} }
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uart0::ptr() as *const _ Self::ptr() as *const _
} }
} }
@ -417,12 +446,13 @@ impl Instance for Uart1 {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART1_RX; const IRQ_RX: pac::Interrupt = pac::Interrupt::UART1_RX;
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART1_TX; const IRQ_TX: pac::Interrupt = pac::Interrupt::UART1_TX;
const PTR: *const uart_base::RegisterBlock = Self::PTR;
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uart1 Self::steal()
} }
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uart1::ptr() as *const _ Self::ptr() as *const _
} }
} }
@ -431,12 +461,28 @@ impl Instance for Uart2 {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart2; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart2;
const IRQ_RX: pac::Interrupt = pac::Interrupt::UART2_RX; const IRQ_RX: pac::Interrupt = pac::Interrupt::UART2_RX;
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART2_TX; const IRQ_TX: pac::Interrupt = pac::Interrupt::UART2_TX;
const PTR: *const uart_base::RegisterBlock = Self::PTR;
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uart2 Self::steal()
} }
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uart2::ptr() as *const _ Self::ptr() as *const _
}
}
impl Bank {
/// Retrieve the peripheral register block.
///
/// # Safety
///
/// Circumvents the HAL safety guarantees.
pub unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock {
match self {
Bank::Uart0 => unsafe { pac::Uart0::reg_block() },
Bank::Uart1 => unsafe { pac::Uart1::reg_block() },
Bank::Uart2 => unsafe { pac::Uart2::reg_block() },
}
} }
} }
@ -575,7 +621,8 @@ impl<Uart: Instance> UartBase<Uart> {
w.rxenable().clear_bit(); w.rxenable().clear_bit();
w.txenable().clear_bit() w.txenable().clear_bit()
}); });
disable_interrupt(Uart::IRQ_RX); disable_nvic_interrupt(Uart::IRQ_RX);
disable_nvic_interrupt(Uart::IRQ_TX);
self.uart self.uart
} }
@ -628,10 +675,10 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
Uart<UartInstance, (TxPinInst, RxPinInst)> Uart<UartInstance, (TxPinInst, RxPinInst)>
{ {
pub fn new( pub fn new(
syscfg: &mut va416xx::Sysconfig,
uart: UartInstance, uart: UartInstance,
pins: (TxPinInst, RxPinInst), pins: (TxPinInst, RxPinInst),
config: impl Into<Config>, config: impl Into<Config>,
syscfg: &mut va416xx::Sysconfig,
clocks: &Clocks, clocks: &Clocks,
) -> Self { ) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
@ -649,10 +696,10 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
} }
pub fn new_with_clock_freq( pub fn new_with_clock_freq(
syscfg: &mut va416xx::Sysconfig,
uart: UartInstance, uart: UartInstance,
pins: (TxPinInst, RxPinInst), pins: (TxPinInst, RxPinInst),
config: impl Into<Config>, config: impl Into<Config>,
syscfg: &mut va416xx::Sysconfig,
clock: impl Into<Hertz>, clock: impl Into<Hertz>,
) -> Self { ) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
@ -719,6 +766,34 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
} }
} }
#[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.
@ -753,6 +828,15 @@ impl<Uart: Instance> Rx<Uart> {
self.0.enable().modify(|_, w| w.rxenable().clear_bit()); self.0.enable().modify(|_, w| w.rxenable().clear_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() });
}
/// Low level function to read a word from the UART FIFO. /// Low level function to read a word from the UART FIFO.
/// ///
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts. /// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
@ -780,8 +864,8 @@ impl<Uart: Instance> Rx<Uart> {
self.0.data().read().bits() self.0.data().read().bits()
} }
pub fn into_rx_with_irq(self) -> RxWithIrq<Uart> { pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
RxWithIrq(self) RxWithInterrupt(self)
} }
pub fn release(self) -> Uart { pub fn release(self) -> Uart {
@ -842,12 +926,51 @@ impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
} }
} }
#[inline(always)]
pub fn enable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().set_bit());
}
#[inline(always)]
pub fn disable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().clear_bit());
}
#[inline(always)]
pub fn enable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().set_bit();
w.irq_tx_status().set_bit();
w.irq_tx_empty().set_bit()
});
}
#[inline(always)]
pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().clear_bit();
w.irq_tx_status().clear_bit();
w.irq_tx_empty().clear_bit()
});
}
/// Serial transmitter /// Serial transmitter
/// ///
/// Can be created by using the [Uart::split] or [UartBase::split] API. /// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Tx<Uart>(Uart); pub struct Tx<Uart>(Uart);
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::steal())
}
#[inline(always)]
fn new(uart: Uart) -> Self { fn new(uart: Uart) -> Self {
Self(uart) Self(uart)
} }
@ -857,7 +980,8 @@ 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.
pub unsafe fn uart(&self) -> &Uart { #[inline(always)]
pub const unsafe fn uart(&self) -> &Uart {
&self.0 &self.0
} }
@ -876,6 +1000,27 @@ impl<Uart: Instance> Tx<Uart> {
self.0.enable().modify(|_, w| w.txenable().clear_bit()); self.0.enable().modify(|_, w| w.txenable().clear_bit());
} }
/// 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.
/// ///
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts. /// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
@ -901,6 +1046,11 @@ impl<Uart: Instance> Tx<Uart> {
pub fn write_fifo_unchecked(&self, data: u32) { pub fn write_fifo_unchecked(&self, data: u32) {
self.0.data().write(|w| unsafe { w.bits(data) }); self.0.data().write(|w| unsafe { w.bits(data) });
} }
#[inline]
pub fn into_async(self) -> TxAsync<Uart> {
TxAsync::new(self)
}
} }
impl<Uart> embedded_io::ErrorType for Tx<Uart> { impl<Uart> embedded_io::ErrorType for Tx<Uart> {
@ -962,15 +1112,15 @@ impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
/// then call the [Self::irq_handler_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 RxWithIrq<Uart>(Rx<Uart>); pub struct RxWithInterrupt<Uart>(Rx<Uart>);
impl<Uart: Instance> RxWithIrq<Uart> { impl<Uart: Instance> RxWithInterrupt<Uart> {
/// This function should be called once at initialization time if the regular /// This function should be called once at initialization time if the regular
/// [Self::irq_handler] is used to read the UART receiver to enable and start the receiver. /// [Self::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.0.enable();
self.enable_rx_irq_sources(true); self.enable_rx_irq_sources(true);
unsafe { enable_interrupt(Uart::IRQ_RX) }; unsafe { enable_nvic_interrupt(Uart::IRQ_RX) };
} }
#[inline(always)] #[inline(always)]
@ -1164,7 +1314,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
fn read_handler( fn read_handler(
&self, &self,
errors: &mut Option<IrqUartError>, errors: &mut Option<UartErrors>,
read_res: &nb::Result<u8, RxError>, read_res: &nb::Result<u8, RxError>,
) -> Option<u8> { ) -> Option<u8> {
match read_res { match read_res {
@ -1172,7 +1322,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
Err(nb::Error::WouldBlock) => None, Err(nb::Error::WouldBlock) => None,
Err(nb::Error::Other(e)) => { Err(nb::Error::Other(e)) => {
// Ensure `errors` is Some(IrqUartError), initializing if it's None // Ensure `errors` is Some(IrqUartError), initializing if it's None
let err = errors.get_or_insert(IrqUartError::default()); let err = errors.get_or_insert(UartErrors::default());
// Now we can safely modify fields inside `err` // Now we can safely modify fields inside `err`
match e { match e {
@ -1185,14 +1335,14 @@ impl<Uart: Instance> RxWithIrq<Uart> {
} }
} }
fn check_for_errors(&self, errors: &mut Option<IrqUartError>) { fn check_for_errors(&self, errors: &mut Option<UartErrors>) {
let rx_status = self.uart().rxstatus().read(); let rx_status = self.uart().rxstatus().read();
if rx_status.rxovr().bit_is_set() if rx_status.rxovr().bit_is_set()
|| rx_status.rxfrm().bit_is_set() || rx_status.rxfrm().bit_is_set()
|| rx_status.rxpar().bit_is_set() || rx_status.rxpar().bit_is_set()
{ {
let err = errors.get_or_insert(IrqUartError::default()); let err = errors.get_or_insert(UartErrors::default());
if rx_status.rxovr().bit_is_set() { if rx_status.rxovr().bit_is_set() {
err.overflow = true; err.overflow = true;
@ -1228,3 +1378,9 @@ impl<Uart: Instance> RxWithIrq<Uart> {
self.0.release() self.0.release()
} }
} }
pub mod tx_asynch;
pub use tx_asynch::*;
pub mod rx_asynch;
pub use rx_asynch::*;

View File

@ -0,0 +1,448 @@
//! # Async UART reception functionality for the VA416xx family.
//!
//! This module provides the [RxAsync] and [RxAsyncOverwriting] 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 two interrupt handlers:
//!
//! - [on_interrupt_rx]
//! - [on_interrupt_rx_overwriting]
//!
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
//!
//! 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 portable_atomic::AtomicBool;
use va416xx::uart0 as uart_base;
use crate::enable_nvic_interrupt;
use super::{Bank, Instance, Rx, RxError, UartErrors};
static UART_RX_WAKERS: [AtomicWaker; 3] = [const { AtomicWaker::new() }; 3];
static RX_READ_ACTIVE: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
static RX_HAS_DATA: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
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: &'static uart_base::RegisterBlock) -> 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(
bank: Bank,
rx_enabled: bool,
read_some_data: bool,
irq_end: u32,
) -> Option<UartErrors> {
let idx = bank as usize;
if read_some_data {
RX_HAS_DATA[idx].store(true, Ordering::Relaxed);
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) {
UART_RX_WAKERS[idx].wake();
}
}
let mut errors = None;
let uart_regs = unsafe { bank.reg_block() };
// Check for RX errors
if rx_enabled {
errors = on_interrupt_handle_rx_errors(uart_regs);
}
// Clear the interrupt status bits
uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) });
errors
}
/// Interrupt handler with overwriting behaviour when the ring buffer is full.
///
/// 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_rx_overwriting<const N: usize>(
bank: Bank,
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(bank, prod, shared_consumer)
}
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
let uart_regs = unsafe { bank.reg_block() };
let irq_end = uart_regs.irq_end().read();
let enb_status = uart_regs.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_regs.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_regs.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_regs.rxstatus().read().rdavl().bit_is_set() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart_regs.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(bank, 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 asynchronous RX operations.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_rx<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(bank, prod)
}
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
let uart = unsafe { bank.reg_block() };
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(bank, 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);
}
}
struct RxAsyncInner<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
pub queue: heapless::spsc::Consumer<'static, u8, N>,
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, data will be lost.
pub struct RxAsync<Uart: Instance, const N: usize>(Option<RxAsyncInner<Uart, 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;
}
fn stop_async_rx<Uart: Instance>(rx: &mut Rx<Uart>) {
rx.disable_interrupts();
rx.disable();
unsafe {
enable_nvic_interrupt(Uart::IRQ_RX);
}
rx.clear_fifo();
}
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 [on_interrupt_rx].
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(|_| {
unsafe {
enable_nvic_interrupt(Uart::IRQ_RX);
}
rx.enable_interrupts();
rx.enable();
});
Self(Some(RxAsyncInner { rx, queue }))
}
pub fn stop(&mut self) {
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
}
pub fn release(mut self) -> (Rx<Uart>, heapless::spsc::Consumer<'static, u8, N>) {
self.stop();
let inner = self.0.take().unwrap();
(inner.rx, inner.queue)
}
}
impl<Uart: Instance, const N: usize> Drop for RxAsync<Uart, N> {
fn drop(&mut self) {
self.stop();
}
}
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.0.as_ref().unwrap().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 mut_ref = self.0.as_mut().unwrap();
let fut = RxFuture::new(&mut mut_ref.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(&mut mut_ref.queue);
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
Ok(handle_data_in_queue(&mut mut_ref.queue))
}
}
struct RxAsyncOverwritingInner<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
/// [on_interrupt_rx_overwriting] interrupt handlers.
pub struct RxAsyncOverwriting<Uart: Instance, const N: usize>(
Option<RxAsyncOverwritingInner<Uart, N>>,
);
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncOverwriting<Uart, N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
impl<Uart: Instance, const N: usize> RxAsyncOverwriting<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>,
shared_consumer: &'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(Some(RxAsyncOverwritingInner {
rx,
shared_consumer,
}))
}
pub fn stop(&mut self) {
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
}
pub fn release(mut self) -> Rx<Uart> {
self.stop();
let inner = self.0.take().unwrap();
inner.rx
}
}
impl<Uart: Instance, const N: usize> Drop for RxAsyncOverwriting<Uart, N> {
fn drop(&mut self) {
self.stop();
}
}
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncOverwriting<Uart, N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
// 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.0.as_ref().unwrap().shared_consumer.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 = |inner: &mut RxAsyncOverwritingInner<Uart, N>| {
critical_section::with(|cs| {
let mut consumer_ref = inner.shared_consumer.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.0.as_mut().unwrap().rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
Ok(read_data)
}
}

View File

@ -0,0 +1,263 @@
//! # Async UART transmission functionality for the VA416xx 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 the [on_interrupt_tx] interrupt handler.
//!
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
//! for a given UART bank.
//!
//! # Example
//!
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va416xx-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; 3] = [const { AtomicWaker::new() }; 3];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 3] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 3];
// Completion flag. Kept outside of the context structure as an atomic to avoid
// critical section.
static TX_DONE: [AtomicBool; 3] = [const { AtomicBool::new(false) }; 3];
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
/// UART bank.
///
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
/// the given UART bank.
pub fn on_interrupt_tx(bank: Bank) {
let uart = unsafe { bank.reg_block() };
let idx = bank as usize;
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[idx].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[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
// Transfer is done.
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
UART_TX_WAKERS[idx].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[idx].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::Uart0::reg_block() },
1 => unsafe { pac::Uart1::reg_block() },
2 => unsafe { pac::Uart2::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> {
/// Create a new asynchronous TX object.
///
/// This function also enable the NVIC interrupt, but does not enable the peripheral specific
/// interrupts.
pub fn new(tx: Tx<Uart>) -> Self {
// Safety: We own TX now.
unsafe { enable_nvic_interrupt(Uart::IRQ_TX) };
Self { tx }
}
/// This function also disables the NVIC interrupt.
pub fn release(self) -> Tx<Uart> {
disable_nvic_interrupt(Uart::IRQ_TX);
self.tx
}
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("TX overrun error")]
pub struct TxOverrunError;
impl embedded_io_async::Error for TxOverrunError {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
}
}
impl<Uart: Instance> embedded_io::ErrorType for TxAsync<Uart> {
type Error = TxOverrunError;
}
impl<Uart: Instance> Write for TxAsync<Uart> {
/// Write a buffer asynchronously.
///
/// This implementation is not side effect free, and a started future might have already
/// written part of the passed buffer.
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
fut.await
}
}

View File

@ -9,7 +9,7 @@ use crate::{
pac, pac,
prelude::SyscfgExt, prelude::SyscfgExt,
}; };
use crate::{disable_interrupt, enable_interrupt}; use crate::{disable_nvic_interrupt, enable_nvic_interrupt};
pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551; pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551;
@ -30,12 +30,12 @@ pub type WdtController = Wdt;
/// This function is `unsafe` because it can break mask-based critical sections. /// This function is `unsafe` because it can break mask-based critical sections.
#[inline] #[inline]
pub unsafe fn enable_wdt_interrupts() { pub unsafe fn enable_wdt_interrupts() {
enable_interrupt(pac::Interrupt::WATCHDOG) enable_nvic_interrupt(pac::Interrupt::WATCHDOG)
} }
#[inline] #[inline]
pub fn disable_wdt_interrupts() { pub fn disable_wdt_interrupts() {
disable_interrupt(pac::Interrupt::WATCHDOG) disable_nvic_interrupt(pac::Interrupt::WATCHDOG)
} }
impl Wdt { impl Wdt {

View File

@ -1,64 +1,44 @@
on: [push] name: ci
on: [push, pull_request]
name: build
jobs: jobs:
check: check:
name: Check name: Check build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1 - uses: dtolnay/rust-toolchain@stable
with: with:
profile: minimal targets: "thumbv7em-none-eabihf"
toolchain: stable - run: cargo check --target thumbv7em-none-eabihf
target: thumbv7em-none-eabihf - run: cargo check --target thumbv7em-none-eabihf --examples
override: true - run: cargo check -p va416xx --target thumbv7em-none-eabihf --all-features
- uses: actions-rs/cargo@v1 - run: cargo check -p va416xx-hal --target thumbv7em-none-eabihf --features "defmt"
with:
use-cross: true
command: check
args: --target thumbv7em-none-eabihf
fmt: fmt:
name: Rustfmt name: Check formatting
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1 - uses: dtolnay/rust-toolchain@stable
with: - run: cargo fmt --all -- --check
profile: minimal
toolchain: stable docs:
override: true name: Check Documentation Build
- run: rustup component add rustfmt runs-on: ubuntu-latest
- uses: actions-rs/cargo@v1 steps:
with: - uses: actions/checkout@v4
command: fmt - uses: dtolnay/rust-toolchain@nightly
args: --all -- --check - run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx --all-features
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx-hal --features "defmt va41630"
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-peb1
clippy: clippy:
name: Clippy name: Clippy
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1 - uses: dtolnay/rust-toolchain@stable
with: with:
profile: minimal targets: "thumbv7em-none-eabihf"
toolchain: stable - run: cargo clippy --target thumbv7em-none-eabihf -- -D warnings
target: thumbv7em-none-eabihf
override: true
- run: rustup component add clippy
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: clippy
args: --target thumbv7em-none-eabihf -- -D warnings
ci:
if: ${{ success() }}
# all new jobs must be added to this list
needs: [check, fmt, clippy]
runs-on: ubuntu-latest
steps:
- name: CI succeeded
run: exit 0

View File

@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.4.0] 2025-02-18
- Re-generated PAC with `svd2rust` v0.35.0 and added optional `defmt` and `Debug` implementations
## [v0.3.0] 2025-02-13 ## [v0.3.0] 2025-02-13
- Re-generated PAC with `svd2rust` v0.35.0 - Re-generated PAC with `svd2rust` v0.35.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va416xx" name = "va416xx"
version = "0.3.0" version = "0.4.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021" edition = "2021"
description = "PAC for the Vorago VA416xx family of MCUs" description = "PAC for the Vorago VA416xx family of MCUs"
@ -15,6 +15,8 @@ categories = ["embedded", "no-std", "hardware-support"]
[dependencies] [dependencies]
cortex-m = "0.7" cortex-m = "0.7"
vcell = "0.1.3" vcell = "0.1.3"
defmt = { version = "0.3", optional = true }
critical-section = { version = "1", optional = true } critical-section = { version = "1", optional = true }
[dependencies.cortex-m-rt] [dependencies.cortex-m-rt]
@ -23,6 +25,8 @@ version = ">=0.6.15,<0.8"
[features] [features]
rt = ["cortex-m-rt/device"] rt = ["cortex-m-rt/device"]
# Adds Debug implementation
debug = []
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@ -29,7 +29,7 @@ then
fi fi
svdtools patch svd/va416xx-patch.yml svdtools patch svd/va416xx-patch.yml
${svd2rust_bin} --reexport-interrupt -i svd/va416xx.svd.patched ${svd2rust_bin} --reexport-interrupt --impl-defmt defmt --impl-debug-feature debug -i svd/va416xx.svd.patched
result=$? result=$?
if [ $result -ne 0 ]; then if [ $result -ne 0 ]; then

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<AddressSpec>; pub type R = crate::R<AddressSpec>;
#[doc = "Register `ADDRESS` writer"] #[doc = "Register `ADDRESS` writer"]
pub type W = crate::W<AddressSpec>; pub type W = crate::W<AddressSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<ClktolimitSpec>; pub type R = crate::R<ClktolimitSpec>;
#[doc = "Register `CLKTOLIMIT` writer"] #[doc = "Register `CLKTOLIMIT` writer"]
pub type W = crate::W<ClktolimitSpec>; pub type W = crate::W<ClktolimitSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<CmdSpec>; pub type R = crate::R<CmdSpec>;
#[doc = "Register `CMD` writer"] #[doc = "Register `CMD` writer"]
pub type W = crate::W<CmdSpec>; pub type W = crate::W<CmdSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DataSpec>; pub type R = crate::R<DataSpec>;
#[doc = "Register `DATA` writer"] #[doc = "Register `DATA` writer"]
pub type W = crate::W<DataSpec>; pub type W = crate::W<DataSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `RXCOUNT` reader"] #[doc = "Register `RXCOUNT` reader"]
pub type R = crate::R<RxcountSpec>; pub type R = crate::R<RxcountSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<RxfifoirqtrgSpec>; pub type R = crate::R<RxfifoirqtrgSpec>;
#[doc = "Register `RXFIFOIRQTRG` writer"] #[doc = "Register `RXFIFOIRQTRG` writer"]
pub type W = crate::W<RxfifoirqtrgSpec>; pub type W = crate::W<RxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `S0_STATE` reader"] #[doc = "Register `S0_STATE` reader"]
pub type R = crate::R<S0StateSpec>; pub type R = crate::R<S0StateSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `STATE` reader"] #[doc = "Register `STATE` reader"]
pub type R = crate::R<StateSpec>; pub type R = crate::R<StateSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<TmconfigSpec>; pub type R = crate::R<TmconfigSpec>;
#[doc = "Register `TMCONFIG` writer"] #[doc = "Register `TMCONFIG` writer"]
pub type W = crate::W<TmconfigSpec>; pub type W = crate::W<TmconfigSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `TXCOUNT` reader"] #[doc = "Register `TXCOUNT` reader"]
pub type R = crate::R<TxcountSpec>; pub type R = crate::R<TxcountSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<TxfifoirqtrgSpec>; pub type R = crate::R<TxfifoirqtrgSpec>;
#[doc = "Register `TXFIFOIRQTRG` writer"] #[doc = "Register `TXFIFOIRQTRG` writer"]
pub type W = crate::W<TxfifoirqtrgSpec>; pub type W = crate::W<TxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<WordsSpec>; pub type R = crate::R<WordsSpec>;
#[doc = "Register `WORDS` writer"] #[doc = "Register `WORDS` writer"]
pub type W = crate::W<WordsSpec>; pub type W = crate::W<WordsSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `CLKDIV0` reader"] #[doc = "Register `CLKDIV0` reader"]
pub type R = crate::R<Clkdiv0Spec>; pub type R = crate::R<Clkdiv0Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Clkdiv1Spec>; pub type R = crate::R<Clkdiv1Spec>;
#[doc = "Register `CLKDIV1` writer"] #[doc = "Register `CLKDIV1` writer"]
pub type W = crate::W<Clkdiv1Spec>; pub type W = crate::W<Clkdiv1Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Clkdiv2Spec>; pub type R = crate::R<Clkdiv2Spec>;
#[doc = "Register `CLKDIV2` writer"] #[doc = "Register `CLKDIV2` writer"]
pub type W = crate::W<Clkdiv2Spec>; pub type W = crate::W<Clkdiv2Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Clkdiv3Spec>; pub type R = crate::R<Clkdiv3Spec>;
#[doc = "Register `CLKDIV3` writer"] #[doc = "Register `CLKDIV3` writer"]
pub type W = crate::W<Clkdiv3Spec>; pub type W = crate::W<Clkdiv3Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Clkdiv4Spec>; pub type R = crate::R<Clkdiv4Spec>;
#[doc = "Register `CLKDIV4` writer"] #[doc = "Register `CLKDIV4` writer"]
pub type W = crate::W<Clkdiv4Spec>; pub type W = crate::W<Clkdiv4Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Clkdiv5Spec>; pub type R = crate::R<Clkdiv5Spec>;
#[doc = "Register `CLKDIV5` writer"] #[doc = "Register `CLKDIV5` writer"]
pub type W = crate::W<Clkdiv5Spec>; pub type W = crate::W<Clkdiv5Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Clkdiv6Spec>; pub type R = crate::R<Clkdiv6Spec>;
#[doc = "Register `CLKDIV6` writer"] #[doc = "Register `CLKDIV6` writer"]
pub type W = crate::W<Clkdiv6Spec>; pub type W = crate::W<Clkdiv6Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Clkdiv7Spec>; pub type R = crate::R<Clkdiv7Spec>;
#[doc = "Register `CLKDIV7` writer"] #[doc = "Register `CLKDIV7` writer"]
pub type W = crate::W<Clkdiv7Spec>; pub type W = crate::W<Clkdiv7Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -3,6 +3,7 @@ pub type R = crate::R<PortaSpec>;
#[doc = "Register `PORTA[%s]` writer"] #[doc = "Register `PORTA[%s]` writer"]
pub type W = crate::W<PortaSpec>; pub type W = crate::W<PortaSpec>;
#[doc = "Input Filter Selectoin\n\nValue on reset: 0"] #[doc = "Input Filter Selectoin\n\nValue on reset: 0"]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)] #[repr(u8)]
pub enum Flttype { pub enum Flttype {

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -3,8 +3,6 @@ svd2rust release can be generated by cloning the svd2rust [repository], checking
#![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"]
@ -467,6 +465,7 @@ pub static __INTERRUPTS: [Vector; 196] = [
Vector { _handler: TXEV }, Vector { _handler: TXEV },
]; ];
#[doc = r"Enumeration of all the interrupts."] #[doc = r"Enumeration of all the interrupts."]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u16)] #[repr(u16)]
pub enum Interrupt { pub enum Interrupt {

View File

@ -1,5 +1,6 @@
#[doc = "Register `DATAIN` reader"] #[doc = "Register `DATAIN` reader"]
pub type R = crate::R<DatainSpec>; pub type R = crate::R<DatainSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `DATAINBYTE[%s]` reader"] #[doc = "Register `DATAINBYTE[%s]` reader"]
pub type R = crate::R<DatainbyteSpec>; pub type R = crate::R<DatainbyteSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DatamaskSpec>; pub type R = crate::R<DatamaskSpec>;
#[doc = "Register `DATAMASK` writer"] #[doc = "Register `DATAMASK` writer"]
pub type W = crate::W<DatamaskSpec>; pub type W = crate::W<DatamaskSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DatamaskbyteSpec>; pub type R = crate::R<DatamaskbyteSpec>;
#[doc = "Register `DATAMASKBYTE[%s]` writer"] #[doc = "Register `DATAMASKBYTE[%s]` writer"]
pub type W = crate::W<DatamaskbyteSpec>; pub type W = crate::W<DatamaskbyteSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `DATAOUT` writer"] #[doc = "Register `DATAOUT` writer"]
pub type W = crate::W<DataoutSpec>; pub type W = crate::W<DataoutSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for crate::generic::Reg<DataoutSpec> { impl core::fmt::Debug for crate::generic::Reg<DataoutSpec> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "(not readable)") write!(f, "(not readable)")

View File

@ -1,5 +1,6 @@
#[doc = "Register `DATAOUTBYTE[%s]` writer"] #[doc = "Register `DATAOUTBYTE[%s]` writer"]
pub type W = crate::W<DataoutbyteSpec>; pub type W = crate::W<DataoutbyteSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for crate::generic::Reg<DataoutbyteSpec> { impl core::fmt::Debug for crate::generic::Reg<DataoutbyteSpec> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "(not readable)") write!(f, "(not readable)")

View File

@ -2,6 +2,7 @@
pub type R = crate::R<EdgeStatusSpec>; pub type R = crate::R<EdgeStatusSpec>;
#[doc = "Register `EDGE_STATUS` writer"] #[doc = "Register `EDGE_STATUS` writer"]
pub type W = crate::W<EdgeStatusSpec>; pub type W = crate::W<EdgeStatusSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IrqEdgeSpec>; pub type R = crate::R<IrqEdgeSpec>;
#[doc = "Register `IRQ_EDGE` writer"] #[doc = "Register `IRQ_EDGE` writer"]
pub type W = crate::W<IrqEdgeSpec>; pub type W = crate::W<IrqEdgeSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IrqEnbSpec>; pub type R = crate::R<IrqEnbSpec>;
#[doc = "Register `IRQ_ENB` writer"] #[doc = "Register `IRQ_ENB` writer"]
pub type W = crate::W<IrqEnbSpec>; pub type W = crate::W<IrqEnbSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `IRQ_END` reader"] #[doc = "Register `IRQ_END` reader"]
pub type R = crate::R<IrqEndSpec>; pub type R = crate::R<IrqEndSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IrqEvtSpec>; pub type R = crate::R<IrqEvtSpec>;
#[doc = "Register `IRQ_EVT` writer"] #[doc = "Register `IRQ_EVT` writer"]
pub type W = crate::W<IrqEvtSpec>; pub type W = crate::W<IrqEvtSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `IRQ_RAW` reader"] #[doc = "Register `IRQ_RAW` reader"]
pub type R = crate::R<IrqRawSpec>; pub type R = crate::R<IrqRawSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<IrqSenSpec>; pub type R = crate::R<IrqSenSpec>;
#[doc = "Register `IRQ_SEN` writer"] #[doc = "Register `IRQ_SEN` writer"]
pub type W = crate::W<IrqSenSpec>; pub type W = crate::W<IrqSenSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<ClkprescaleSpec>; pub type R = crate::R<ClkprescaleSpec>;
#[doc = "Register `CLKPRESCALE` writer"] #[doc = "Register `CLKPRESCALE` writer"]
pub type W = crate::W<ClkprescaleSpec>; pub type W = crate::W<ClkprescaleSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DataSpec>; pub type R = crate::R<DataSpec>;
#[doc = "Register `DATA` writer"] #[doc = "Register `DATA` writer"]
pub type W = crate::W<DataSpec>; pub type W = crate::W<DataSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<RxfifoirqtrgSpec>; pub type R = crate::R<RxfifoirqtrgSpec>;
#[doc = "Register `RXFIFOIRQTRG` writer"] #[doc = "Register `RXFIFOIRQTRG` writer"]
pub type W = crate::W<RxfifoirqtrgSpec>; pub type W = crate::W<RxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `STATE` reader"] #[doc = "Register `STATE` reader"]
pub type R = crate::R<StateSpec>; pub type R = crate::R<StateSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<TxfifoirqtrgSpec>; pub type R = crate::R<TxfifoirqtrgSpec>;
#[doc = "Register `TXFIFOIRQTRG` writer"] #[doc = "Register `TXFIFOIRQTRG` writer"]
pub type W = crate::W<TxfifoirqtrgSpec>; pub type W = crate::W<TxfifoirqtrgSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `EF_ID0` reader"] #[doc = "Register `EF_ID0` reader"]
pub type R = crate::R<EfId0Spec>; pub type R = crate::R<EfId0Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `EF_ID1` reader"] #[doc = "Register `EF_ID1` reader"]
pub type R = crate::R<EfId1Spec>; pub type R = crate::R<EfId1Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PROCID` reader"] #[doc = "Register `PROCID` reader"]
pub type R = crate::R<ProcidSpec>; pub type R = crate::R<ProcidSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<CntValueSpec>; pub type R = crate::R<CntValueSpec>;
#[doc = "Register `CNT_VALUE` writer"] #[doc = "Register `CNT_VALUE` writer"]
pub type W = crate::W<CntValueSpec>; pub type W = crate::W<CntValueSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -21,6 +21,7 @@ pub type IrqEnbR = crate::BitReader;
#[doc = "Field `IRQ_ENB` writer - Interrupt Enable"] #[doc = "Field `IRQ_ENB` writer - Interrupt Enable"]
pub type IrqEnbW<'a, REG> = crate::BitWriter<'a, REG>; pub type IrqEnbW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Counter Status Selection\n\nValue on reset: 0"] #[doc = "Counter Status Selection\n\nValue on reset: 0"]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)] #[repr(u8)]
pub enum StatusSel { pub enum StatusSel {

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<PwmValueSpec>; pub type R = crate::R<PwmValueSpec>;
#[doc = "Register `PWM_VALUE` writer"] #[doc = "Register `PWM_VALUE` writer"]
pub type W = crate::W<PwmValueSpec>; pub type W = crate::W<PwmValueSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<PwmaValueSpec>; pub type R = crate::R<PwmaValueSpec>;
#[doc = "Register `PWMA_VALUE` writer"] #[doc = "Register `PWMA_VALUE` writer"]
pub type W = crate::W<PwmaValueSpec>; pub type W = crate::W<PwmaValueSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<PwmbValueSpec>; pub type R = crate::R<PwmbValueSpec>;
#[doc = "Register `PWMB_VALUE` writer"] #[doc = "Register `PWMB_VALUE` writer"]
pub type W = crate::W<PwmbValueSpec>; pub type W = crate::W<PwmbValueSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<RstValueSpec>; pub type R = crate::R<RstValueSpec>;
#[doc = "Register `RST_VALUE` writer"] #[doc = "Register `RST_VALUE` writer"]
pub type W = crate::W<RstValueSpec>; pub type W = crate::W<RstValueSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Addr9Spec>; pub type R = crate::R<Addr9Spec>;
#[doc = "Register `ADDR9` writer"] #[doc = "Register `ADDR9` writer"]
pub type W = crate::W<Addr9Spec>; pub type W = crate::W<Addr9Spec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<Addr9maskSpec>; pub type R = crate::R<Addr9maskSpec>;
#[doc = "Register `ADDR9MASK` writer"] #[doc = "Register `ADDR9MASK` writer"]
pub type W = crate::W<Addr9maskSpec>; pub type W = crate::W<Addr9maskSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -2,6 +2,7 @@
pub type R = crate::R<DataSpec>; pub type R = crate::R<DataSpec>;
#[doc = "Register `DATA` writer"] #[doc = "Register `DATA` writer"]
pub type W = crate::W<DataSpec>; pub type W = crate::W<DataSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

View File

@ -1,5 +1,6 @@
#[doc = "Register `PERID` reader"] #[doc = "Register `PERID` reader"]
pub type R = crate::R<PeridSpec>; pub type R = crate::R<PeridSpec>;
#[cfg(feature = "debug")]
impl core::fmt::Debug for R { impl core::fmt::Debug for R {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.bits()) write!(f, "{}", self.bits())

Some files were not shown because too many files have changed in this diff Show More