diff --git a/esp-hal/src/lcd_cam/cam.rs b/esp-hal/src/lcd_cam/cam.rs index abf7b5d398f..dee3a375949 100644 --- a/esp-hal/src/lcd_cam/cam.rs +++ b/esp-hal/src/lcd_cam/cam.rs @@ -81,7 +81,7 @@ use crate::{ OutputSignal, Pull, }, - lcd_cam::{cam::private::RxPins, private::calculate_clkm, BitOrder, ByteOrder}, + lcd_cam::{calculate_clkm, BitOrder, ByteOrder}, peripheral::{Peripheral, PeripheralRef}, peripherals::LCD_CAM, }; @@ -604,8 +604,7 @@ impl RxPins for RxSixteenBits { const BUS_WIDTH: usize = 2; } -mod private { - pub trait RxPins { - const BUS_WIDTH: usize; - } +#[doc(hidden)] +pub trait RxPins { + const BUS_WIDTH: usize; } diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 72ab45dea80..2017f4d07cb 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -75,12 +75,13 @@ use crate::{ OutputSignal, }, lcd_cam::{ - asynch::LCD_DONE_WAKER, - lcd::{i8080::private::TxPins, ClockMode, DelayMode, Phase, Polarity}, - private::{calculate_clkm, Instance}, + calculate_clkm, + lcd::{ClockMode, DelayMode, Phase, Polarity}, BitOrder, ByteOrder, + Instance, Lcd, + LCD_DONE_WAKER, }, peripheral::{Peripheral, PeripheralRef}, peripherals::LCD_CAM, @@ -703,8 +704,7 @@ impl<'d> TxPins for TxSixteenBits<'d> { } } -mod private { - pub trait TxPins { - fn configure(&mut self); - } +#[doc(hidden)] +pub trait TxPins { + fn configure(&mut self); } diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index 38a29f83b4a..3e095151fe0 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -10,12 +10,18 @@ pub mod lcd; use core::marker::PhantomData; +use embassy_sync::waitqueue::AtomicWaker; + use crate::{ interrupt::InterruptHandler, lcd_cam::{cam::Cam, lcd::Lcd}, + macros::handler, peripheral::Peripheral, - peripherals::LCD_CAM, + peripherals::{Interrupt, LCD_CAM}, system::{self, PeripheralClockControl}, + Async, + Blocking, + Cpu, InterruptConfigurable, }; @@ -27,7 +33,7 @@ pub struct LcdCam<'d, DM: crate::Mode> { pub cam: Cam<'d>, } -impl<'d> LcdCam<'d, crate::Blocking> { +impl<'d> LcdCam<'d, Blocking> { /// Creates a new `LcdCam` instance. pub fn new(lcd_cam: impl Peripheral

+ 'd) -> Self { crate::into_ref!(lcd_cam); @@ -40,56 +46,47 @@ impl<'d> LcdCam<'d, crate::Blocking> { lcd_cam: unsafe { lcd_cam.clone_unchecked() }, _mode: PhantomData, }, - cam: Cam { - lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + cam: Cam { lcd_cam }, + } + } + + /// Reconfigures the peripheral for asynchronous operation. + pub fn into_async(mut self) -> LcdCam<'d, Async> { + self.set_interrupt_handler(interrupt_handler); + LcdCam { + lcd: Lcd { + lcd_cam: self.lcd.lcd_cam, + _mode: PhantomData, }, + cam: self.cam, } } } -impl<'d> crate::private::Sealed for LcdCam<'d, crate::Blocking> {} +impl crate::private::Sealed for LcdCam<'_, Blocking> {} // TODO: This interrupt is shared with the Camera module, we should handle this // in a similar way to the gpio::IO -impl<'d> InterruptConfigurable for LcdCam<'d, crate::Blocking> { +impl InterruptConfigurable for LcdCam<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::LCD_CAM, - handler.handler(), - ); - crate::interrupt::enable(crate::peripherals::Interrupt::LCD_CAM, handler.priority()) - .unwrap(); + crate::interrupt::bind_interrupt(Interrupt::LCD_CAM, handler.handler()); + crate::interrupt::enable(Interrupt::LCD_CAM, handler.priority()).unwrap(); } } } -impl<'d> LcdCam<'d, crate::Async> { - /// Creates a new `LcdCam` instance for asynchronous operation. - pub fn new_async(lcd_cam: impl Peripheral

