WIP: Higher prio
This commit is contained in:
parent
376416c32e
commit
dde67e46fe
|
|
@ -86,6 +86,7 @@ tinyvec = { version = "1.10.0", default-features = false, features = ["alloc"] }
|
|||
esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] }
|
||||
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
||||
indoc = "2.0.7"
|
||||
ouroboros = "0.18.5"
|
||||
|
||||
# A fork of slint with patches for `allocator_api` support.
|
||||
# Don't forget to change `slint-build` in build dependencies, if this is changed.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ use log::LevelFilter;
|
|||
|
||||
pub const LOG_LEVEL_FILTER: LevelFilter = {
|
||||
if let Some(string) = option_env!("ESP_LOG") {
|
||||
if string.eq_ignore_ascii_case("ERROR") {
|
||||
if string.eq_ignore_ascii_case("OFF") {
|
||||
LevelFilter::Off
|
||||
} else if string.eq_ignore_ascii_case("ERROR") {
|
||||
LevelFilter::Error
|
||||
} else if string.eq_ignore_ascii_case("WARN") {
|
||||
LevelFilter::Warn
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use alloc::vec::Vec;
|
|||
use cfg_if::cfg_if;
|
||||
use embassy_embedded_hal::adapter::BlockingAsync;
|
||||
use embassy_embedded_hal::flash::partition::Partition;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_executor::{SendSpawner, Spawner, SpawnerTraceExt};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
|
|
@ -224,7 +224,7 @@ async fn test_bounce_buffers(
|
|||
burst_config,
|
||||
true,
|
||||
);
|
||||
buf.send().await;
|
||||
buf.launch_interrupt_driven_task().await;
|
||||
error!("TEST BOUNCE BUFFERS SECTION DONE");
|
||||
}
|
||||
|
||||
|
|
@ -321,11 +321,17 @@ async fn main(_spawner: Spawner) {
|
|||
static EXECUTOR_CORE_0: StaticCell<InterruptExecutor<2>> = StaticCell::new();
|
||||
let executor_core_0 = InterruptExecutor::new(software_interrupt.software_interrupt2);
|
||||
let executor_core_0 = EXECUTOR_CORE_0.init(executor_core_0);
|
||||
let interrupt_core_0_spawner = executor_core_0.start(interrupt::Priority::Priority3);
|
||||
let interrupt_core_0_spawner = executor_core_0.start(interrupt::Priority::Priority1);
|
||||
|
||||
// static EXECUTOR_CORE_1: StaticCell<InterruptExecutor<3>> = StaticCell::new();
|
||||
// let executor_core_1 = InterruptExecutor::new(software_interrupt.software_interrupt3);
|
||||
// let executor_core_1 = EXECUTOR_CORE_1.init(executor_core_1);
|
||||
// let interrupt_core_1_spawner = executor_core_1.start(interrupt::Priority::Priority2);
|
||||
|
||||
info!("ESP-RTOS started!");
|
||||
|
||||
let main_task_peripherals = MainPeripherals {
|
||||
// high_priority_task_spawner: interrupt_core_1_spawner,
|
||||
uart_rx,
|
||||
software_interrupt1: software_interrupt.software_interrupt1,
|
||||
RNG: peripherals.RNG,
|
||||
|
|
@ -376,6 +382,7 @@ async fn main(_spawner: Spawner) {
|
|||
/// Peripherals passed to the main task.
|
||||
#[allow(non_snake_case)]
|
||||
struct MainPeripherals {
|
||||
// high_priority_task_spawner: SendSpawner,
|
||||
uart_rx: UartRx<'static, Blocking>,
|
||||
software_interrupt1: SoftwareInterrupt<'static, 1>,
|
||||
RNG: esp_hal::peripherals::RNG<'static>,
|
||||
|
|
@ -706,8 +713,8 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
112,
|
||||
368 - 112,
|
||||
960,
|
||||
16,
|
||||
false,
|
||||
8,
|
||||
true,
|
||||
));
|
||||
|
||||
info!("Framebuffer created!");
|
||||
|
|
@ -751,6 +758,19 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
|
||||
info!("Awaiting on all tasks...");
|
||||
|
||||
// let spawner = unsafe { Spawner::for_current_executor() }.await;
|
||||
|
||||
// peripherals
|
||||
// .high_priority_task_spawner
|
||||
// .must_spawn(run_bounce_buffers(framebuffer));
|
||||
|
||||
framebuffer
|
||||
.bounce_buffers
|
||||
.take()
|
||||
.unwrap()
|
||||
.launch_interrupt_driven_task()
|
||||
.await;
|
||||
|
||||
// TODO: Probably want to select! instead and re-try.
|
||||
join_all![
|
||||
run_alloc_stats_reporter(),
|
||||
|
|
@ -762,7 +782,7 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
// warn!("Waited.");
|
||||
// framebuffer.bounce_buffers.send().await;
|
||||
// },
|
||||
framebuffer.bounce_buffers.send(),
|
||||
// framebuffer.bounce_buffers.send(),
|
||||
// ui::dpi::run_lcd(st7701s, framebuffer),
|
||||
// lcd_task,
|
||||
run_devices! (
|
||||
|
|
@ -785,6 +805,11 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
.await;
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn run_bounce_buffers(framebuffer: &'static mut Framebuffer) {
|
||||
framebuffer.bounce_buffers.as_mut().unwrap().send().await;
|
||||
}
|
||||
|
||||
async fn run_alloc_stats_reporter() {
|
||||
let mut psram_used_prev = 0;
|
||||
let mut heap_used_prev = 0;
|
||||
|
|
|
|||
|
|
@ -1276,7 +1276,7 @@ where
|
|||
//
|
||||
// Adafruit would use 11 MHz.
|
||||
// I had lowered the frequency, so that `DmaBounce` could keep up.
|
||||
.with_frequency(Rate::from_mhz(7)) // From Adafruit
|
||||
.with_frequency(Rate::from_mhz(5)) // From Adafruit
|
||||
.with_clock_mode(ClockMode {
|
||||
polarity: Polarity::IdleLow, // From Adafruit
|
||||
phase: Phase::ShiftHigh, // From Adafruit
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use core::{
|
||||
alloc::Layout,
|
||||
cell::UnsafeCell,
|
||||
pin::Pin,
|
||||
sync::atomic::{self, AtomicBool, AtomicU32, AtomicUsize},
|
||||
};
|
||||
|
|
@ -20,7 +21,7 @@ use esp_hal::{
|
|||
dma::{
|
||||
self, AnyGdmaChannel, BufView, BurstConfig, DmaChannel, DmaChannelConvert, DmaDescriptor,
|
||||
DmaDescriptorFlags, DmaEligible, DmaRxStreamBuf, DmaTxBuf, DmaTxBuffer, DmaTxInterrupt,
|
||||
ExternalBurstConfig, InternalBurstConfig, Mem2Mem, SimpleMem2MemTransfer,
|
||||
ExternalBurstConfig, InternalBurstConfig, Mem2Mem, SimpleMem2Mem, SimpleMem2MemTransfer,
|
||||
},
|
||||
dma_descriptors, handler,
|
||||
interrupt::{self, Priority},
|
||||
|
|
@ -33,8 +34,9 @@ use esp_sync::RawMutex;
|
|||
use i_slint_core::software_renderer::{Rgb565Pixel, TargetPixel};
|
||||
use indoc::{formatdoc, indoc};
|
||||
use log::{error, info, warn};
|
||||
use ouroboros::self_referencing;
|
||||
use rmk::{
|
||||
futures::{FutureExt, pin_mut},
|
||||
futures::{self, FutureExt, pin_mut},
|
||||
join_all,
|
||||
};
|
||||
|
||||
|
|
@ -76,6 +78,14 @@ pub unsafe fn cache_invalidate_addr(addr: u32, size: u32) {
|
|||
const DMA_CHANNEL_OUTBOUND: usize = 2;
|
||||
const INTERRUPT_OUTBOUND: Interrupt = Interrupt::DMA_OUT_CH2;
|
||||
|
||||
#[self_referencing]
|
||||
struct ReceivingTransfer {
|
||||
mem2mem: SimpleMem2Mem<'static, Blocking>,
|
||||
#[borrows(mut mem2mem)]
|
||||
#[covariant]
|
||||
transfer: Option<SimpleMem2MemTransfer<'this, 'static, Blocking>>,
|
||||
}
|
||||
|
||||
pub struct DmaBounce {
|
||||
// TODO: Make these generic.
|
||||
// They currently cannot be generic, because they lacks a `reborrow` method.
|
||||
|
|
@ -84,6 +94,8 @@ pub struct DmaBounce {
|
|||
peripheral_src: AnySpi<'static>,
|
||||
// This can also be more generic, see `DmaEligible` in `Mem2Mem::new`.
|
||||
peripheral_dst: Option<Dpi<'static, Blocking>>,
|
||||
// TODO: Combine with peripheral_dst using an enum?
|
||||
transfer_dst: Option<DpiTransfer<'static, DmaTxBounceBuf, Blocking>>,
|
||||
|
||||
// TODO: Consider having a separate burst config for the two transfers.
|
||||
burst_config: BurstConfig,
|
||||
|
|
@ -105,9 +117,11 @@ pub struct DmaBounce {
|
|||
bounce_dst_descs: &'static mut [DmaDescriptor],
|
||||
// A cyclic descriptor list that spans the buffers `bounce_buffer_dst` and `bounce_buffer_src`.
|
||||
bounce_src_descs: &'static mut [DmaDescriptor],
|
||||
descriptors_per_window: usize,
|
||||
// The index of the next window about to be received into the destination bounce buffer.
|
||||
window_index_next: usize,
|
||||
frame_index_next: usize,
|
||||
receiving_transfer: Option<ReceivingTransfer>,
|
||||
}
|
||||
|
||||
impl DmaBounce {
|
||||
|
|
@ -183,21 +197,7 @@ impl DmaBounce {
|
|||
});
|
||||
let bounce_dst_descs =
|
||||
Self::linear_descriptors_for_buffer(window_size, burst_config, |_| {});
|
||||
let bounce_src_descs = if cyclic {
|
||||
Self::bounce_descriptors_for_buffer_cyclic(
|
||||
row_front_porch_bytes,
|
||||
row_width_bytes,
|
||||
window_size_rows,
|
||||
unsafe {
|
||||
(
|
||||
&mut *(bounce_buffer_dst as *mut _),
|
||||
&mut *(bounce_buffer_src as *mut _),
|
||||
)
|
||||
},
|
||||
burst_config,
|
||||
)
|
||||
} else {
|
||||
Self::bounce_descriptors_for_buffer_single(
|
||||
let (bounce_src_descs, descriptors_per_window) = Self::bounce_descriptors_for_buffer(
|
||||
windows_len,
|
||||
row_front_porch_bytes,
|
||||
row_width_bytes,
|
||||
|
|
@ -209,13 +209,14 @@ impl DmaBounce {
|
|||
)
|
||||
},
|
||||
burst_config,
|
||||
)
|
||||
};
|
||||
cyclic,
|
||||
);
|
||||
|
||||
Self {
|
||||
channel,
|
||||
peripheral_src,
|
||||
peripheral_dst: Some(peripheral_dst),
|
||||
transfer_dst: None,
|
||||
burst_config,
|
||||
cyclic,
|
||||
window_size,
|
||||
|
|
@ -226,8 +227,10 @@ impl DmaBounce {
|
|||
src_descs,
|
||||
bounce_dst_descs,
|
||||
bounce_src_descs,
|
||||
descriptors_per_window,
|
||||
window_index_next: 0,
|
||||
frame_index_next: 0,
|
||||
receiving_transfer: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -358,81 +361,15 @@ impl DmaBounce {
|
|||
);
|
||||
}
|
||||
|
||||
fn bounce_descriptors_for_buffer_cyclic(
|
||||
row_front_porch_bytes: usize,
|
||||
row_width_bytes: usize,
|
||||
window_size_rows: usize,
|
||||
bounce_buffers: (&'static mut [u8], &'static mut [u8]),
|
||||
burst_config: BurstConfig,
|
||||
) -> &'static mut [DmaDescriptor] {
|
||||
assert_eq!(
|
||||
bounce_buffers.0.len(),
|
||||
bounce_buffers.1.len(),
|
||||
"bounce buffers must be equal in size"
|
||||
);
|
||||
|
||||
let buffer_len = bounce_buffers.0.len();
|
||||
|
||||
assert_eq!(
|
||||
buffer_len,
|
||||
row_width_bytes * window_size_rows,
|
||||
"the provided bounce buffers have an invalid size"
|
||||
);
|
||||
|
||||
let max_chunk_size = burst_config.max_compatible_chunk_size();
|
||||
let descriptors_per_row_front_porch =
|
||||
dma::descriptor_count(row_front_porch_bytes, max_chunk_size, false);
|
||||
let descriptors_per_row_stored =
|
||||
dma::descriptor_count(row_width_bytes, max_chunk_size, false);
|
||||
let descriptors_per_row = descriptors_per_row_stored + descriptors_per_row_front_porch;
|
||||
let descriptors_per_window = window_size_rows * descriptors_per_row;
|
||||
let descriptors_combined =
|
||||
Box::leak(vec![DmaDescriptor::EMPTY; 2 * descriptors_per_window].into_boxed_slice());
|
||||
let descriptors_pair = descriptors_combined.split_at_mut(descriptors_per_window);
|
||||
|
||||
// Link up the descriptors.
|
||||
fn link_up_descriptors(
|
||||
descriptors: &mut [DmaDescriptor],
|
||||
descriptors_other: &mut [DmaDescriptor],
|
||||
) {
|
||||
let mut next = descriptors_other.first_mut().unwrap();
|
||||
for desc in descriptors.iter_mut().rev() {
|
||||
desc.next = next;
|
||||
next = desc;
|
||||
}
|
||||
}
|
||||
|
||||
link_up_descriptors(descriptors_pair.0, descriptors_pair.1);
|
||||
link_up_descriptors(descriptors_pair.1, descriptors_pair.0);
|
||||
|
||||
// Prepare each descriptor's buffer size.
|
||||
for (bounce_buffer, descriptors) in [
|
||||
(bounce_buffers.0, descriptors_pair.0),
|
||||
(bounce_buffers.1, descriptors_pair.1),
|
||||
] {
|
||||
Self::prepare_descriptors_window(
|
||||
bounce_buffer,
|
||||
descriptors,
|
||||
row_front_porch_bytes,
|
||||
row_width_bytes,
|
||||
window_size_rows,
|
||||
max_chunk_size,
|
||||
descriptors_per_row,
|
||||
descriptors_per_row_front_porch,
|
||||
);
|
||||
}
|
||||
|
||||
descriptors_combined
|
||||
}
|
||||
|
||||
fn bounce_descriptors_for_buffer_single(
|
||||
fn bounce_descriptors_for_buffer(
|
||||
windows_len: usize,
|
||||
row_front_porch_bytes: usize,
|
||||
row_width_bytes: usize,
|
||||
window_size_rows: usize,
|
||||
bounce_buffers: (&'static mut [u8], &'static mut [u8]),
|
||||
burst_config: BurstConfig,
|
||||
) -> &'static mut [DmaDescriptor] {
|
||||
cyclic: bool,
|
||||
) -> (&'static mut [DmaDescriptor], usize) {
|
||||
assert_eq!(
|
||||
bounce_buffers.0.len(),
|
||||
bounce_buffers.1.len(),
|
||||
|
|
@ -449,6 +386,15 @@ impl DmaBounce {
|
|||
"the provided bounce buffers have an invalid size"
|
||||
);
|
||||
|
||||
// Implementation note:
|
||||
// A cyclic descriptor could consist of just a set of descriptors per window,
|
||||
// so two sets in total, because there are two bounce buffers.
|
||||
// However, we can also access the pointer of the EOF descriptor within the
|
||||
// EOF interrupt handler, which lets us compute which descriptor generated that
|
||||
// interrupt.
|
||||
// This is useful in the case when an interrupt is missed. Then the number of interrupts
|
||||
// handled doesn't correspond to the number of windows sent to the destination peripheral.
|
||||
// In that case, the number of windows sent can be computed from the address of the descriptor.
|
||||
let max_chunk_size = burst_config.max_compatible_chunk_size();
|
||||
let descriptors_per_row_front_porch =
|
||||
dma::descriptor_count(row_front_porch_bytes, max_chunk_size, false);
|
||||
|
|
@ -459,9 +405,15 @@ impl DmaBounce {
|
|||
let descriptors_per_frame = descriptors_per_window * windows_len;
|
||||
let descriptors_frame =
|
||||
Box::leak(vec![DmaDescriptor::EMPTY; descriptors_per_frame].into_boxed_slice());
|
||||
let descriptors_frame_ptr = descriptors_frame.as_ptr();
|
||||
|
||||
// Link up the descriptors.
|
||||
let mut next = core::ptr::null_mut();
|
||||
let mut next = if cyclic {
|
||||
descriptors_frame.first_mut().unwrap() as *mut _
|
||||
} else {
|
||||
core::ptr::null_mut()
|
||||
};
|
||||
|
||||
for desc in descriptors_frame.iter_mut().rev() {
|
||||
desc.next = next;
|
||||
next = desc;
|
||||
|
|
@ -497,7 +449,7 @@ impl DmaBounce {
|
|||
windows_len * window_size_rows * (row_front_porch_bytes + row_width_bytes)
|
||||
);
|
||||
|
||||
descriptors_frame
|
||||
(descriptors_frame, descriptors_per_window)
|
||||
}
|
||||
|
||||
fn linear_descriptors_prepare(
|
||||
|
|
@ -549,6 +501,61 @@ impl DmaBounce {
|
|||
.modify(|_, w| w.out_eof().bit(true));
|
||||
}
|
||||
|
||||
/// Receive a window of bytes into the current dst bounce buffer.
|
||||
/// Finally, swaps the bounce buffers.
|
||||
///
|
||||
/// # Safety:
|
||||
/// TODO
|
||||
unsafe fn receive_window_start(&mut self) -> ReceivingTransfer {
|
||||
// Descriptors are initialized by `DmaTxBuf::new`.
|
||||
let buffer_src_window =
|
||||
&mut self.buffer_src[self.window_index_next * self.window_size..][..self.window_size];
|
||||
|
||||
Self::linear_descriptors_prepare(self.src_descs, Some(buffer_src_window), |_desc| {
|
||||
// No need to call `DmaDescriptor::reset_for_tx`, because
|
||||
// 1. we don't rely on the ownership flag;
|
||||
// 2. the EOF flag is already set during the construction of this buffer.
|
||||
});
|
||||
// TODO: Precompute a descriptor list for each buffer, then use `None` instead of `Some(&mut *self.bounce_buffer_dst)`.
|
||||
Self::linear_descriptors_prepare(
|
||||
self.bounce_dst_descs,
|
||||
Some(&mut *self.bounce_buffer_dst),
|
||||
|desc| {
|
||||
desc.reset_for_rx();
|
||||
},
|
||||
);
|
||||
|
||||
// Extend the lifetime to 'static because it is required by Mem2Mem.
|
||||
//
|
||||
// Safety:
|
||||
// Pointees are done being used by the driver before this scope ends,
|
||||
// this is because we `SimpleMem2MemTransfer::wait()` on the transfer to finish.
|
||||
let bounce_dst_descs: &'static mut [DmaDescriptor] =
|
||||
unsafe { &mut *(self.bounce_dst_descs as *mut _) };
|
||||
let src_descs: &'static mut [DmaDescriptor] = unsafe { &mut *(self.src_descs as *mut _) };
|
||||
|
||||
let mem2mem = unsafe {
|
||||
Mem2Mem::new(
|
||||
self.channel.clone_unchecked(),
|
||||
self.peripheral_src.clone_unchecked(),
|
||||
)
|
||||
}
|
||||
.with_descriptors(bounce_dst_descs, src_descs, self.burst_config)
|
||||
.unwrap();
|
||||
|
||||
ReceivingTransferBuilder {
|
||||
mem2mem,
|
||||
transfer_builder: |mem2mem| {
|
||||
Some(
|
||||
mem2mem
|
||||
.start_transfer(&mut self.bounce_buffer_dst, buffer_src_window)
|
||||
.unwrap(),
|
||||
)
|
||||
},
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Receive a window of bytes into the current dst bounce buffer.
|
||||
/// Finally, swaps the bounce buffers.
|
||||
async fn receive_window(&mut self) {
|
||||
|
|
@ -595,14 +602,39 @@ impl DmaBounce {
|
|||
self.increase_window_counter(1);
|
||||
}
|
||||
|
||||
fn increase_window_counter(&mut self, windows: usize) {
|
||||
if windows % 2 == 1 {
|
||||
fn increase_window_counter(&mut self, windows: isize) {
|
||||
if windows.rem_euclid(2) == 1 {
|
||||
core::mem::swap(&mut self.bounce_buffer_dst, &mut self.bounce_buffer_src);
|
||||
}
|
||||
|
||||
self.window_index_next += windows;
|
||||
self.frame_index_next += self.window_index_next / self.windows_len;
|
||||
self.window_index_next = self.window_index_next % self.windows_len;
|
||||
let window_index_next = self.window_index_next as isize + windows;
|
||||
self.frame_index_next = (self.frame_index_next as isize
|
||||
+ window_index_next / self.windows_len as isize)
|
||||
as usize;
|
||||
self.window_index_next = window_index_next.rem_euclid(self.windows_len as isize) as usize;
|
||||
}
|
||||
|
||||
pub async fn launch_interrupt_driven_task(mut self) {
|
||||
Self::enable_interrupts();
|
||||
|
||||
// Receive the first 2 windows, so that the outbound transfer can read valid data.
|
||||
self.receive_window().await;
|
||||
|
||||
let dma_tx_buffer = self.get_dma_tx_buffer();
|
||||
let transfer = self
|
||||
.peripheral_dst
|
||||
.take()
|
||||
.unwrap()
|
||||
.send(self.cyclic /* Send perpetually */, dma_tx_buffer)
|
||||
.unwrap_or_else(|(error, _, _)| {
|
||||
panic!("failed to begin the transmission of the first frame: {error:?}");
|
||||
});
|
||||
self.transfer_dst = Some(transfer);
|
||||
self.receiving_transfer = Some(unsafe { self.receive_window_start() });
|
||||
|
||||
unsafe {
|
||||
*DMA_STATE.0.get() = Some(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send(&mut self) {
|
||||
|
|
@ -633,31 +665,17 @@ impl DmaBounce {
|
|||
// "Window received: {} {}",
|
||||
// self.window_index_next, self.frame_index_next
|
||||
// );
|
||||
let windows_skipped = WINDOWS_SKIPPED
|
||||
let windows_sent = WINDOWS_SENT
|
||||
.wait()
|
||||
.with_timeout(Duration::from_millis(100))
|
||||
.await
|
||||
.unwrap_or_else(|_| {
|
||||
error!("Timed out when waiting for skipped windows.");
|
||||
0 // TODO: This should be -1 to repeat the same window.
|
||||
0
|
||||
});
|
||||
let windows_skipped = windows_sent as isize - 1;
|
||||
|
||||
// let windows_skipped = match windows_skipped {
|
||||
// Ok(windows_skipped) => windows_skipped,
|
||||
// Err(_) => {
|
||||
// warn!(
|
||||
// "Waiting for skipped windows timed out. Transfer done: {}",
|
||||
// transfer.is_done()
|
||||
// );
|
||||
// if transfer.is_done() {
|
||||
// let (result, _, _) = transfer.wait();
|
||||
// panic!("Transfer result: {result:?}");
|
||||
// }
|
||||
// 0
|
||||
// }
|
||||
// };
|
||||
|
||||
if windows_skipped > 0 {
|
||||
if windows_skipped != 0 {
|
||||
self.increase_window_counter(windows_skipped);
|
||||
windows_skipped_total += windows_skipped;
|
||||
// error!(
|
||||
|
|
@ -674,7 +692,9 @@ impl DmaBounce {
|
|||
|
||||
if !self.cyclic && (self.window_index_next == 1 || transfer.is_done()) {
|
||||
if self.window_index_next > 1 {
|
||||
self.increase_window_counter(self.windows_len - self.window_index_next + 1);
|
||||
self.increase_window_counter(
|
||||
self.windows_len as isize - self.window_index_next as isize + 1,
|
||||
);
|
||||
} else if self.window_index_next == 0 {
|
||||
self.increase_window_counter(1);
|
||||
}
|
||||
|
|
@ -763,25 +783,94 @@ unsafe impl DmaTxBuffer for DmaTxBounceBuf {
|
|||
|
||||
/// Intended to be listened on by the renderer, to synchronize the refresh frequency with.
|
||||
pub static FRAMES_SKIPPED: Signal<RawMutex, usize> = Signal::new();
|
||||
static WINDOWS_SKIPPED: Signal<RawMutex, usize> = Signal::new();
|
||||
// static INBOUND_TRANSFER_FINISHED: Signal<RawMutex, ()> = Signal::new();
|
||||
static WINDOWS_SENT: Signal<RawMutex, usize> = Signal::new();
|
||||
static DMA_STATE: SyncUnsafeCell<Option<DmaBounce>> = SyncUnsafeCell(UnsafeCell::new(None));
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct SyncUnsafeCell<T>(UnsafeCell<T>);
|
||||
|
||||
unsafe impl<T> Sync for SyncUnsafeCell<T> {}
|
||||
|
||||
// #[derive(Clone, Copy)]
|
||||
// struct DmaState {
|
||||
// descriptors_ptr: *const DmaDescriptor,
|
||||
// descriptors_per_window: usize,
|
||||
// windows_per_frame: usize,
|
||||
// last_window_index: usize,
|
||||
// }
|
||||
|
||||
// unsafe impl Sync for DmaState {}
|
||||
|
||||
#[handler(priority = Priority::Priority3)]
|
||||
#[ram] // Improves performance.
|
||||
fn dma_outbound_interrupt_handler() {
|
||||
let interrupt = DMA::regs().ch(DMA_CHANNEL_OUTBOUND).out_int();
|
||||
let bounce_buffer_processed = interrupt.st().read().out_eof().bit_is_set();
|
||||
if bounce_buffer_processed {
|
||||
let bounce_buffer_sent = interrupt.st().read().out_eof().bit_is_set();
|
||||
|
||||
if !bounce_buffer_sent {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the bit by writing 1 to the clear bits.
|
||||
interrupt.clr().write(|w| w.out_eof().bit(true));
|
||||
|
||||
WINDOWS_SKIPPED.signal(
|
||||
WINDOWS_SKIPPED
|
||||
.try_take()
|
||||
.map(|windows_skipped| windows_skipped + 1)
|
||||
.unwrap_or_default(),
|
||||
// SAFETY: This value is only ever read in our interrupt handler,
|
||||
// and interrupts are disabled, and we only use this in one thread.
|
||||
let Some(dma_state) = unsafe { &mut *DMA_STATE.0.get() }.as_mut() else {
|
||||
error!("no DMA state available when executing DMA interrupt handler");
|
||||
return;
|
||||
};
|
||||
|
||||
// The descriptor of the buffer with an EOF flag that just finished being sent.
|
||||
let descriptor_ptr = DMA::regs()
|
||||
.ch(DMA_CHANNEL_OUTBOUND)
|
||||
.out_eof_des_addr()
|
||||
.read()
|
||||
.out_eof_des_addr()
|
||||
.bits() as *const DmaDescriptor;
|
||||
// This is the index of the window that just finished being transmitted to the destination peripheral.
|
||||
let window_sent_index =
|
||||
unsafe { descriptor_ptr.offset_from_unsigned(dma_state.bounce_src_descs.as_ptr()) }
|
||||
/ dma_state.descriptors_per_window;
|
||||
// warn!("{window_sent_index}");
|
||||
// The next window to be sent is `(window_sent_index + 1) % dma_state.windows_len`.
|
||||
// That is not the window we want to buffer, because the transmissions would race.
|
||||
// We instead want to buffer the next window:
|
||||
let window_index_next = (window_sent_index + 2) % dma_state.windows_len;
|
||||
|
||||
// Swap bounce buffers.
|
||||
if (dma_state.windows_len + window_index_next - dma_state.window_index_next) % 2 == 1 {
|
||||
core::mem::swap(
|
||||
&mut dma_state.bounce_buffer_dst,
|
||||
&mut dma_state.bounce_buffer_src,
|
||||
);
|
||||
}
|
||||
|
||||
dma_state.window_index_next = window_index_next;
|
||||
|
||||
// warn!("{window_sent_index} {window_index_next}");
|
||||
|
||||
let mut receiving_transfer = dma_state
|
||||
.receiving_transfer
|
||||
.take()
|
||||
.expect("no ongoing transfer to a bounce buffer present");
|
||||
let receiving_transfer = receiving_transfer
|
||||
.with_mut(|x| x.transfer.take())
|
||||
.expect("no ongoing inner transfer to a bounce buffer present");
|
||||
|
||||
// if !receiving_transfer.is_done() {
|
||||
// error!("{window_sent_index}");
|
||||
// error!("the transfer to a bounce buffer has not finished yet, aborting");
|
||||
// }
|
||||
|
||||
if receiving_transfer.is_done() {
|
||||
drop(receiving_transfer);
|
||||
} else {
|
||||
receiving_transfer.wait().unwrap();
|
||||
}
|
||||
|
||||
// If there is any ongoing transfer, cancel it and start a new one.
|
||||
dma_state.receiving_transfer = Some(unsafe { dma_state.receive_window_start() });
|
||||
}
|
||||
|
||||
// #[handler(priority = Priority::Priority3)]
|
||||
|
|
@ -882,7 +971,7 @@ pub fn allocate_dma_buffer_in<A: Allocator>(
|
|||
pub struct Framebuffer {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub bounce_buffers: DmaBounce,
|
||||
pub bounce_buffers: Option<DmaBounce>,
|
||||
}
|
||||
|
||||
impl Framebuffer {
|
||||
|
|
@ -920,11 +1009,11 @@ impl Framebuffer {
|
|||
Self {
|
||||
width: width_pixels,
|
||||
height: height_pixels,
|
||||
bounce_buffers,
|
||||
bounce_buffers: Some(bounce_buffers),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_target_pixels(&mut self) -> &mut [Rgb565Pixel] {
|
||||
bytemuck::cast_slice_mut::<_, Rgb565Pixel>(self.bounce_buffers.buffer_src)
|
||||
bytemuck::cast_slice_mut::<_, Rgb565Pixel>(self.bounce_buffers.as_mut().unwrap().buffer_src)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -377,11 +377,12 @@ impl State {
|
|||
// SIGNAL_LCD_SUBMIT.signal(());
|
||||
#[cfg(feature = "limit-fps")]
|
||||
embassy_time::Timer::after(FRAME_DURATION_MIN).await;
|
||||
let frames_skipped = FRAMES_SKIPPED.wait().await;
|
||||
// let frames_skipped = FRAMES_SKIPPED.wait().await;
|
||||
|
||||
// if frames_skipped > 0 {
|
||||
// error!("Renderer missed {frames_skipped} frames.");
|
||||
// }
|
||||
|
||||
if frames_skipped > 0 {
|
||||
error!("Renderer missed {frames_skipped} frames.");
|
||||
}
|
||||
// SIGNAL_UI_RENDER.wait().await;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue