tiny_led_matrix/lib.rs
1#![no_std]
2
3//! A library for direct control of a small monochrome LED display.
4//!
5//! This is designed to support the [micro:bit](https://microbit.org/)'s 5×5
6//! display, but nothing in this crate is specific to the micro:bit.
7//!
8//! The library assumes the display is internally organised as a two-dimensional
9//! matrix of rows and columns, with the individual LEDs addressed directly
10//! (typically with one GPIO pin for each row and one for each column).
11//!
12//! It isn't designed for the kind of display where you “clock in” the data
13//! for an LED row using a smaller number of output pins.
14//!
15//!
16//! # Display model
17//!
18//! The LED display must be organised as a two-dimensional array of *matrix
19//! rows* and *matrix columns*. These describe how the LEDs are wired up, and
20//! need not match their visible arrangement.
21//!
22//! At any time, LEDs from at most one matrix row of the display are lit; the
23//! display driver repeatedly lights LEDs from each row in turn to create the
24//! illusion of a stable image.
25//!
26//! At present this crate supports at most 16 matrix columns. There's no
27//! strict limit on the number of rows, but in practice it must be small so
28//! that the LEDs are lit for a reasonable proportion of the time.
29//!
30//!
31//! ## Greyscale model
32//!
33//! LED brightness levels are described using a scale from 0 (off) to 9
34//! (brightest) inclusive.
35//!
36//! These are converted to time slices using the same relative durations as
37//! the [micro:bit MicroPython port][micropython] uses.
38//!
39//! The time slice for each level above 1 is approximately 1.9× the slice for
40//! the previous level.
41//!
42//! If there are three matrix rows in the display, an LED with brightness 9 is
43//! lit for one third of the time.
44//!
45//!
46//! # Configuring the library for a device.
47//!
48//! To use this library, you will have to supply implementations of a number
49//! of traits, describing your device and its display.
50//!
51//!
52//! ## Matrix
53//!
54//! You must supply an implementation of the [`Matrix`] trait to describe the
55//! matrix dimensions, and how the matrix corresponds to the visible arrangement
56//! of LEDs.
57//!
58//!
59//! ## Images and Render
60//!
61//! The [`Render`] trait defines the interface that an image-like type needs to
62//! provide in order to be displayed.
63//!
64//! The image reports the brightness to use for a given LED, given coordinates
65//! according to the visible arrangement.
66//!
67//! This crate doesn't supply any implementations of `Render`; you should
68//! define at least one image type of a suitable size and implement `Render`
69//! for it.
70//!
71//!
72//! ## Frames
73//!
74//! Types implementing [`Render`] are used to update a [`Frame`] (which is in
75//! turn passed to a [`Display`]).
76//!
77//! A `Frame` instance is a 'compiled' representation of a greyscale image of
78//! the size required by the display, in a form that's more directly usable by
79//! the display code.
80//!
81//! This is exposed in the public API so that you can construct the `Frame`
82//! representation in code running at a low priority. Then only
83//! [`Display::set_frame()`] has to be called in code that can't be interrupted
84//! by the display timer.
85//!
86//! You must supply an implementation of the [`Frame`] trait.
87//!
88//!
89//! ## Timer control
90//!
91//! The `Display` expects to control a single timer which will generate
92//! interrupts at appropriate times. [`Display::handle_event()`] is intended to
93//! be called from these interrupts.
94//!
95//! You must supply an implementation of the [`DisplayTimer`] trait providing
96//! the interface that the `Display` needs to control the timer.
97//!
98//! The [`DisplayTimer`] implementation determines the refresh rate for the
99//! display.
100//!
101//! The `Display` requests an interrupt for the point in time when the next
102//! row is due to be lit. When rendering greyscale images, it requests
103//! additional interrupts within each row's time period. It only requests
104//! interrupts for the greyscale levels which are required for what's
105//! currently being displayed.
106//!
107//!
108//! ## LED control
109//!
110//! The `Display` expects to be able to light an arbitrary subset of the LEDs in
111//! a given matrix row.
112//!
113//! You must supply an implementation of the [`DisplayControl`] trait to provide
114//! the interface that it needs.
115//!
116//!
117//! # Using the library
118//!
119//! ## Display
120//!
121//! A [`Display`] instance controls the LEDs and programs a timer. There will
122//! normally be a single `Display` instance in a program using this library.
123//!
124//! `Display` is generic over a type implementing [`Frame`], which in turn
125//! determines the [`Matrix`] in use.
126//!
127//! ## Putting it together
128//!
129//! Once you have provided implementations of all the necessary traits, you
130//! can use this library as follows:
131//!
132//! When your program starts, call [`initialise_control()`] (passing it the
133//! device implementing `DisplayControl`) and [`initialise_timer()`] (passing
134//! it the device implementing `DisplayTimer`), and create a [`Display`] using
135//! your [`Frame`] type.
136//!
137//! In an interrupt handler for the timer you used for `initialise_timer()`,
138//! call [`Display::handle_event()`], passing it the same two devices.
139//!
140//! To display an image: create a [`Frame`] instance, use [`Frame::set()`] to
141//! put the image in it, then call [`Display::set_frame()`].
142//!
143//! You can call `set_frame()` at any time, so long as you're not
144//! interrupting, or interruptable by, `handle_event()`.
145//!
146//! Once you've called `set_frame()`, you are free to reuse the `Frame`
147//! instance.
148//!
149//!
150//! [micropython]: https://microbit-micropython.readthedocs.io/
151
152
153mod control;
154mod display;
155mod timer;
156mod render;
157
158pub use control::DisplayControl;
159pub use display::{RowPlan, Matrix, Frame, Display, Event,
160 initialise_timer, initialise_control,
161};
162pub use timer::DisplayTimer;
163pub use render::{BRIGHTNESSES, MAX_BRIGHTNESS, Render};