Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 23f0fa3b0b | |||
| 682655c036 | |||
| 79b6f6124b | |||
| 2424fbf075 | |||
| eb41170fa2 | |||
| 8108beb70f | |||
| 4493cddb32 | |||
| f8114ba3f3 | |||
| 6f0890c6b2 | |||
| 3fcc13a179 | |||
| 427862d54b | |||
| db26553ed4 | |||
| 232b3e8c39 |
@@ -4,8 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = "1"
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
|
cfg-if = "1"
|
||||||
embedded-hal-async = "1"
|
embedded-hal-async = "1"
|
||||||
embedded-io = "0.7"
|
embedded-io = "0.7"
|
||||||
embedded-io-async = "0.7"
|
embedded-io-async = "0.7"
|
||||||
@@ -18,10 +19,10 @@ panic-probe = { version = "1", features = ["print-defmt"] }
|
|||||||
|
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
|
|
||||||
embassy-sync = "0.7"
|
embassy-sync = "0.8"
|
||||||
embassy-time = "0.5"
|
embassy-time = "0.5"
|
||||||
embassy-executor = { version = "0.9", features = [
|
embassy-executor = { version = "0.10", features = [
|
||||||
"arch-cortex-m",
|
"platform-cortex-m",
|
||||||
"executor-thread",
|
"executor-thread",
|
||||||
"executor-interrupt"
|
"executor-interrupt"
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// Safety: Only called once here.
|
// Safety: Only called once here.
|
||||||
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||||
|
unsafe {
|
||||||
|
cortex_m::interrupt::enable();
|
||||||
|
}
|
||||||
|
|
||||||
let porta = PinsA::new(dp.porta);
|
let porta = PinsA::new(dp.porta);
|
||||||
let portb = PinsB::new(dp.portb);
|
let portb = PinsB::new(dp.portb);
|
||||||
@@ -71,36 +74,35 @@ async fn main(spawner: Spawner) {
|
|||||||
let out_pb22 = Output::new(portb.pb22, PinState::Low);
|
let out_pb22 = Output::new(portb.pb22, PinState::Low);
|
||||||
let in_pb23 = Input::new_floating(portb.pb23);
|
let in_pb23 = Input::new_floating(portb.pb23);
|
||||||
|
|
||||||
let mut in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
|
let mut in_pa1_async = InputPinAsync::new(
|
||||||
let mut in_pb23_async = InputPinAsync::new(in_pb23, PB22_TO_PB23_IRQ);
|
in_pa1,
|
||||||
|
va108xx_hal::InterruptConfig::new(pac::Interrupt::OC10, true, true),
|
||||||
|
);
|
||||||
|
let mut in_pb23_async = InputPinAsync::new(
|
||||||
|
in_pb23,
|
||||||
|
va108xx_hal::InterruptConfig::new(PB22_TO_PB23_IRQ, true, true),
|
||||||
|
);
|
||||||
|
|
||||||
spawner
|
spawner.spawn(output_task("PA0 to PA1", out_pa0, CHANNEL_PA0_PA1.receiver()).unwrap());
|
||||||
.spawn(output_task(
|
spawner.spawn(output_task("PB22 to PB23", out_pb22, CHANNEL_PB22_TO_PB23.receiver()).unwrap());
|
||||||
"PA0 to PA1",
|
|
||||||
out_pa0,
|
|
||||||
CHANNEL_PA0_PA1.receiver(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
spawner
|
|
||||||
.spawn(output_task(
|
|
||||||
"PB22 to PB23",
|
|
||||||
out_pb22,
|
|
||||||
CHANNEL_PB22_TO_PB23.receiver(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if CHECK_PA0_TO_PA1 {
|
for i in 0..3 {
|
||||||
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), &mut in_pa1_async).await;
|
defmt::info!("Starting async GPIO operations check {}", i);
|
||||||
defmt::info!("Example PA0 to PA1 done");
|
if CHECK_PA0_TO_PA1 {
|
||||||
}
|
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), &mut in_pa1_async)
|
||||||
if CHECK_PB22_TO_PB23 {
|
.await;
|
||||||
check_pin_to_pin_async_ops(
|
defmt::info!("Example PA0 to PA1 done");
|
||||||
"PB22 to PB23",
|
}
|
||||||
CHANNEL_PB22_TO_PB23.sender(),
|
if CHECK_PB22_TO_PB23 {
|
||||||
&mut in_pb23_async,
|
check_pin_to_pin_async_ops(
|
||||||
)
|
"PB22 to PB23",
|
||||||
.await;
|
CHANNEL_PB22_TO_PB23.sender(),
|
||||||
defmt::info!("Example PB22 to PB23 done");
|
&mut in_pb23_async,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
defmt::info!("Example PB22 to PB23 done");
|
||||||
|
}
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
defmt::info!("Example done, toggling LED0");
|
defmt::info!("Example done, toggling LED0");
|
||||||
|
|||||||
@@ -97,9 +97,7 @@ async fn main(spawner: Spawner) {
|
|||||||
});
|
});
|
||||||
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
||||||
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
|
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
|
||||||
spawner
|
spawner.spawn(uart_b_task(async_rx_uart_b, tx_uart_b).unwrap());
|
||||||
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
|
|
||||||
.unwrap();
|
|
||||||
let mut buf = [0u8; 256];
|
let mut buf = [0u8; 256];
|
||||||
loop {
|
loop {
|
||||||
defmt::info!("Current time UART A: {}", Instant::now().as_secs());
|
defmt::info!("Current time UART A: {}", Instant::now().as_secs());
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ impl Button {
|
|||||||
irq_cfg: InterruptConfig,
|
irq_cfg: InterruptConfig,
|
||||||
) {
|
) {
|
||||||
self.0.configure_edge_interrupt(edge_type);
|
self.0.configure_edge_interrupt(edge_type);
|
||||||
self.0.enable_interrupt(irq_cfg);
|
self.0.enable_interrupt(irq_cfg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures an IRQ on level.
|
/// Configures an IRQ on level.
|
||||||
@@ -46,7 +46,7 @@ impl Button {
|
|||||||
irq_cfg: InterruptConfig,
|
irq_cfg: InterruptConfig,
|
||||||
) {
|
) {
|
||||||
self.0.configure_level_interrupt(level);
|
self.0.configure_level_interrupt(level);
|
||||||
self.0.enable_interrupt(irq_cfg);
|
self.0.enable_interrupt(irq_cfg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures a filter on the button. This can be useful for debouncing the switch.
|
/// Configures a filter on the button. This can be useful for debouncing the switch.
|
||||||
|
|||||||
@@ -270,37 +270,11 @@ fn boot_app(app_sel: AppSel, cp: &cortex_m::Peripherals) -> ! {
|
|||||||
cortex_m::asm::isb();
|
cortex_m::asm::isb();
|
||||||
unsafe {
|
unsafe {
|
||||||
if app_sel == AppSel::A {
|
if app_sel == AppSel::A {
|
||||||
cp.SCB.vtor.write(APP_A_START_ADDR);
|
cortex_m::asm::bootload(APP_A_START_ADDR as *const u32);
|
||||||
} else {
|
} else {
|
||||||
cp.SCB.vtor.write(APP_B_START_ADDR);
|
cortex_m::asm::bootload(APP_B_START_ADDR as *const u32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cortex_m::asm::dsb();
|
|
||||||
cortex_m::asm::isb();
|
|
||||||
vector_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vector_reset() -> ! {
|
|
||||||
unsafe {
|
|
||||||
// Set R0 to VTOR address (0xE000ED08)
|
|
||||||
let vtor_address: u32 = 0xE000ED08;
|
|
||||||
|
|
||||||
// Load VTOR
|
|
||||||
let vtor: u32 = *(vtor_address as *const u32);
|
|
||||||
|
|
||||||
// Load initial MSP value
|
|
||||||
let initial_msp: u32 = *(vtor as *const u32);
|
|
||||||
|
|
||||||
// Set SP value (assume MSP is selected)
|
|
||||||
core::arch::asm!("mov sp, {0}", in(reg) initial_msp);
|
|
||||||
|
|
||||||
// Load reset vector
|
|
||||||
let reset_vector: u32 = *((vtor + 4) as *const u32);
|
|
||||||
|
|
||||||
// Branch to reset handler
|
|
||||||
core::arch::asm!("bx {0}", in(reg) reset_vector);
|
|
||||||
}
|
|
||||||
unreachable!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_edac(syscfg: &mut pac::Sysconfig) {
|
fn setup_edac(syscfg: &mut pac::Sysconfig) {
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added missing `AnyPin` trait impl for Multi HW CS pins.
|
- Added missing `AnyPin` trait impl for Multi HW CS pins.
|
||||||
- Expose inner `Input` pin for `InputPinAsync`.
|
- Expose inner `Input` pin for `InputPinAsync`.
|
||||||
- Bugfix for UART clock calculation with 8x baud mode.
|
- Bugfix for UART clock calculation with 8x baud mode.
|
||||||
|
- Possible bugfix for Asynch GPIO where the interrupt handler could become stuck in a loop.
|
||||||
|
- Robustness improvements for the Asynch GPIO driver code.
|
||||||
|
|
||||||
## [v0.2.0] 2025-09-03
|
## [v0.2.0] 2025-09-03
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fugit = "0.3"
|
|||||||
defmt = { version = "1", optional = true }
|
defmt = { version = "1", optional = true }
|
||||||
va108xx = { version = "0.6", path = "../va108xx/va108xx", default-features = false, optional = true }
|
va108xx = { version = "0.6", path = "../va108xx/va108xx", default-features = false, optional = true }
|
||||||
va416xx = { version = "0.5", path = "../va416xx/va416xx", default-features = false, optional = true }
|
va416xx = { version = "0.5", path = "../va416xx/va416xx", default-features = false, optional = true }
|
||||||
embassy-sync = "0.7"
|
embassy-sync = "0.8"
|
||||||
embassy-time-driver = "0.2"
|
embassy-time-driver = "0.2"
|
||||||
embassy-time-queue-utils = "0.3"
|
embassy-time-queue-utils = "0.3"
|
||||||
once_cell = { version = "1", default-features = false, features = [
|
once_cell = { version = "1", default-features = false, features = [
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
|
|||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
use super::ll::PortDoesNotSupportInterrupts;
|
use super::ll::PortDoesNotSupportInterrupts;
|
||||||
|
|
||||||
#[cfg(feature = "vor1x")]
|
|
||||||
use va108xx as pac;
|
|
||||||
|
|
||||||
pub use super::ll::InterruptEdge;
|
pub use super::ll::InterruptEdge;
|
||||||
use super::{
|
use super::{
|
||||||
Input, Port,
|
Input, Port,
|
||||||
@@ -118,34 +115,35 @@ pub fn on_interrupt_for_async_gpio_for_port(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
|
fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
|
||||||
let gpio = unsafe { port.steal_gpio() };
|
let mut gpio = unsafe { port.steal_regs() };
|
||||||
|
|
||||||
let irq_enb = gpio.read_irq_enable();
|
let irq_enb = gpio.read_irq_enable();
|
||||||
let edge_status = gpio.read_edge_status();
|
let edge_status = gpio.read_edge_status();
|
||||||
let irq_status = gpio.read_irq_status();
|
|
||||||
// Depending on silicon/configuration, edge-triggered events can be reflected in EDGE_STATUS,
|
|
||||||
// IRQ_STATUS, or both. Accept either source for pending detection.
|
|
||||||
let pending = irq_enb & (edge_status | irq_status);
|
|
||||||
let (wakers, edge_detection) = pin_group_to_waker_and_edge_detection_group(port);
|
let (wakers, edge_detection) = pin_group_to_waker_and_edge_detection_group(port);
|
||||||
|
|
||||||
on_interrupt_for_port(pending, wakers, edge_detection);
|
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn on_interrupt_for_port(
|
fn on_interrupt_for_port(
|
||||||
mut pending: u32,
|
mut irq_enb: u32,
|
||||||
|
edge_status: u32,
|
||||||
wakers: &'static [AtomicWaker],
|
wakers: &'static [AtomicWaker],
|
||||||
edge_detection: &'static [AtomicBool],
|
edge_detection: &'static [AtomicBool],
|
||||||
) {
|
) {
|
||||||
while pending != 0 {
|
// Check all enabled interrupts.
|
||||||
let bit_pos = pending.trailing_zeros() as usize;
|
while irq_enb != 0 {
|
||||||
|
// For all enabled interrupts, check whether the corresponding edge detection has
|
||||||
|
// triggered.
|
||||||
|
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||||
let bit_mask = 1 << bit_pos;
|
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);
|
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
wakers[bit_pos].wake();
|
||||||
// Clear the processed bit in our local bitmap.
|
}
|
||||||
pending &= !bit_mask;
|
// Clear the processed bit
|
||||||
|
irq_enb &= !bit_mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,13 +161,12 @@ pub struct InputPinFuture {
|
|||||||
impl InputPinFuture {
|
impl InputPinFuture {
|
||||||
/// Create a new input pin future from mutable reference to an [Input] pin.
|
/// Create a new input pin future from mutable reference to an [Input] pin.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
pub fn new_with_input_pin(pin: &mut Input, irq: pac::Interrupt, edge: InterruptEdge) -> Self {
|
pub fn new_with_input_pin(pin: &mut Input, edge: InterruptEdge) -> Self {
|
||||||
let (waker_group, edge_detection_group) =
|
let (waker_group, edge_detection_group) =
|
||||||
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||||
edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
|
edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
pin.configure_edge_interrupt(edge);
|
pin.configure_edge_interrupt(edge);
|
||||||
#[cfg(feature = "vor1x")]
|
pin.enable_interrupt_gpio_only();
|
||||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
|
||||||
Self {
|
Self {
|
||||||
id: pin.id(),
|
id: pin.id(),
|
||||||
waker_group,
|
waker_group,
|
||||||
@@ -186,7 +183,7 @@ impl InputPinFuture {
|
|||||||
let (waker_group, edge_detection_group) =
|
let (waker_group, edge_detection_group) =
|
||||||
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||||
pin.configure_edge_interrupt(edge);
|
pin.configure_edge_interrupt(edge);
|
||||||
pin.enable_interrupt(true)?;
|
pin.enable_interrupt_gpio_only();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
id: pin.id(),
|
id: pin.id(),
|
||||||
waker_group,
|
waker_group,
|
||||||
@@ -223,8 +220,6 @@ impl Future for InputPinFuture {
|
|||||||
/// Input pin which has additional asynchronous support.
|
/// Input pin which has additional asynchronous support.
|
||||||
pub struct InputPinAsync {
|
pub struct InputPinAsync {
|
||||||
pin: Input,
|
pin: Input,
|
||||||
#[cfg(feature = "vor1x")]
|
|
||||||
irq: va108xx::Interrupt,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputPinAsync {
|
impl InputPinAsync {
|
||||||
@@ -235,8 +230,10 @@ impl InputPinAsync {
|
|||||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||||
/// for the asynchronous functionality to work.
|
/// for the asynchronous functionality to work.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
pub fn new(pin: Input, irq: va108xx::Interrupt) -> Self {
|
pub fn new(mut pin: Input, irq_config: InterruptConfig) -> Self {
|
||||||
Self { pin, irq }
|
// Do not enable GPIO interrupt bit yet.
|
||||||
|
pin.enable_interrupt(irq_config, false);
|
||||||
|
Self { pin }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
|
/// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
|
||||||
@@ -246,10 +243,12 @@ impl InputPinAsync {
|
|||||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||||
/// for the asynchronous functionality to work.
|
/// for the asynchronous functionality to work.
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
pub fn new(pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
|
pub fn new(mut pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
|
||||||
if pin.id().port() == Port::G {
|
if pin.id().port() == Port::G {
|
||||||
return Err(PortDoesNotSupportInterrupts);
|
return Err(PortDoesNotSupportInterrupts);
|
||||||
}
|
}
|
||||||
|
// Do not enable GPIO interrupt bit yet.
|
||||||
|
pin.enable_interrupt(true, false)?;
|
||||||
Ok(Self { pin })
|
Ok(Self { pin })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,8 +258,7 @@ impl InputPinAsync {
|
|||||||
pub async fn wait_for_high(&mut self) {
|
pub async fn wait_for_high(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
let fut =
|
let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh);
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
let fut =
|
let fut =
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
|
||||||
@@ -300,8 +298,7 @@ impl InputPinAsync {
|
|||||||
pub async fn wait_for_low(&mut self) {
|
pub async fn wait_for_low(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
let fut =
|
let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow);
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
let fut =
|
let fut =
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
|
||||||
@@ -315,7 +312,7 @@ impl InputPinAsync {
|
|||||||
pub async fn wait_for_falling_edge(&mut self) {
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).await;
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow)
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -326,14 +323,14 @@ impl InputPinAsync {
|
|||||||
pub async fn wait_for_rising_edge(&mut self) {
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
pub async fn wait_for_any_edge(&mut self) {
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
// Unwrap okay, checked pin in constructor.
|
// Unwrap okay, checked pin in constructor.
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges).await;
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
|
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|||||||
@@ -384,32 +384,53 @@ impl LowLevelGpio {
|
|||||||
self.gpio.write_tog_out(self.mask_32());
|
self.gpio.write_tog_out(self.mask_32());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "vor1x")]
|
/// Only enabled GPIO peripheral interrupt bit without enabling the interrupt in NVIC
|
||||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
/// or routing it in the IRQSEL peripheral for VA108xx devices.
|
||||||
if irq_cfg.route {
|
#[inline]
|
||||||
self.configure_irqsel(irq_cfg.id);
|
pub fn enable_interrupt_gpio_only(&mut self) {
|
||||||
}
|
|
||||||
if irq_cfg.enable_in_nvic {
|
|
||||||
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
|
||||||
}
|
|
||||||
self.gpio.modify_irq_enable(|mut value| {
|
self.gpio.modify_irq_enable(|mut value| {
|
||||||
value |= 1 << self.id.offset;
|
value |= 1 << self.id.offset;
|
||||||
value
|
value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Depending on the configuration parameters, does the following:
|
||||||
|
///
|
||||||
|
/// - Routes the interrupt in the IRQSEL peripheral
|
||||||
|
/// - Enables the interrupt in the NVIC,
|
||||||
|
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
||||||
|
#[cfg(feature = "vor1x")]
|
||||||
|
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) {
|
||||||
|
if irq_cfg.route {
|
||||||
|
self.configure_irqsel(irq_cfg.id);
|
||||||
|
}
|
||||||
|
if irq_cfg.enable_in_nvic {
|
||||||
|
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
||||||
|
}
|
||||||
|
if gpio {
|
||||||
|
self.enable_interrupt_gpio_only();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Depending on the configuration parameters, does the following:
|
||||||
|
///
|
||||||
|
/// - Enables the interrupt in the NVIC,
|
||||||
|
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
pub fn enable_interrupt(
|
pub fn enable_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
enable_in_nvic: bool,
|
enable_in_nvic: bool,
|
||||||
|
gpio: bool,
|
||||||
) -> Result<(), PortDoesNotSupportInterrupts> {
|
) -> Result<(), PortDoesNotSupportInterrupts> {
|
||||||
|
if self.id().port() == Port::G {
|
||||||
|
return Err(PortDoesNotSupportInterrupts);
|
||||||
|
}
|
||||||
if enable_in_nvic {
|
if enable_in_nvic {
|
||||||
unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
|
unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
|
||||||
}
|
}
|
||||||
self.gpio.modify_irq_enable(|mut value| {
|
if gpio {
|
||||||
value |= 1 << self.id.offset;
|
self.enable_interrupt_gpio_only();
|
||||||
value
|
}
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,19 +132,34 @@ impl Input {
|
|||||||
self.0.id()
|
self.0.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "vor1x")]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
pub fn enable_interrupt_gpio_only(&mut self) {
|
||||||
self.0.enable_interrupt(irq_cfg);
|
self.0.enable_interrupt_gpio_only();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Depending on the configuration parameters, does the following:
|
||||||
|
///
|
||||||
|
/// - Routes the interrupt in the IRQSEL peripheral
|
||||||
|
/// - Enables the interrupt in the NVIC,
|
||||||
|
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
||||||
|
#[cfg(feature = "vor1x")]
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) {
|
||||||
|
self.0.enable_interrupt(irq_cfg, gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Depending on the configuration parameters, does the following:
|
||||||
|
///
|
||||||
|
/// - Enables the interrupt in the NVIC,
|
||||||
|
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
||||||
#[cfg(feature = "vor4x")]
|
#[cfg(feature = "vor4x")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enable_interrupt(
|
pub fn enable_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
enable_in_nvic: bool,
|
enable_in_nvic: bool,
|
||||||
|
gpio: bool,
|
||||||
) -> Result<(), ll::PortDoesNotSupportInterrupts> {
|
) -> Result<(), ll::PortDoesNotSupportInterrupts> {
|
||||||
self.0.enable_interrupt(enable_in_nvic)
|
self.0.enable_interrupt(enable_in_nvic, gpio)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ pub struct Gpio {
|
|||||||
/// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago.
|
/// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago.
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
irq_status: u32,
|
irq_status: u32,
|
||||||
#[mmio(PureRead)]
|
// Reading this register clears it.
|
||||||
|
#[mmio(Read)]
|
||||||
edge_status: u32,
|
edge_status: u32,
|
||||||
|
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ impl Port {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Circumvents ownership and safety guarantees by the HAL.
|
/// Circumvents ownership and safety guarantees by the HAL.
|
||||||
pub unsafe fn steal_gpio(&self) -> gpio::regs::MmioGpio<'static> {
|
pub unsafe fn steal_regs(&self) -> gpio::regs::MmioGpio<'static> {
|
||||||
gpio::regs::Gpio::new_mmio(*self)
|
gpio::regs::Gpio::new_mmio(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+210
-133
@@ -1,4 +1,4 @@
|
|||||||
use core::{cell::RefCell, convert::Infallible};
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use arbitrary_int::u5;
|
use arbitrary_int::u5;
|
||||||
use critical_section::Mutex;
|
use critical_section::Mutex;
|
||||||
@@ -8,7 +8,10 @@ use raw_slice::{RawBufSlice, RawBufSliceMut};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
shared::{FifoClear, TriggerLevel},
|
shared::{FifoClear, TriggerLevel},
|
||||||
spi::regs::{Data, InterruptClear, InterruptControl, InterruptStatus},
|
spi::{
|
||||||
|
FIFO_DEPTH,
|
||||||
|
regs::{Data, InterruptClear, InterruptControl},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "vor1x")]
|
#[cfg(feature = "vor1x")]
|
||||||
@@ -23,6 +26,17 @@ static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; NUM_SPIS] =
|
|||||||
// critical section.
|
// critical section.
|
||||||
static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPIS];
|
static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPIS];
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("SPI RX FIFO overrun")]
|
||||||
|
pub struct RxOverrunError;
|
||||||
|
|
||||||
|
impl embedded_hal_async::spi::Error for RxOverrunError {
|
||||||
|
fn kind(&self) -> embedded_hal::spi::ErrorKind {
|
||||||
|
embedded_hal::spi::ErrorKind::Overrun
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given
|
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given
|
||||||
/// SPI peripheral.
|
/// SPI peripheral.
|
||||||
///
|
///
|
||||||
@@ -30,18 +44,24 @@ static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPI
|
|||||||
/// the given SPI bank.
|
/// the given SPI bank.
|
||||||
pub fn on_interrupt(peripheral: super::Bank) {
|
pub fn on_interrupt(peripheral: super::Bank) {
|
||||||
let mut spi = unsafe { peripheral.steal_regs() };
|
let mut spi = unsafe { peripheral.steal_regs() };
|
||||||
let idx = peripheral as usize;
|
let index = peripheral as usize;
|
||||||
let interrupt_enabled = spi.read_interrupt_control();
|
let enabled_irqs = spi.read_interrupt_control();
|
||||||
let isr = spi.read_interrupt_status();
|
let interrupt_status = spi.read_interrupt_status();
|
||||||
// IRQ is not related.
|
spi.write_interrupt_clear(InterruptClear::ALL);
|
||||||
if interrupt_enabled.raw_value() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Prevent spurious interrupts from messing with out logic here.
|
// Prevent spurious interrupts from messing with out logic here.
|
||||||
spi.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
spi.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
||||||
spi.write_interrupt_clear(InterruptClear::ALL);
|
// IRQ is not related.
|
||||||
|
if enabled_irqs.raw_value() == 0 {
|
||||||
|
reset_trigger_levels(&mut spi);
|
||||||
|
spi.write_fifo_clear(FifoClear::ALL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if interrupt_status.rx_overrun() {
|
||||||
|
// Not sure how to otherwise handle this cleanly..
|
||||||
|
return handle_rx_overrun(&mut spi, index);
|
||||||
|
}
|
||||||
let mut context = critical_section::with(|cs| {
|
let mut context = critical_section::with(|cs| {
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
|
||||||
*context_ref.borrow()
|
*context_ref.borrow()
|
||||||
});
|
});
|
||||||
// No transfer active.
|
// No transfer active.
|
||||||
@@ -50,30 +70,47 @@ pub fn on_interrupt(peripheral: super::Bank) {
|
|||||||
}
|
}
|
||||||
let transfer_type = context.transfer_type.unwrap();
|
let transfer_type = context.transfer_type.unwrap();
|
||||||
match transfer_type {
|
match transfer_type {
|
||||||
TransferType::Read => on_interrupt_read(idx, &mut context, &mut spi, isr),
|
TransferType::Read => on_interrupt_read(index, &mut context, &mut spi, enabled_irqs),
|
||||||
TransferType::Write => on_interrupt_write(idx, &mut context, &mut spi, isr),
|
TransferType::Write => on_interrupt_write(index, &mut context, &mut spi, enabled_irqs),
|
||||||
TransferType::Transfer => on_interrupt_transfer(idx, &mut context, &mut spi, isr),
|
TransferType::Transfer => {
|
||||||
|
on_interrupt_transfer(index, &mut context, &mut spi, enabled_irqs)
|
||||||
|
}
|
||||||
TransferType::TransferInPlace => {
|
TransferType::TransferInPlace => {
|
||||||
on_interrupt_transfer_in_place(idx, &mut context, &mut spi, isr)
|
on_interrupt_transfer_in_place(index, &mut context, &mut spi, enabled_irqs)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_rx_overrun(spi: &mut super::regs::MmioSpi<'static>, idx: usize) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
|
context_ref.borrow_mut().rx_overrun = true;
|
||||||
|
});
|
||||||
|
// Clean up, restore clean state.
|
||||||
|
reset_trigger_levels(spi);
|
||||||
|
spi.write_fifo_clear(FifoClear::ALL);
|
||||||
|
// Interrupts were already disabled and cleared.
|
||||||
|
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
WAKERS[idx].wake();
|
||||||
|
}
|
||||||
|
|
||||||
fn on_interrupt_read(
|
fn on_interrupt_read(
|
||||||
idx: usize,
|
idx: usize,
|
||||||
context: &mut TransferContext,
|
context: &mut TransferContext,
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
spi: &mut super::regs::MmioSpi<'static>,
|
||||||
isr: InterruptStatus,
|
enabled_irqs: InterruptControl,
|
||||||
) {
|
) {
|
||||||
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||||
let transfer_len = read_slice.len();
|
let transfer_len = read_slice.len();
|
||||||
|
|
||||||
// Read data from RX FIFO first.
|
// Read data from RX FIFO first.
|
||||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
while spi.read_status().rx_not_empty() {
|
||||||
(0..read_len).for_each(|_| {
|
let data = spi.read_data();
|
||||||
read_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
if context.rx_progress < transfer_len {
|
||||||
context.rx_progress += 1;
|
read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
|
||||||
});
|
context.rx_progress += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The FIFO still needs to be pumped.
|
// The FIFO still needs to be pumped.
|
||||||
while context.tx_progress < read_slice.len() && spi.read_status().tx_not_full() {
|
while context.tx_progress < read_slice.len() && spi.read_status().tx_not_full() {
|
||||||
@@ -81,24 +118,25 @@ fn on_interrupt_read(
|
|||||||
context.tx_progress += 1;
|
context.tx_progress += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
isr_finish_handler(idx, spi, context, transfer_len)
|
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt_write(
|
fn on_interrupt_write(
|
||||||
idx: usize,
|
idx: usize,
|
||||||
context: &mut TransferContext,
|
context: &mut TransferContext,
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
spi: &mut super::regs::MmioSpi<'static>,
|
||||||
isr: InterruptStatus,
|
enabled_irqs: InterruptControl,
|
||||||
) {
|
) {
|
||||||
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
||||||
let transfer_len = write_slice.len();
|
let transfer_len = write_slice.len();
|
||||||
|
|
||||||
// Read data from RX FIFO first.
|
// Read data from RX FIFO first.
|
||||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
while spi.read_status().rx_not_empty() {
|
||||||
(0..read_len).for_each(|_| {
|
|
||||||
spi.read_data();
|
spi.read_data();
|
||||||
context.rx_progress += 1;
|
if context.rx_progress < transfer_len {
|
||||||
});
|
context.rx_progress += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Data still needs to be sent
|
// Data still needs to be sent
|
||||||
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
||||||
@@ -108,14 +146,14 @@ fn on_interrupt_write(
|
|||||||
context.tx_progress += 1;
|
context.tx_progress += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
isr_finish_handler(idx, spi, context, transfer_len)
|
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt_transfer(
|
fn on_interrupt_transfer(
|
||||||
idx: usize,
|
idx: usize,
|
||||||
context: &mut TransferContext,
|
context: &mut TransferContext,
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
spi: &mut super::regs::MmioSpi<'static>,
|
||||||
isr: InterruptStatus,
|
enabled_irqs: InterruptControl,
|
||||||
) {
|
) {
|
||||||
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||||
let read_len = read_slice.len();
|
let read_len = read_slice.len();
|
||||||
@@ -125,36 +163,31 @@ fn on_interrupt_transfer(
|
|||||||
|
|
||||||
// Send data first to avoid overwriting data that still needs to be sent.
|
// Send data first to avoid overwriting data that still needs to be sent.
|
||||||
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
||||||
if context.tx_progress < write_len {
|
spi.write_data(Data::new_with_raw_value(
|
||||||
spi.write_data(Data::new_with_raw_value(
|
write_slice.get(context.tx_progress).copied().unwrap_or(0) as u32,
|
||||||
write_slice[context.tx_progress] as u32,
|
));
|
||||||
));
|
// Always increment this.
|
||||||
} else {
|
|
||||||
// Dummy write.
|
|
||||||
spi.write_data(Data::new_with_raw_value(0));
|
|
||||||
}
|
|
||||||
context.tx_progress += 1;
|
context.tx_progress += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read data from RX FIFO.
|
// Read data from RX FIFO.
|
||||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
while spi.read_status().rx_not_empty() {
|
||||||
(0..read_len).for_each(|_| {
|
let data = spi.read_data();
|
||||||
if context.rx_progress < read_len {
|
if context.rx_progress < read_len {
|
||||||
read_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
|
||||||
} else {
|
|
||||||
spi.read_data();
|
|
||||||
}
|
}
|
||||||
|
// Always increment this.
|
||||||
context.rx_progress += 1;
|
context.rx_progress += 1;
|
||||||
});
|
}
|
||||||
|
|
||||||
isr_finish_handler(idx, spi, context, transfer_len)
|
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt_transfer_in_place(
|
fn on_interrupt_transfer_in_place(
|
||||||
idx: usize,
|
idx: usize,
|
||||||
context: &mut TransferContext,
|
context: &mut TransferContext,
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
spi: &mut super::regs::MmioSpi<'static>,
|
||||||
isr: InterruptStatus,
|
enabled_irqs: InterruptControl,
|
||||||
) {
|
) {
|
||||||
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||||
let transfer_len = transfer_slice.len();
|
let transfer_len = transfer_slice.len();
|
||||||
@@ -166,29 +199,15 @@ fn on_interrupt_transfer_in_place(
|
|||||||
context.tx_progress += 1;
|
context.tx_progress += 1;
|
||||||
}
|
}
|
||||||
// Read data from RX FIFO.
|
// Read data from RX FIFO.
|
||||||
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
while spi.read_status().rx_not_empty() {
|
||||||
(0..read_len).for_each(|_| {
|
let data = spi.read_data();
|
||||||
transfer_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
|
if context.rx_progress < transfer_len {
|
||||||
context.rx_progress += 1;
|
transfer_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
|
||||||
});
|
context.rx_progress += 1;
|
||||||
|
}
|
||||||
isr_finish_handler(idx, spi, context, transfer_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_read_len(
|
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
isr: InterruptStatus,
|
|
||||||
total_read_len: usize,
|
|
||||||
rx_progress: usize,
|
|
||||||
) -> usize {
|
|
||||||
if isr.rx() {
|
|
||||||
core::cmp::min(super::FIFO_DEPTH, total_read_len - rx_progress)
|
|
||||||
} else if spi.read_status().rx_not_empty() {
|
|
||||||
let fifo_level = spi.read_state().rx_fifo();
|
|
||||||
core::cmp::min(total_read_len - rx_progress, fifo_level as usize)
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
|
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
|
||||||
@@ -198,49 +217,73 @@ fn isr_finish_handler(
|
|||||||
spi: &mut super::regs::MmioSpi<'static>,
|
spi: &mut super::regs::MmioSpi<'static>,
|
||||||
context: &mut TransferContext,
|
context: &mut TransferContext,
|
||||||
transfer_len: usize,
|
transfer_len: usize,
|
||||||
|
enabled: InterruptControl,
|
||||||
) {
|
) {
|
||||||
// Transfer finish condition.
|
// Transfer finish condition.
|
||||||
if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
|
if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
|
||||||
finish_transfer(idx, context, spi);
|
finish_transfer(spi, idx, context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unfinished_transfer(spi, transfer_len, context.rx_progress);
|
|
||||||
|
|
||||||
// If the transfer is done, the context structure was already written back.
|
// If the transfer is done, the context structure was already written back.
|
||||||
// Write back updated context structure.
|
// Write back updated context structure.
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
*context_ref.borrow_mut() = *context;
|
*context_ref.borrow_mut() = *context;
|
||||||
});
|
});
|
||||||
|
unfinished_transfer(spi, transfer_len, context, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_transfer(
|
fn finish_transfer(
|
||||||
|
spi: &mut super::regs::MmioSpi<'static>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
context: &mut TransferContext,
|
context: &mut TransferContext,
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
|
||||||
) {
|
) {
|
||||||
// Write back updated context structure.
|
// Write back updated context structure.
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
*context_ref.borrow_mut() = *context;
|
*context_ref.borrow_mut() = *context;
|
||||||
});
|
});
|
||||||
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
|
// Clean up, restore clean state.
|
||||||
spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x00)));
|
reset_trigger_levels(spi);
|
||||||
|
spi.write_fifo_clear(FifoClear::ALL);
|
||||||
// Interrupts were already disabled and cleared.
|
// Interrupts were already disabled and cleared.
|
||||||
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
WAKERS[idx].wake();
|
WAKERS[idx].wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn unfinished_transfer(
|
fn unfinished_transfer(
|
||||||
spi: &mut super::regs::MmioSpi<'static>,
|
spi: &mut super::regs::MmioSpi<'static>,
|
||||||
transfer_len: usize,
|
transfer_len: usize,
|
||||||
rx_progress: usize,
|
context: &TransferContext,
|
||||||
|
enabled_irqs: InterruptControl,
|
||||||
) {
|
) {
|
||||||
let new_trig_level = core::cmp::min(super::FIFO_DEPTH, transfer_len - rx_progress);
|
// Take 8 as a conservative value to make sure that the FIFO does not overflow even if there
|
||||||
|
// is a significant delay between the interrupt being triggered and the handler being executed.
|
||||||
|
let new_trig_level = core::cmp::min(8, transfer_len - context.rx_progress);
|
||||||
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8)));
|
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8)));
|
||||||
|
|
||||||
|
// If TX was already enabled and the transfer is finished, stop enabling it. Otherwise, we can
|
||||||
|
// become stuck in an interrupt loop. In any other case, enable it. I am not fully sure
|
||||||
|
// why this is necessary and why we can not stop interrupts as soon as we have the full
|
||||||
|
// TX progress, but tests with ADCs have shown that not doing this causes timeouts.
|
||||||
|
let enable_tx = !(enabled_irqs.tx() && context.tx_progress == transfer_len);
|
||||||
|
|
||||||
// Re-enable interrupts with the new RX FIFO trigger level.
|
// Re-enable interrupts with the new RX FIFO trigger level.
|
||||||
spi.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
spi.write_interrupt_control(
|
||||||
|
InterruptControl::builder()
|
||||||
|
.with_tx(enable_tx)
|
||||||
|
.with_rx(true)
|
||||||
|
.with_rx_timeout(true)
|
||||||
|
.with_rx_overrun(true)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn reset_trigger_levels(spi: &mut super::regs::MmioSpi<'static>) {
|
||||||
|
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
|
||||||
|
spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x00)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -259,6 +302,7 @@ pub struct TransferContext {
|
|||||||
rx_progress: usize,
|
rx_progress: usize,
|
||||||
tx_slice: RawBufSlice,
|
tx_slice: RawBufSlice,
|
||||||
rx_slice: RawBufSliceMut,
|
rx_slice: RawBufSliceMut,
|
||||||
|
rx_overrun: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
@@ -270,6 +314,7 @@ impl TransferContext {
|
|||||||
rx_progress: 0,
|
rx_progress: 0,
|
||||||
tx_slice: RawBufSlice::new_nulled(),
|
tx_slice: RawBufSlice::new_nulled(),
|
||||||
rx_slice: RawBufSliceMut::new_nulled(),
|
rx_slice: RawBufSliceMut::new_nulled(),
|
||||||
|
rx_overrun: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,33 +330,30 @@ impl<'spi> SpiFuture<'spi> {
|
|||||||
if words.is_empty() {
|
if words.is_empty() {
|
||||||
panic!("words length unexpectedly 0");
|
panic!("words length unexpectedly 0");
|
||||||
}
|
}
|
||||||
let idx = bank as usize;
|
Self::generic_init_transfer(spi, bank);
|
||||||
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
spi.regs
|
let write_index = core::cmp::min(super::FIFO_DEPTH, words.len());
|
||||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
|
||||||
spi.regs.write_fifo_clear(FifoClear::ALL);
|
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
|
|
||||||
let write_idx = core::cmp::min(super::FIFO_DEPTH, words.len());
|
|
||||||
// Send dummy bytes.
|
// Send dummy bytes.
|
||||||
(0..write_idx).for_each(|_| {
|
(0..write_index).for_each(|_| {
|
||||||
spi.regs.write_data(Data::new_with_raw_value(0));
|
spi.regs.write_data(Data::new_with_raw_value(0));
|
||||||
});
|
});
|
||||||
|
|
||||||
Self::set_triggers(spi, write_idx, words.len());
|
Self::set_triggers(spi, write_index, words.len());
|
||||||
|
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs);
|
||||||
let mut context = context_ref.borrow_mut();
|
let mut context = context_ref.borrow_mut();
|
||||||
context.transfer_type = Some(TransferType::Read);
|
context.transfer_type = Some(TransferType::Read);
|
||||||
unsafe {
|
unsafe {
|
||||||
context.rx_slice.set(words);
|
context.rx_slice.set(words);
|
||||||
}
|
}
|
||||||
context.tx_slice.set_null();
|
context.tx_slice.set_null();
|
||||||
context.tx_progress = write_idx;
|
context.tx_progress = write_index;
|
||||||
context.rx_progress = 0;
|
context.rx_progress = 0;
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||||
spi.regs
|
spi.regs.write_interrupt_control(
|
||||||
.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
|
||||||
|
);
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
@@ -325,20 +367,22 @@ impl<'spi> SpiFuture<'spi> {
|
|||||||
if words.is_empty() {
|
if words.is_empty() {
|
||||||
panic!("words length unexpectedly 0");
|
panic!("words length unexpectedly 0");
|
||||||
}
|
}
|
||||||
let (idx, write_idx) = Self::generic_init_transfer(spi, bank, words);
|
let index = bank as usize;
|
||||||
|
let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
|
||||||
let mut context = context_ref.borrow_mut();
|
let mut context = context_ref.borrow_mut();
|
||||||
context.transfer_type = Some(TransferType::Write);
|
context.transfer_type = Some(TransferType::Write);
|
||||||
unsafe {
|
unsafe {
|
||||||
context.tx_slice.set(words);
|
context.tx_slice.set(words);
|
||||||
}
|
}
|
||||||
context.rx_slice.set_null();
|
context.rx_slice.set_null();
|
||||||
context.tx_progress = write_idx;
|
context.tx_progress = write_index;
|
||||||
context.rx_progress = 0;
|
context.rx_progress = 0;
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||||
spi.regs
|
spi.regs.write_interrupt_control(
|
||||||
.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
|
||||||
|
);
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
@@ -350,31 +394,44 @@ impl<'spi> SpiFuture<'spi> {
|
|||||||
|
|
||||||
fn new_for_transfer(
|
fn new_for_transfer(
|
||||||
spi: &'spi mut super::Spi<u8>,
|
spi: &'spi mut super::Spi<u8>,
|
||||||
spi_id: super::Bank,
|
bank: super::Bank,
|
||||||
read: &mut [u8],
|
read: &mut [u8],
|
||||||
write: &[u8],
|
write: &[u8],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if read.is_empty() || write.is_empty() {
|
if read.is_empty() || write.is_empty() {
|
||||||
panic!("read or write buffer unexpectedly empty");
|
panic!("read or write buffer unexpectedly empty");
|
||||||
}
|
}
|
||||||
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, write);
|
let index = bank as usize;
|
||||||
|
let full_write_len = core::cmp::max(read.len(), write.len());
|
||||||
|
let fifo_prefill = core::cmp::min(super::FIFO_DEPTH, full_write_len);
|
||||||
|
|
||||||
|
Self::generic_init_transfer(spi, bank);
|
||||||
|
|
||||||
|
for write_index in 0..fifo_prefill {
|
||||||
|
let value = write.get(write_index).copied().unwrap_or(0);
|
||||||
|
spi.regs.write_data(Data::new_with_raw_value(value as u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::set_triggers(spi, fifo_prefill, full_write_len);
|
||||||
|
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
|
||||||
let mut context = context_ref.borrow_mut();
|
let mut context = context_ref.borrow_mut();
|
||||||
context.transfer_type = Some(TransferType::Transfer);
|
context.transfer_type = Some(TransferType::Transfer);
|
||||||
unsafe {
|
unsafe {
|
||||||
context.tx_slice.set(write);
|
context.tx_slice.set(write);
|
||||||
context.rx_slice.set(read);
|
context.rx_slice.set(read);
|
||||||
}
|
}
|
||||||
context.tx_progress = write_idx;
|
context.tx_progress = fifo_prefill;
|
||||||
context.rx_progress = 0;
|
context.rx_progress = 0;
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||||
spi.regs
|
spi.regs.write_interrupt_control(
|
||||||
.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
InterruptControl::ENABLE_ALL.with_tx(fifo_prefill > FIFO_DEPTH),
|
||||||
|
);
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
bank: spi_id,
|
bank,
|
||||||
spi,
|
spi,
|
||||||
finished_regularly: core::cell::Cell::new(false),
|
finished_regularly: core::cell::Cell::new(false),
|
||||||
}
|
}
|
||||||
@@ -382,15 +439,15 @@ impl<'spi> SpiFuture<'spi> {
|
|||||||
|
|
||||||
fn new_for_transfer_in_place(
|
fn new_for_transfer_in_place(
|
||||||
spi: &'spi mut super::Spi<u8>,
|
spi: &'spi mut super::Spi<u8>,
|
||||||
spi_id: super::Bank,
|
bank: super::Bank,
|
||||||
words: &mut [u8],
|
words: &mut [u8],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if words.is_empty() {
|
if words.is_empty() {
|
||||||
panic!("read and write buffer unexpectedly empty");
|
panic!("read and write buffer unexpectedly empty");
|
||||||
}
|
}
|
||||||
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
|
let write_idx = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs);
|
||||||
let mut context = context_ref.borrow_mut();
|
let mut context = context_ref.borrow_mut();
|
||||||
context.transfer_type = Some(TransferType::TransferInPlace);
|
context.transfer_type = Some(TransferType::TransferInPlace);
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -400,28 +457,34 @@ impl<'spi> SpiFuture<'spi> {
|
|||||||
context.tx_progress = write_idx;
|
context.tx_progress = write_idx;
|
||||||
context.rx_progress = 0;
|
context.rx_progress = 0;
|
||||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||||
spi.regs
|
spi.regs.write_interrupt_control(
|
||||||
.write_interrupt_control(InterruptControl::ENABLE_ALL);
|
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
|
||||||
|
);
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
bank: spi_id,
|
bank,
|
||||||
spi,
|
spi,
|
||||||
finished_regularly: core::cell::Cell::new(false),
|
finished_regularly: core::cell::Cell::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_init_transfer(
|
fn generic_init_transfer(spi: &mut super::Spi<u8>, bank: super::Bank) {
|
||||||
spi: &mut super::Spi<u8>,
|
|
||||||
bank: super::Bank,
|
|
||||||
write: &[u8],
|
|
||||||
) -> (usize, usize) {
|
|
||||||
let idx = bank as usize;
|
let idx = bank as usize;
|
||||||
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
spi.regs
|
spi.regs
|
||||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
||||||
spi.regs.write_fifo_clear(FifoClear::ALL);
|
spi.regs.write_fifo_clear(FifoClear::ALL);
|
||||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
|
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns amount of bytes written to FIFO.
|
||||||
|
fn generic_init_transfer_write_transfer_in_place(
|
||||||
|
spi: &mut super::Spi<u8>,
|
||||||
|
bank: super::Bank,
|
||||||
|
write: &[u8],
|
||||||
|
) -> usize {
|
||||||
|
Self::generic_init_transfer(spi, bank);
|
||||||
|
|
||||||
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
|
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
|
||||||
(0..write_idx).for_each(|idx| {
|
(0..write_idx).for_each(|idx| {
|
||||||
@@ -430,20 +493,21 @@ impl<'spi> SpiFuture<'spi> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Self::set_triggers(spi, write_idx, write.len());
|
Self::set_triggers(spi, write_idx, write.len());
|
||||||
(idx, write_idx)
|
write_idx
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_triggers(spi: &mut super::Spi<u8>, write_idx: usize, write_len: usize) {
|
fn set_triggers(spi: &mut super::Spi<u8>, fifo_prefill: usize, write_len: usize) {
|
||||||
// This should never fail because it is never larger than the FIFO depth.
|
|
||||||
spi.regs
|
spi.regs
|
||||||
.write_rx_fifo_trigger(TriggerLevel::new(u5::new(write_idx as u8)));
|
.write_rx_fifo_trigger(TriggerLevel::new(u5::new(core::cmp::min(
|
||||||
|
fifo_prefill,
|
||||||
|
FIFO_DEPTH / 2,
|
||||||
|
) as u8)));
|
||||||
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size
|
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size
|
||||||
// is larger than the FIFO depth. I am not sure whether the default value of 1 ensures
|
// is larger than the FIFO depth. Otherwise, set it to 0. Not exactly sure what that does,
|
||||||
// this because the PG says that this interrupt is triggered when the FIFO has less than
|
// but we do not enable interrupts anyway.
|
||||||
// threshold entries.
|
|
||||||
if write_len > super::FIFO_DEPTH {
|
if write_len > super::FIFO_DEPTH {
|
||||||
spi.regs
|
spi.regs
|
||||||
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(2)));
|
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(8)));
|
||||||
} else {
|
} else {
|
||||||
spi.regs
|
spi.regs
|
||||||
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0)));
|
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0)));
|
||||||
@@ -452,7 +516,7 @@ impl<'spi> SpiFuture<'spi> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'spi> Future for SpiFuture<'spi> {
|
impl<'spi> Future for SpiFuture<'spi> {
|
||||||
type Output = ();
|
type Output = Result<(), RxOverrunError>;
|
||||||
|
|
||||||
fn poll(
|
fn poll(
|
||||||
self: core::pin::Pin<&mut Self>,
|
self: core::pin::Pin<&mut Self>,
|
||||||
@@ -460,14 +524,19 @@ impl<'spi> Future for SpiFuture<'spi> {
|
|||||||
) -> core::task::Poll<Self::Output> {
|
) -> core::task::Poll<Self::Output> {
|
||||||
WAKERS[self.bank as usize].register(cx.waker());
|
WAKERS[self.bank as usize].register(cx.waker());
|
||||||
if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||||
critical_section::with(|cs| {
|
let rx_overrun = critical_section::with(|cs| {
|
||||||
let mut ctx = TRANSFER_CONTEXTS[self.bank as usize]
|
let mut ctx = TRANSFER_CONTEXTS[self.bank as usize]
|
||||||
.borrow(cs)
|
.borrow(cs)
|
||||||
.borrow_mut();
|
.borrow_mut();
|
||||||
|
let overrun = ctx.rx_overrun;
|
||||||
*ctx = TransferContext::default();
|
*ctx = TransferContext::default();
|
||||||
|
overrun
|
||||||
});
|
});
|
||||||
self.finished_regularly.set(true);
|
self.finished_regularly.set(true);
|
||||||
return core::task::Poll::Ready(());
|
if rx_overrun {
|
||||||
|
return core::task::Poll::Ready(Err(RxOverrunError));
|
||||||
|
}
|
||||||
|
return core::task::Poll::Ready(Ok(()));
|
||||||
}
|
}
|
||||||
core::task::Poll::Pending
|
core::task::Poll::Pending
|
||||||
}
|
}
|
||||||
@@ -554,28 +623,36 @@ impl SpiAsync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_hal_async::spi::ErrorType for SpiAsync {
|
impl embedded_hal_async::spi::ErrorType for SpiAsync {
|
||||||
type Error = Infallible;
|
type Error = RxOverrunError;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_hal_async::spi::SpiBus for SpiAsync {
|
impl embedded_hal_async::spi::SpiBus for SpiAsync {
|
||||||
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.read(words).unwrap().await;
|
if words.is_empty() {
|
||||||
Ok(())
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.read(words).unwrap().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.write(words).unwrap().await;
|
if words.is_empty() {
|
||||||
Ok(())
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.write(words).unwrap().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.transfer(read, write).unwrap().await;
|
if read.is_empty() && write.is_empty() {
|
||||||
Ok(())
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.transfer(read, write).unwrap().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.transfer_in_place(words).unwrap().await;
|
if words.is_empty() {
|
||||||
Ok(())
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.transfer_in_place(words).unwrap().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
|||||||
Reference in New Issue
Block a user