use core::marker::PhantomData;
use crate::{
Async,
Blocking,
asynch::AtomicWaker,
clock::dividers::FractionalDivider,
handler,
interrupt::InterruptHandler,
lcd_cam::{cam::Cam, lcd::Lcd},
peripherals::{Interrupt, LCD_CAM},
system::{Cpu, GenericPeripheralGuard},
};
pub mod cam;
pub mod lcd;
#[diagnostic::on_unimplemented(
message = "The DMA channel cannot be used as a TX channel for LCD",
label = "This DMA channel"
)]
pub trait LcdDmaTxChannel<'d>: Into<ErasedTxChannel<'d>> + crate::private::Sealed {}
#[diagnostic::on_unimplemented(
message = "The DMA channel cannot be used as an RX channel for Camera",
label = "This DMA channel"
)]
pub trait CamDmaRxChannel<'d>: Into<ErasedRxChannel<'d>> + crate::private::Sealed {}
with_lcd_cam_dma_engine! {
($engine:tt, $any_channel:tt) => {
type ErasedTxChannel<'d> = <crate::dma::$any_channel<'d> as crate::dma::DmaChannel>::Tx;
type ErasedRxChannel<'d> = <crate::dma::$any_channel<'d> as crate::dma::DmaChannel>::Rx;
crate::macros::impl_dma_channel_trait! {
$engine,
peri = LCD_CAM,
($peri:path, $ch:path) => {
impl<'d> LcdDmaTxChannel<'d> for $ch {}
impl<'d> CamDmaRxChannel<'d> for $ch {}
}
}
impl<'d> LcdDmaTxChannel<'d> for ErasedTxChannel<'d> {}
impl<'d> CamDmaRxChannel<'d> for ErasedRxChannel<'d> {}
};
}
pub struct LcdCam<'d, Dm: crate::DriverMode> {
pub lcd: Lcd<'d, Dm>,
pub cam: Cam<'d>,
}
impl<'d> LcdCam<'d, Blocking> {
pub fn new(lcd_cam: LCD_CAM<'d>) -> Self {
let lcd_guard = GenericPeripheralGuard::new();
let cam_guard = GenericPeripheralGuard::new();
Self {
lcd: Lcd {
inner: lcd::Inner {
lcd_cam: unsafe { lcd_cam.clone_unchecked() },
_guard: lcd_guard,
clock_requested: false,
},
_mode: PhantomData,
},
cam: Cam {
lcd_cam,
_guard: cam_guard,
clock_requested: false,
},
}
}
pub fn into_async(mut self) -> LcdCam<'d, Async> {
self.set_interrupt_handler(interrupt_handler);
LcdCam {
lcd: self.lcd.into_async(),
cam: self.cam,
}
}
#[instability::unstable]
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
for core in crate::system::Cpu::other() {
crate::interrupt::disable(core, Interrupt::LCD_CAM);
}
crate::interrupt::bind_handler(Interrupt::LCD_CAM, handler);
}
}
impl crate::private::Sealed for LcdCam<'_, Blocking> {}
#[instability::unstable]
impl crate::interrupt::InterruptConfigurable for LcdCam<'_, Blocking> {
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
self.set_interrupt_handler(handler);
}
}
impl<'d> LcdCam<'d, Async> {
pub fn into_blocking(self) -> LcdCam<'d, Blocking> {
crate::interrupt::disable(Cpu::current(), Interrupt::LCD_CAM);
LcdCam {
lcd: self.lcd.into_blocking(),
cam: self.cam,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BitOrder {
#[default]
Native = 0,
Inverted = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ByteOrder {
#[default]
Native = 0,
Inverted = 1,
}
pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new();
#[handler]
fn interrupt_handler() {
if Instance::is_lcd_done_set() {
Instance::unlisten_lcd_done();
LCD_DONE_WAKER.wake()
}
}
pub(crate) struct Instance;
impl Instance {
fn enable_listenlcd_done(en: bool) {
LCD_CAM::regs()
.lc_dma_int_ena()
.modify(|_, w| w.lcd_trans_done_int_ena().bit(en));
}
pub(crate) fn listen_lcd_done() {
Self::enable_listenlcd_done(true);
}
pub(crate) fn unlisten_lcd_done() {
Self::enable_listenlcd_done(false);
}
pub(crate) fn is_lcd_done_set() -> bool {
LCD_CAM::regs()
.lc_dma_int_raw()
.read()
.lcd_trans_done_int_raw()
.bit()
}
}
pub(crate) struct ClockDivider {
pub div_num: u32,
pub div_b: u32,
pub div_a: u32,
}
impl ClockDivider {
fn new(divider: FractionalDivider) -> Self {
Self {
div_num: divider.integer,
div_b: divider.numerator,
div_a: divider.denominator.max(1),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClockError {
FrequencyTooLow,
}
pub(crate) fn calculate_clkm(
desired_frequency: u32,
source_frequencies: &[u32],
) -> Result<(usize, ClockDivider), ClockError> {
let mut result_error = 0;
let mut result = None;
for (i, &source_frequency) in source_frequencies.iter().enumerate() {
let Some(divider) = calculate_closest_divider(source_frequency, desired_frequency) else {
continue;
};
let error = divider
.output_frequency(source_frequency)
.abs_diff(desired_frequency);
if result.is_none() || error < result_error {
result = Some((i, divider));
result_error = error;
}
}
let (index, divider) = result.ok_or(ClockError::FrequencyTooLow)?;
Ok((index, ClockDivider::new(divider)))
}
fn calculate_closest_divider(
source_frequency: u32,
desired_frequency: u32,
) -> Option<FractionalDivider> {
let (min_divider, max_divider) = property!("clock_tree.lcd_cam.lcd_clock.div_num");
let (_, max_denominator) = property!("clock_tree.lcd_cam.lcd_clock.div_a");
if source_frequency / desired_frequency < min_divider {
return Some(FractionalDivider {
integer: min_divider,
numerator: 0,
denominator: 0,
});
}
let divider = FractionalDivider::new(source_frequency, desired_frequency, max_denominator);
(divider.integer <= max_divider).then_some(divider)
}