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}