Skip to main content

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