//! # LCD and Camera //! //! ## Overview //! This peripheral consists of an LCD module and a Camera module, which can be //! used simultaneously. For more information on these modules, please refer to //! the documentation in their respective modules. pub mod cam; 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::{Interrupt, LCD_CAM}, system::{self, PeripheralClockControl}, Async, Blocking, Cpu, InterruptConfigurable, }; /// Represents a combined LCD and Camera interface. pub struct LcdCam<'d, DM: crate::Mode> { /// The LCD interface. pub lcd: Lcd<'d, DM>, /// The Camera interface. pub cam: Cam<'d>, } impl<'d> LcdCam<'d, Blocking> { /// Creates a new `LcdCam` instance. pub fn new(lcd_cam: impl Peripheral
+ 'd) -> Self {
crate::into_ref!(lcd_cam);
PeripheralClockControl::reset(system::Peripheral::LcdCam);
PeripheralClockControl::enable(system::Peripheral::LcdCam);
Self {
lcd: Lcd {
lcd_cam: unsafe { lcd_cam.clone_unchecked() },
_mode: PhantomData,
},
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 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 InterruptConfigurable for LcdCam<'_, Blocking> {
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
for core in crate::Cpu::other() {
crate::interrupt::disable(core, Interrupt::LCD_CAM);
}
unsafe { crate::interrupt::bind_interrupt(Interrupt::LCD_CAM, handler.handler()) };
unwrap!(crate::interrupt::enable(
Interrupt::LCD_CAM,
handler.priority()
));
}
}
impl<'d> LcdCam<'d, Async> {
/// Reconfigures the peripheral for blocking operation.
pub fn into_blocking(self) -> LcdCam<'d, Blocking> {
crate::interrupt::disable(Cpu::current(), Interrupt::LCD_CAM);
LcdCam {
lcd: Lcd {
lcd_cam: self.lcd.lcd_cam,
_mode: PhantomData,
},
cam: self.cam,
}
}
}
/// LCD_CAM bit order
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BitOrder {
/// Do not change bit order.
#[default]
Native = 0,
/// Invert bit order.
Inverted = 1,
}
/// LCD_CAM byte order
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ByteOrder {
/// Do not change byte order.
#[default]
Native = 0,
/// Invert byte order.
Inverted = 1,
}
pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new();
#[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()
}
}
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) 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(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),
};
if divider.div_b != 0 && divider.div_a != 0 {
// OUTPUT = SOURCE / (N + B/A)
// OUTPUT = SOURCE / ((NA + B)/A)
// OUTPUT = (SOURCE * A) / (NA + B)
// u64 is required to fit the numbers from this arithmetic.
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;
((source * a) / (n * a + b)) as _
} else {
source_frequency / n
}
}
fn calculate_closest_divider(
source_frequency: usize,
desired_frequency: usize,
) -> Option