Async DMA transfer
This commit is contained in:
parent
04f4070634
commit
ebf8205f2d
|
|
@ -38,3 +38,14 @@ ACID_COMPOSE_LOCALE = "cs_CZ.UTF-8"
|
||||||
# This can be substituted with a `-Zbuild-std="core,alloc"` cargo flag.
|
# This can be substituted with a `-Zbuild-std="core,alloc"` cargo flag.
|
||||||
[unstable]
|
[unstable]
|
||||||
build-std = ["alloc", "core"]
|
build-std = ["alloc", "core"]
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
esp-backtrace = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
esp-hal = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
esp-storage = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
esp-alloc = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
esp-println = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
esp-radio = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
esp-rtos = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
esp-bootloader-esp-idf = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
esp-sync = { git = "https://github.com/Limeth/esp-hal.git", rev = "72e22e2de678297da65a185023309a76aef8a5ca" }
|
||||||
|
|
|
||||||
|
|
@ -289,9 +289,7 @@ async fn main(_spawner: Spawner) {
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||||
esp_rtos::start(
|
esp_rtos::start(timg0.timer0, software_interrupt.software_interrupt0);
|
||||||
timg0.timer0, /*, software_interrupt.software_interrupt0 */
|
|
||||||
);
|
|
||||||
|
|
||||||
static EXECUTOR_CORE_0: StaticCell<InterruptExecutor<2>> = StaticCell::new();
|
static EXECUTOR_CORE_0: StaticCell<InterruptExecutor<2>> = StaticCell::new();
|
||||||
let executor_core_0 = InterruptExecutor::new(software_interrupt.software_interrupt2);
|
let executor_core_0 = InterruptExecutor::new(software_interrupt.software_interrupt2);
|
||||||
|
|
@ -592,7 +590,7 @@ async fn main(_spawner: Spawner) {
|
||||||
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
||||||
esp_rtos::start_second_core(
|
esp_rtos::start_second_core(
|
||||||
peripherals.CPU_CTRL,
|
peripherals.CPU_CTRL,
|
||||||
software_interrupt.software_interrupt0,
|
// software_interrupt.software_interrupt0,
|
||||||
software_interrupt.software_interrupt1,
|
software_interrupt.software_interrupt1,
|
||||||
second_core_stack,
|
second_core_stack,
|
||||||
move || {
|
move || {
|
||||||
|
|
|
||||||
|
|
@ -1276,7 +1276,7 @@ where
|
||||||
//
|
//
|
||||||
// Adafruit would use 11 MHz.
|
// Adafruit would use 11 MHz.
|
||||||
// I had lowered the frequency, so that `DmaBounce` could keep up.
|
// I had lowered the frequency, so that `DmaBounce` could keep up.
|
||||||
.with_frequency(Rate::from_mhz(8)) // From Adafruit
|
.with_frequency(Rate::from_mhz(7)) // From Adafruit
|
||||||
.with_clock_mode(ClockMode {
|
.with_clock_mode(ClockMode {
|
||||||
polarity: Polarity::IdleLow, // From Adafruit
|
polarity: Polarity::IdleLow, // From Adafruit
|
||||||
phase: Phase::ShiftHigh, // From Adafruit
|
phase: Phase::ShiftHigh, // From Adafruit
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,19 @@ use embassy_sync::{
|
||||||
channel::{Channel, TrySendError},
|
channel::{Channel, TrySendError},
|
||||||
signal::Signal,
|
signal::Signal,
|
||||||
};
|
};
|
||||||
use embassy_time::{Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer, WithTimeout};
|
||||||
use esp_alloc::MemoryCapability;
|
use esp_alloc::MemoryCapability;
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
Blocking,
|
Blocking,
|
||||||
dma::{
|
dma::{
|
||||||
self, AnyGdmaChannel, BufView, BurstConfig, DmaChannel, DmaChannelConvert, DmaDescriptor,
|
self, AnyGdmaChannel, BufView, BurstConfig, DmaChannel, DmaChannelConvert, DmaDescriptor,
|
||||||
DmaDescriptorFlags, DmaEligible, DmaRxStreamBuf, DmaTxBuf, DmaTxBuffer, DmaTxInterrupt,
|
DmaDescriptorFlags, DmaEligible, DmaRxStreamBuf, DmaTxBuf, DmaTxBuffer, DmaTxInterrupt,
|
||||||
ExternalBurstConfig, Mem2Mem,
|
ExternalBurstConfig, Mem2Mem, SimpleMem2MemTransfer,
|
||||||
},
|
},
|
||||||
dma_descriptors, handler,
|
dma_descriptors, handler,
|
||||||
interrupt::{self, Priority},
|
interrupt::{self, Priority},
|
||||||
lcd_cam::lcd::dpi::{Dpi, DpiTransfer},
|
lcd_cam::lcd::dpi::{Dpi, DpiTransfer},
|
||||||
peripherals::{DMA, DMA_CH0, Peripherals, SPI2},
|
peripherals::{DMA, DMA_CH0, Interrupt, Peripherals, SPI2},
|
||||||
ram,
|
ram,
|
||||||
spi::master::AnySpi,
|
spi::master::AnySpi,
|
||||||
};
|
};
|
||||||
|
|
@ -74,6 +74,11 @@ pub unsafe fn cache_invalidate_addr(addr: u32, size: u32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const DMA_CHANNEL_INBOUND: usize = 0;
|
||||||
|
// const INTERRUPT_INBOUND: Interrupt = Interrupt::DMA_IN_CH0;
|
||||||
|
const DMA_CHANNEL_OUTBOUND: usize = 2;
|
||||||
|
const INTERRUPT_OUTBOUND: Interrupt = Interrupt::DMA_OUT_CH2;
|
||||||
|
|
||||||
pub struct DmaBounce {
|
pub struct DmaBounce {
|
||||||
// TODO: Make these generic.
|
// TODO: Make these generic.
|
||||||
// They currently cannot be generic, because they lacks a `reborrow` method.
|
// They currently cannot be generic, because they lacks a `reborrow` method.
|
||||||
|
|
@ -344,21 +349,28 @@ impl DmaBounce {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enable_interrupts() {
|
fn enable_interrupts() {
|
||||||
// TODO: Get from self.channel
|
|
||||||
let channel_number = 2;
|
|
||||||
let interrupt = esp_hal::peripherals::Interrupt::DMA_OUT_CH2;
|
|
||||||
|
|
||||||
// Enable interrupts for the peripheral
|
// Enable interrupts for the peripheral
|
||||||
interrupt::enable(interrupt, dma_interrupt_handler.priority()).unwrap();
|
// interrupt::enable(INTERRUPT_INBOUND, dma_inbound_interrupt_handler.priority()).unwrap();
|
||||||
|
interrupt::enable(
|
||||||
|
INTERRUPT_OUTBOUND,
|
||||||
|
dma_outbound_interrupt_handler.priority(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Bind the handler
|
// Bind the handler
|
||||||
unsafe {
|
unsafe {
|
||||||
interrupt::bind_interrupt(interrupt, dma_interrupt_handler.handler());
|
// interrupt::bind_interrupt(INTERRUPT_INBOUND, dma_inbound_interrupt_handler.handler());
|
||||||
|
interrupt::bind_interrupt(INTERRUPT_OUTBOUND, dma_outbound_interrupt_handler.handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable interrupts in the peripheral.
|
// Enable interrupts in the peripheral.
|
||||||
|
// DMA::regs()
|
||||||
|
// .ch(DMA_CHANNEL_INBOUND)
|
||||||
|
// .in_int()
|
||||||
|
// .ena()
|
||||||
|
// .modify(|_, w| w.in_done().bit(true));
|
||||||
DMA::regs()
|
DMA::regs()
|
||||||
.ch(channel_number)
|
.ch(DMA_CHANNEL_OUTBOUND)
|
||||||
.out_int()
|
.out_int()
|
||||||
.ena()
|
.ena()
|
||||||
.modify(|_, w| w.out_eof().bit(true));
|
.modify(|_, w| w.out_eof().bit(true));
|
||||||
|
|
@ -366,7 +378,7 @@ impl DmaBounce {
|
||||||
|
|
||||||
/// Receive a window of bytes into the current dst bounce buffer.
|
/// Receive a window of bytes into the current dst bounce buffer.
|
||||||
/// Finally, swaps the bounce buffers.
|
/// Finally, swaps the bounce buffers.
|
||||||
fn receive_window_blocking(&mut self) {
|
async fn receive_window(&mut self) {
|
||||||
// Descriptors are initialized by `DmaTxBuf::new`.
|
// Descriptors are initialized by `DmaTxBuf::new`.
|
||||||
let buffer_src_window =
|
let buffer_src_window =
|
||||||
&mut self.buffer_src[self.window_index_next * self.window_size..][..self.window_size];
|
&mut self.buffer_src[self.window_index_next * self.window_size..][..self.window_size];
|
||||||
|
|
@ -392,13 +404,15 @@ impl DmaBounce {
|
||||||
let bounce_dst_descs = unsafe { &mut *(self.bounce_dst_descs as *mut _) };
|
let bounce_dst_descs = unsafe { &mut *(self.bounce_dst_descs as *mut _) };
|
||||||
let src_descs = unsafe { &mut *(self.src_descs as *mut _) };
|
let src_descs = unsafe { &mut *(self.src_descs as *mut _) };
|
||||||
let mut mem2mem = Mem2Mem::new(self.channel.reborrow(), self.peripheral_src.reborrow())
|
let mut mem2mem = Mem2Mem::new(self.channel.reborrow(), self.peripheral_src.reborrow())
|
||||||
|
.into_async()
|
||||||
.with_descriptors(bounce_dst_descs, src_descs, self.burst_config)
|
.with_descriptors(bounce_dst_descs, src_descs, self.burst_config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let transfer = mem2mem
|
let transfer = mem2mem
|
||||||
.start_transfer(&mut self.bounce_buffer_dst, buffer_src_window)
|
.start_transfer(&mut self.bounce_buffer_dst, buffer_src_window)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
transfer.wait().unwrap();
|
// INBOUND_TRANSFER_FINISHED.wait().await;
|
||||||
|
transfer.wait_async().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.increase_window_counter(1);
|
self.increase_window_counter(1);
|
||||||
|
|
@ -409,7 +423,7 @@ impl DmaBounce {
|
||||||
core::mem::swap(&mut self.bounce_buffer_dst, &mut self.bounce_buffer_src);
|
core::mem::swap(&mut self.bounce_buffer_dst, &mut self.bounce_buffer_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.window_index_next = self.window_index_next + windows;
|
self.window_index_next += windows;
|
||||||
self.frame_index_next += self.window_index_next / self.windows_len;
|
self.frame_index_next += self.window_index_next / self.windows_len;
|
||||||
self.window_index_next = self.window_index_next % self.windows_len;
|
self.window_index_next = self.window_index_next % self.windows_len;
|
||||||
}
|
}
|
||||||
|
|
@ -419,7 +433,7 @@ impl DmaBounce {
|
||||||
Self::enable_interrupts();
|
Self::enable_interrupts();
|
||||||
|
|
||||||
// Receive the first window, so that the outbound transfer can read valid data.
|
// Receive the first window, so that the outbound transfer can read valid data.
|
||||||
self.receive_window_blocking();
|
self.receive_window().await;
|
||||||
|
|
||||||
let mut dma_tx_buffer = self.get_dma_tx_buffer();
|
let mut dma_tx_buffer = self.get_dma_tx_buffer();
|
||||||
let mut transfer = self
|
let mut transfer = self
|
||||||
|
|
@ -434,8 +448,15 @@ impl DmaBounce {
|
||||||
let mut windows_skipped_total = 0;
|
let mut windows_skipped_total = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
self.receive_window_blocking();
|
self.receive_window().await;
|
||||||
let windows_skipped = WINDOWS_SKIPPED.wait().await;
|
let windows_skipped = WINDOWS_SKIPPED
|
||||||
|
.wait()
|
||||||
|
// .with_timeout(Duration::from_secs(1))
|
||||||
|
.await;
|
||||||
|
// .unwrap_or_else(|_| {
|
||||||
|
// error!("Timed out while waiting for skipped windows");
|
||||||
|
// 0
|
||||||
|
// });
|
||||||
|
|
||||||
if windows_skipped > 0 {
|
if windows_skipped > 0 {
|
||||||
self.increase_window_counter(windows_skipped);
|
self.increase_window_counter(windows_skipped);
|
||||||
|
|
@ -548,11 +569,12 @@ unsafe impl DmaTxBuffer for DmaTxBounceBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
static WINDOWS_SKIPPED: Signal<RawMutex, usize> = Signal::new();
|
static WINDOWS_SKIPPED: Signal<RawMutex, usize> = Signal::new();
|
||||||
|
// static INBOUND_TRANSFER_FINISHED: Signal<RawMutex, ()> = Signal::new();
|
||||||
|
|
||||||
#[handler(priority = Priority::Priority3)]
|
#[handler(priority = Priority::Priority3)]
|
||||||
#[ram] // Improves performance.
|
#[ram] // Improves performance.
|
||||||
fn dma_interrupt_handler() {
|
fn dma_outbound_interrupt_handler() {
|
||||||
let interrupt = DMA::regs().ch(2).out_int();
|
let interrupt = DMA::regs().ch(DMA_CHANNEL_OUTBOUND).out_int();
|
||||||
let bounce_buffer_processed = interrupt.st().read().out_eof().bit_is_set();
|
let bounce_buffer_processed = interrupt.st().read().out_eof().bit_is_set();
|
||||||
if bounce_buffer_processed {
|
if bounce_buffer_processed {
|
||||||
// Clear the bit by writing 1 to the clear bits.
|
// Clear the bit by writing 1 to the clear bits.
|
||||||
|
|
@ -566,6 +588,25 @@ fn dma_interrupt_handler() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[handler(priority = Priority::Priority3)]
|
||||||
|
// #[ram] // Improves performance.
|
||||||
|
// fn dma_inbound_interrupt_handler() {
|
||||||
|
// warn!("Inbound");
|
||||||
|
|
||||||
|
// let interrupt = DMA::regs().ch(DMA_CHANNEL_INBOUND).in_int();
|
||||||
|
// let bounce_buffer_processed = interrupt.st().read().in_done().bit_is_set();
|
||||||
|
// if bounce_buffer_processed {
|
||||||
|
// // Clear the bit by writing 1 to the clear bits.
|
||||||
|
// interrupt.clr().write(|w| w.in_done().bit(true));
|
||||||
|
|
||||||
|
// assert!(
|
||||||
|
// !INBOUND_TRANSFER_FINISHED.signaled(),
|
||||||
|
// "inbound transfer already signalled"
|
||||||
|
// );
|
||||||
|
// INBOUND_TRANSFER_FINISHED.signal(());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
pub async fn run_lcd(
|
pub async fn run_lcd(
|
||||||
mut st7701s: St7701s<'static, Blocking>,
|
mut st7701s: St7701s<'static, Blocking>,
|
||||||
framebuffer: &'static mut Framebuffer,
|
framebuffer: &'static mut Framebuffer,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue