microbit_common/display/
blocking.rs

1//! Blocking support for the 5x5 LED display.
2//!
3//! This module provides a simple blocking interface
4//! to the on board 5x5 LED display. If you need a more sophisticated
5//! or non-blocking interface use the [`display::nonblocking`](crate::display::nonblocking) module.
6//!
7//! # Example
8//!
9//! ```no_run
10//! # use microbit_common as microbit;
11//! # use microbit::{
12//! #     Board,
13//! #     hal,
14//! #     display::blocking::Display,
15//! # };
16//! # use embedded_hal::delay::DelayNs;
17//! // take the board
18//! let board = Board::take().unwrap();
19//! // make a timer
20//! let mut timer = hal::Timer::new(board.TIMER0);
21//! // create the Display
22//! let mut display = Display::new(board.display_pins);
23//! // and light up some LEDs
24//! let heart = [
25//!     [0, 1, 0, 1, 0],
26//!     [1, 0, 1, 0, 1],
27//!     [1, 0, 0, 0, 1],
28//!     [0, 1, 0, 1, 0],
29//!     [0, 0, 1, 0, 0],
30//! ];
31//! loop {
32//!     display.show(&mut timer, heart, 1000);
33//!     display.clear();
34//!     timer.delay_ms(250);
35//! }
36//! ```
37//!
38//! The coordiante system is oriented so the 'bottom' (x,4) row is the edge with the edge
39//! connector. That means that
40//!
41//! ```no_run
42//! # use microbit_common as microbit;
43//! # use microbit::{
44//! #     Board,
45//! #     hal,
46//! #     display::blocking::Display,
47//! # };
48//! # let board = Board::take().unwrap();
49//! # let mut timer = hal::Timer::new(board.TIMER0);
50//! # let mut display = Display::new(board.display_pins);
51//! display.show(
52//!    &mut timer,
53//!    [
54//!        [0, 0, 1, 0, 0],
55//!        [0, 1, 1, 1, 0],
56//!        [1, 0, 1, 0, 1],
57//!        [0, 0, 1, 0, 0],
58//!        [0, 0, 1, 0, 0],
59//!    ],
60//!    1000,
61//!);
62//! ```
63//! Will display an arrow pointing towards the boards usb port.
64//!
65//! For a working example [`examples/display-blocking`](https://github.com/nrf-rs/microbit/tree/main/examples/display-blocking)
66use crate::gpio::{DisplayPins, NUM_COLS, NUM_ROWS};
67use crate::hal::gpio::{Output, Pin, PushPull};
68use embedded_hal::{delay::DelayNs, digital::OutputPin};
69
70#[allow(clippy::upper_case_acronyms)]
71pub(crate) type LED = Pin<Output<PushPull>>;
72
73const DEFAULT_DELAY_MS: u32 = 2;
74#[cfg(feature = "v1")]
75const LED_LAYOUT: [[(usize, usize); 5]; 5] = [
76    [(0, 0), (1, 3), (0, 1), (1, 4), (0, 2)],
77    [(2, 3), (2, 4), (2, 5), (2, 6), (2, 7)],
78    [(1, 1), (0, 8), (1, 2), (2, 8), (1, 0)],
79    [(0, 7), (0, 6), (0, 5), (0, 4), (0, 3)],
80    [(2, 2), (1, 6), (2, 0), (1, 5), (2, 1)],
81];
82
83/// Blocking interface to the on board LED display
84pub struct Display {
85    delay_ms: u32,
86    rows: [LED; NUM_ROWS],
87    cols: [LED; NUM_COLS],
88}
89
90impl Display {
91    /// Create and initialise the display driver
92    ///
93    /// The [`display_pins!`](crate::display_pins) macro can be used
94    /// to create [`DisplayPins`].
95    pub fn new(pins: DisplayPins) -> Self {
96        let (cols, rows) = pins.degrade();
97        Display {
98            delay_ms: DEFAULT_DELAY_MS,
99            rows,
100            cols,
101        }
102    }
103
104    /// Clear the display
105    pub fn clear(&mut self) {
106        for row in &mut self.rows {
107            row.set_low().ok();
108        }
109        for col in &mut self.cols {
110            col.set_high().ok();
111        }
112    }
113
114    /// Set delay, time spent on each matrix row, in ms
115    pub fn set_delay_ms(&mut self, delay_ms: u32) {
116        self.delay_ms = delay_ms;
117    }
118
119    /// Set refresh rate, time for matrix scan
120    pub fn set_refresh_rate(&mut self, freq_hz: u32) {
121        self.delay_ms = 1000 / freq_hz / (NUM_ROWS as u32);
122    }
123
124    /// Convert 5x5 image to 3x9 matrix
125    ///
126    /// The pins are represented as a [3x9 matrix on the micro:bit
127    /// V1](https://tech.microbit.org/hardware/1-5-revision/#display).
128    #[cfg(feature = "v1")]
129    fn image2matrix(led_image: [[u8; 5]; 5]) -> [[u8; 9]; 3] {
130        let mut led_matrix: [[u8; 9]; 3] = [[0; 9]; 3];
131        for (led_image_row, layout_row) in led_image.iter().zip(LED_LAYOUT.iter()) {
132            for (led_image_val, layout_loc) in led_image_row.iter().zip(layout_row) {
133                led_matrix[layout_loc.0][layout_loc.1] = *led_image_val;
134            }
135        }
136        led_matrix
137    }
138
139    /// Display 5x5 image for a given duration
140    pub fn show<D: DelayNs>(&mut self, delay: &mut D, led_display: [[u8; 5]; 5], duration_ms: u32) {
141        #[cfg(feature = "v1")]
142        {
143            let led_matrix = Display::image2matrix(led_display);
144            self.show_inner(delay, led_matrix, duration_ms);
145        }
146        #[cfg(feature = "v2")]
147        self.show_inner(delay, led_display, duration_ms);
148    }
149
150    /// Display matrix image for a given duration (3x9 for V1 micro:bit)
151    ///
152    /// The pins are represented as a [3x9 matrix on the micro:bit
153    /// V1](https://tech.microbit.org/hardware/1-5-revision/#display).
154    fn show_inner<D: DelayNs>(
155        &mut self,
156        delay: &mut D,
157        led_matrix: [[u8; NUM_COLS]; NUM_ROWS],
158        duration_ms: u32,
159    ) {
160        // TODO: something more intelligent with timers
161        let loops = duration_ms / (self.rows.len() as u32 * self.delay_ms);
162        for _ in 0..loops {
163            for (row_line, led_matrix_row) in self.rows.iter_mut().zip(led_matrix.iter()) {
164                row_line.set_high().ok();
165                for (col_line, led_matrix_val) in self.cols.iter_mut().zip(led_matrix_row.iter()) {
166                    // TODO : use value to set brightness
167                    if *led_matrix_val > 0 {
168                        col_line.set_low().ok();
169                    }
170                }
171                delay.delay_us(self.delay_ms * 1000);
172                for col_line in &mut self.cols {
173                    col_line.set_high().ok();
174                }
175                row_line.set_low().ok();
176            }
177        }
178    }
179}