+ 'd) -> Self { - crate::into_ref!(lcd_cam); - - PeripheralClockControl::enable(system::Peripheral::LcdCam); - - unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::LCD_CAM, - asynch::interrupt_handler.handler(), - ); - } - crate::interrupt::enable( - crate::peripherals::Interrupt::LCD_CAM, - asynch::interrupt_handler.priority(), - ) - .unwrap(); - - Self { +impl<'d> LcdCam<'d, Async> { + /// Reconfigures the peripheral for blocking operation. + pub fn into_blocking(self) -> LcdCam<'d, Blocking> { + crate::interrupt::disable(Cpu::ProCpu, Interrupt::LCD_CAM); + #[cfg(multi_core)] + crate::interrupt::disable(Cpu::AppCpu, Interrupt::LCD_CAM); + LcdCam { lcd: Lcd { - lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + lcd_cam: self.lcd.lcd_cam, _mode: PhantomData, }, - cam: Cam { - lcd_cam: unsafe { lcd_cam.clone_unchecked() }, - }, + cam: self.cam, } } } @@ -116,205 +113,193 @@ pub enum ByteOrder { Inverted = 1, } -#[doc(hidden)] -pub mod asynch { - use embassy_sync::waitqueue::AtomicWaker; - use procmacros::handler; - - use super::private::Instance; - - pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new(); +pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new(); - #[handler] - pub(crate) fn interrupt_handler() { - // TODO: this is a shared interrupt with Camera and here we ignore that! - if Instance::is_lcd_done_set() { - Instance::unlisten_lcd_done(); - LCD_DONE_WAKER.wake() - } +#[handler] +fn interrupt_handler() { + // TODO: this is a shared interrupt with Camera and here we ignore that! + if Instance::is_lcd_done_set() { + Instance::unlisten_lcd_done(); + LCD_DONE_WAKER.wake() } } -mod private { - use crate::peripherals::LCD_CAM; - - pub(crate) struct Instance; - - // NOTE: the LCD_CAM interrupt registers are shared between LCD and Camera and - // this is only implemented for the LCD side, when the Camera is implemented a - // CriticalSection will be needed to protect these shared registers. - impl Instance { - pub(crate) fn listen_lcd_done() { - let lcd_cam = unsafe { LCD_CAM::steal() }; - lcd_cam - .lc_dma_int_ena() - .modify(|_, w| w.lcd_trans_done_int_ena().set_bit()); - } - - pub(crate) fn unlisten_lcd_done() { - let lcd_cam = unsafe { LCD_CAM::steal() }; - lcd_cam - .lc_dma_int_ena() - .modify(|_, w| w.lcd_trans_done_int_ena().clear_bit()); - } +pub(crate) struct Instance; + +// NOTE: the LCD_CAM interrupt registers are shared between LCD and Camera and +// this is only implemented for the LCD side, when the Camera is implemented a +// CriticalSection will be needed to protect these shared registers. +impl Instance { + pub(crate) fn listen_lcd_done() { + let lcd_cam = unsafe { LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_ena() + .modify(|_, w| w.lcd_trans_done_int_ena().set_bit()); + } - pub(crate) fn is_lcd_done_set() -> bool { - let lcd_cam = unsafe { LCD_CAM::steal() }; - lcd_cam - .lc_dma_int_raw() - .read() - .lcd_trans_done_int_raw() - .bit() - } + pub(crate) fn unlisten_lcd_done() { + let lcd_cam = unsafe { LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_ena() + .modify(|_, w| w.lcd_trans_done_int_ena().clear_bit()); } - pub struct ClockDivider { - // Integral LCD clock divider value. (8 bits) - // Value 0 is treated as 256 - // Value 1 is treated as 2 - // Value N is treated as N - pub div_num: usize, - - // Fractional clock divider numerator value. (6 bits) - pub div_b: usize, - - // Fractional clock divider denominator value. (6 bits) - pub div_a: usize, + + pub(crate) fn is_lcd_done_set() -> bool { + let lcd_cam = unsafe { LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_raw() + .read() + .lcd_trans_done_int_raw() + .bit() } +} +pub(crate) struct ClockDivider { + // Integral LCD clock divider value. (8 bits) + // Value 0 is treated as 256 + // Value 1 is treated as 2 + // Value N is treated as N + pub div_num: usize, + + // Fractional clock divider numerator value. (6 bits) + pub div_b: usize, + + // Fractional clock divider denominator value. (6 bits) + pub div_a: usize, +} - pub fn calculate_clkm( - desired_frequency: usize, - source_frequencies: &[usize], - ) -> (usize, ClockDivider) { - let mut result_freq = 0; - let mut result = None; - - for (i, &source_frequency) in source_frequencies.iter().enumerate() { - let div = calculate_closest_divider(source_frequency, desired_frequency); - if let Some(div) = div { - let freq = calculate_output_frequency(source_frequency, &div); - if result.is_none() || freq > result_freq { - result = Some((i, div)); - result_freq = freq; - } +pub(crate) fn calculate_clkm( + desired_frequency: usize, + source_frequencies: &[usize], +) -> (usize, ClockDivider) { + let mut result_freq = 0; + let mut result = None; + + for (i, &source_frequency) in source_frequencies.iter().enumerate() { + let div = calculate_closest_divider(source_frequency, desired_frequency); + if let Some(div) = div { + let freq = calculate_output_frequency(source_frequency, &div); + if result.is_none() || freq > result_freq { + result = Some((i, div)); + result_freq = freq; } } - - result.expect("Desired frequency was too low for the dividers to divide to") } - fn calculate_output_frequency(source_frequency: usize, divider: &ClockDivider) -> usize { - let n = match divider.div_num { - 0 => 256, - 1 => 2, - _ => divider.div_num.min(256), - }; + result.expect("Desired frequency was too low for the dividers to divide to") +} - if divider.div_b != 0 && divider.div_a != 0 { - // OUTPUT = SOURCE / (N + B/A) - // OUTPUT = SOURCE / ((NA + B)/A) - // OUTPUT = (SOURCE * A) / (NA + B) +fn calculate_output_frequency(source_frequency: usize, divider: &ClockDivider) -> usize { + let n = match divider.div_num { + 0 => 256, + 1 => 2, + _ => divider.div_num.min(256), + }; - // u64 is required to fit the numbers from this arithmetic. + if divider.div_b != 0 && divider.div_a != 0 { + // OUTPUT = SOURCE / (N + B/A) + // OUTPUT = SOURCE / ((NA + B)/A) + // OUTPUT = (SOURCE * A) / (NA + B) - let source = source_frequency as u64; - let n = n as u64; - let a = divider.div_b as u64; - let b = divider.div_a as u64; + // u64 is required to fit the numbers from this arithmetic. - ((source * a) / (n * a + b)) as _ - } else { - source_frequency / n - } - } + let source = source_frequency as u64; + let n = n as u64; + let a = divider.div_b as u64; + let b = divider.div_a as u64; - fn calculate_closest_divider( - source_frequency: usize, - desired_frequency: usize, - ) -> Option { - let div_num = source_frequency / desired_frequency; - if div_num < 2 { - // Source clock isn't fast enough to reach the desired frequency. - // Return max output. - return Some(ClockDivider { - div_num: 1, - div_b: 0, - div_a: 0, - }); - } - if div_num > 256 { - // Source is too fast to divide to the desired frequency. Return None. - return None; - } + ((source * a) / (n * a + b)) as _ + } else { + source_frequency / n + } +} - let div_num = if div_num == 256 { 0 } else { div_num }; +fn calculate_closest_divider( + source_frequency: usize, + desired_frequency: usize, +) -> Option { + let div_num = source_frequency / desired_frequency; + if div_num < 2 { + // Source clock isn't fast enough to reach the desired frequency. + // Return max output. + return Some(ClockDivider { + div_num: 1, + div_b: 0, + div_a: 0, + }); + } + if div_num > 256 { + // Source is too fast to divide to the desired frequency. Return None. + return None; + } - let div_fraction = { - let div_remainder = source_frequency % desired_frequency; - let gcd = hcf(div_remainder, desired_frequency); - Fraction { - numerator: div_remainder / gcd, - denominator: desired_frequency / gcd, - } - }; + let div_num = if div_num == 256 { 0 } else { div_num }; - let divider = if div_fraction.numerator == 0 { - ClockDivider { - div_num, - div_b: 0, - div_a: 0, - } - } else { - let target = div_fraction; - let closest = farey_sequence(63) - .find(|curr| { - // https://en.wikipedia.org/wiki/Fraction#Adding_unlike_quantities - - let new_curr_num = curr.numerator * target.denominator; - let new_target_num = target.numerator * curr.denominator; - new_curr_num >= new_target_num - }) - .expect("The fraction must be between 0 and 1"); - - ClockDivider { - div_num, - div_b: closest.numerator, - div_a: closest.denominator, - } - }; - Some(divider) - } + let div_fraction = { + let div_remainder = source_frequency % desired_frequency; + let gcd = hcf(div_remainder, desired_frequency); + Fraction { + numerator: div_remainder / gcd, + denominator: desired_frequency / gcd, + } + }; - // https://en.wikipedia.org/wiki/Euclidean_algorithm - const fn hcf(a: usize, b: usize) -> usize { - if b != 0 { - hcf(b, a % b) - } else { - a + let divider = if div_fraction.numerator == 0 { + ClockDivider { + div_num, + div_b: 0, + div_a: 0, } - } + } else { + let target = div_fraction; + let closest = farey_sequence(63) + .find(|curr| { + // https://en.wikipedia.org/wiki/Fraction#Adding_unlike_quantities + + let new_curr_num = curr.numerator * target.denominator; + let new_target_num = target.numerator * curr.denominator; + new_curr_num >= new_target_num + }) + .expect("The fraction must be between 0 and 1"); + + ClockDivider { + div_num, + div_b: closest.numerator, + div_a: closest.denominator, + } + }; + Some(divider) +} - struct Fraction { - pub numerator: usize, - pub denominator: usize, +// https://en.wikipedia.org/wiki/Euclidean_algorithm +const fn hcf(a: usize, b: usize) -> usize { + if b != 0 { + hcf(b, a % b) + } else { + a } +} - // https://en.wikipedia.org/wiki/Farey_sequence#Next_term - fn farey_sequence(denominator: usize) -> impl Iterator { - let mut a = 0; - let mut b = 1; - let mut c = 1; - let mut d = denominator; - core::iter::from_fn(move || { - if a > denominator { - return None; - } - let next = Fraction { - numerator: a, - denominator: b, - }; - let k = (denominator + b) / d; - (a, b, c, d) = (c, d, k * c - a, k * d - b); - Some(next) - }) - } +struct Fraction { + pub numerator: usize, + pub denominator: usize, +} + +// https://en.wikipedia.org/wiki/Farey_sequence#Next_term +fn farey_sequence(denominator: usize) -> impl Iterator { + let mut a = 0; + let mut b = 1; + let mut c = 1; + let mut d = denominator; + core::iter::from_fn(move || { + if a > denominator { + return None; + } + let next = Fraction { + numerator: a, + denominator: b, + }; + let k = (denominator + b) / d; + (a, b, c, d) = (c, d, k * c - a, k * d - b); + Some(next) + }) } diff --git a/hil-test/tests/lcd_cam_i8080_async.rs b/hil-test/tests/lcd_cam_i8080_async.rs index 756e68f1bc5..eefbe00ef8e 100644 --- a/hil-test/tests/lcd_cam_i8080_async.rs +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -37,7 +37,7 @@ mod tests { let peripherals = esp_hal::init(esp_hal::Config::default()); let dma = Dma::new(peripherals.DMA); - let lcd_cam = LcdCam::new_async(peripherals.LCD_CAM); + let lcd_cam = LcdCam::new(peripherals.LCD_CAM).into_async(); let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE); let dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();