esp_hal/lcd_cam/
mod.rs

1//! # LCD and Camera
2//!
3//! ## Overview
4//! This peripheral consists of an LCD module and a Camera module, which can be
5//! used simultaneously. For more information on these modules, please refer to
6//! the documentation in their respective modules.
7
8pub mod cam;
9pub mod lcd;
10
11use core::marker::PhantomData;
12
13use crate::{
14    Async,
15    Blocking,
16    asynch::AtomicWaker,
17    handler,
18    interrupt::InterruptHandler,
19    lcd_cam::{cam::Cam, lcd::Lcd},
20    peripherals::{Interrupt, LCD_CAM},
21    system::{Cpu, GenericPeripheralGuard},
22};
23
24/// Represents a combined LCD and Camera interface.
25pub struct LcdCam<'d, Dm: crate::DriverMode> {
26    /// The LCD interface.
27    pub lcd: Lcd<'d, Dm>,
28    /// The Camera interface.
29    pub cam: Cam<'d>,
30}
31
32impl<'d> LcdCam<'d, Blocking> {
33    /// Creates a new `LcdCam` instance.
34    pub fn new(lcd_cam: LCD_CAM<'d>) -> Self {
35        let lcd_guard = GenericPeripheralGuard::new();
36        let cam_guard = GenericPeripheralGuard::new();
37
38        Self {
39            lcd: Lcd {
40                lcd_cam: unsafe { lcd_cam.clone_unchecked() },
41                _mode: PhantomData,
42                _guard: lcd_guard,
43            },
44            cam: Cam {
45                lcd_cam,
46                _guard: cam_guard,
47            },
48        }
49    }
50
51    /// Reconfigures the peripheral for asynchronous operation.
52    pub fn into_async(mut self) -> LcdCam<'d, Async> {
53        self.set_interrupt_handler(interrupt_handler);
54        LcdCam {
55            lcd: Lcd {
56                lcd_cam: self.lcd.lcd_cam,
57                _mode: PhantomData,
58                _guard: self.lcd._guard,
59            },
60            cam: self.cam,
61        }
62    }
63
64    /// Registers an interrupt handler for the LCD_CAM peripheral.
65    ///
66    /// Note that this will replace any previously registered interrupt
67    /// handlers.
68    #[instability::unstable]
69    pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
70        for core in crate::system::Cpu::other() {
71            crate::interrupt::disable(core, Interrupt::LCD_CAM);
72        }
73        unsafe { crate::interrupt::bind_interrupt(Interrupt::LCD_CAM, handler.handler()) };
74        unwrap!(crate::interrupt::enable(
75            Interrupt::LCD_CAM,
76            handler.priority()
77        ));
78    }
79}
80
81impl crate::private::Sealed for LcdCam<'_, Blocking> {}
82// TODO: This interrupt is shared with the Camera module, we should handle this
83// in a similar way to the gpio::IO
84#[instability::unstable]
85impl crate::interrupt::InterruptConfigurable for LcdCam<'_, Blocking> {
86    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
87        self.set_interrupt_handler(handler);
88    }
89}
90
91impl<'d> LcdCam<'d, Async> {
92    /// Reconfigures the peripheral for blocking operation.
93    pub fn into_blocking(self) -> LcdCam<'d, Blocking> {
94        crate::interrupt::disable(Cpu::current(), Interrupt::LCD_CAM);
95        LcdCam {
96            lcd: Lcd {
97                lcd_cam: self.lcd.lcd_cam,
98                _mode: PhantomData,
99                _guard: self.lcd._guard,
100            },
101            cam: self.cam,
102        }
103    }
104}
105
106/// LCD_CAM bit order
107#[derive(Debug, Clone, Copy, PartialEq, Default)]
108#[cfg_attr(feature = "defmt", derive(defmt::Format))]
109pub enum BitOrder {
110    /// Do not change bit order.
111    #[default]
112    Native   = 0,
113    /// Invert bit order.
114    Inverted = 1,
115}
116
117/// LCD_CAM byte order
118#[derive(Debug, Clone, Copy, PartialEq, Default)]
119#[cfg_attr(feature = "defmt", derive(defmt::Format))]
120pub enum ByteOrder {
121    /// Do not change byte order.
122    #[default]
123    Native   = 0,
124    /// Invert byte order.
125    Inverted = 1,
126}
127
128pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new();
129
130#[handler]
131fn interrupt_handler() {
132    // TODO: this is a shared interrupt with Camera and here we ignore that!
133    if Instance::is_lcd_done_set() {
134        Instance::unlisten_lcd_done();
135        LCD_DONE_WAKER.wake()
136    }
137}
138
139pub(crate) struct Instance;
140
141// NOTE: the LCD_CAM interrupt registers are shared between LCD and Camera and
142// this is only implemented for the LCD side, when the Camera is implemented a
143// CriticalSection will be needed to protect these shared registers.
144impl Instance {
145    fn enable_listenlcd_done(en: bool) {
146        LCD_CAM::regs()
147            .lc_dma_int_ena()
148            .modify(|_, w| w.lcd_trans_done_int_ena().bit(en));
149    }
150
151    pub(crate) fn listen_lcd_done() {
152        Self::enable_listenlcd_done(true);
153    }
154
155    pub(crate) fn unlisten_lcd_done() {
156        Self::enable_listenlcd_done(false);
157    }
158
159    pub(crate) fn is_lcd_done_set() -> bool {
160        LCD_CAM::regs()
161            .lc_dma_int_raw()
162            .read()
163            .lcd_trans_done_int_raw()
164            .bit()
165    }
166}
167pub(crate) struct ClockDivider {
168    // Integral LCD clock divider value. (8 bits)
169    // Value 0 is treated as 256
170    // Value 1 is treated as 2
171    // Value N is treated as N
172    pub div_num: usize,
173
174    // Fractional clock divider numerator value. (6 bits)
175    pub div_b: usize,
176
177    // Fractional clock divider denominator value. (6 bits)
178    pub div_a: usize,
179}
180
181/// Clock configuration errors.
182#[derive(Debug, Clone, Copy, PartialEq)]
183#[cfg_attr(feature = "defmt", derive(defmt::Format))]
184pub enum ClockError {
185    /// Desired frequency was too low for the dividers to divide to
186    FrequencyTooLow,
187}
188
189pub(crate) fn calculate_clkm(
190    desired_frequency: usize,
191    source_frequencies: &[usize],
192) -> Result<(usize, ClockDivider), ClockError> {
193    let mut result_freq = 0;
194    let mut result = None;
195
196    for (i, &source_frequency) in source_frequencies.iter().enumerate() {
197        let div = calculate_closest_divider(source_frequency, desired_frequency);
198        if let Some(div) = div {
199            let freq = calculate_output_frequency(source_frequency, &div);
200            if result.is_none() || freq > result_freq {
201                result = Some((i, div));
202                result_freq = freq;
203            }
204        }
205    }
206
207    result.ok_or(ClockError::FrequencyTooLow)
208}
209
210fn calculate_output_frequency(source_frequency: usize, divider: &ClockDivider) -> usize {
211    let n = match divider.div_num {
212        0 => 256,
213        1 => 2,
214        _ => divider.div_num.min(256),
215    };
216
217    if divider.div_b != 0 && divider.div_a != 0 {
218        // OUTPUT = SOURCE / (N + B/A)
219        // OUTPUT = SOURCE / ((NA + B)/A)
220        // OUTPUT = (SOURCE * A) / (NA + B)
221
222        // u64 is required to fit the numbers from this arithmetic.
223
224        let source = source_frequency as u64;
225        let n = n as u64;
226        let a = divider.div_b as u64;
227        let b = divider.div_a as u64;
228
229        ((source * a) / (n * a + b)) as _
230    } else {
231        source_frequency / n
232    }
233}
234
235fn calculate_closest_divider(
236    source_frequency: usize,
237    desired_frequency: usize,
238) -> Option<ClockDivider> {
239    let div_num = source_frequency / desired_frequency;
240    if div_num < 2 {
241        // Source clock isn't fast enough to reach the desired frequency.
242        // Return max output.
243        return Some(ClockDivider {
244            div_num: 1,
245            div_b: 0,
246            div_a: 0,
247        });
248    }
249    if div_num > 256 {
250        // Source is too fast to divide to the desired frequency. Return None.
251        return None;
252    }
253
254    let div_num = if div_num == 256 { 0 } else { div_num };
255
256    let div_fraction = {
257        let div_remainder = source_frequency % desired_frequency;
258        let gcd = hcf(div_remainder, desired_frequency);
259        Fraction {
260            numerator: div_remainder / gcd,
261            denominator: desired_frequency / gcd,
262        }
263    };
264
265    let divider = if div_fraction.numerator == 0 {
266        ClockDivider {
267            div_num,
268            div_b: 0,
269            div_a: 0,
270        }
271    } else {
272        let target = div_fraction;
273        let closest = farey_sequence(63).find(|curr| {
274            // https://en.wikipedia.org/wiki/Fraction#Adding_unlike_quantities
275
276            let new_curr_num = curr.numerator * target.denominator;
277            let new_target_num = target.numerator * curr.denominator;
278            new_curr_num >= new_target_num
279        });
280
281        let closest = unwrap!(closest, "The fraction must be between 0 and 1");
282
283        ClockDivider {
284            div_num,
285            div_b: closest.numerator,
286            div_a: closest.denominator,
287        }
288    };
289    Some(divider)
290}
291
292// https://en.wikipedia.org/wiki/Euclidean_algorithm
293const fn hcf(a: usize, b: usize) -> usize {
294    if b != 0 { hcf(b, a % b) } else { a }
295}
296
297struct Fraction {
298    pub numerator: usize,
299    pub denominator: usize,
300}
301
302// https://en.wikipedia.org/wiki/Farey_sequence#Next_term
303fn farey_sequence(denominator: usize) -> impl Iterator<Item = Fraction> {
304    let mut a = 0;
305    let mut b = 1;
306    let mut c = 1;
307    let mut d = denominator;
308    core::iter::from_fn(move || {
309        if a > denominator {
310            return None;
311        }
312        let next = Fraction {
313            numerator: a,
314            denominator: b,
315        };
316        let k = (denominator + b) / d;
317        (a, b, c, d) = (c, d, k * c - a, k * d - b);
318        Some(next)
319    })
320}