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