Module microbit::display[][src]

Expand description

Non-blocking support for the 5×5 LED display.

Scope

Together with tiny-led-matrix, this module provides:

  • support for driving the LED display from a timer interrupt
  • ten levels of brightness for each LED
  • simple 5×5 greyscale and black-and-white image types.

The module doesn’t define interrupt handlers directly; instead it provides a function to be called from a timer interrupt. It knows how to program one of the micro:bit’s timers to provide that interrupt.

Example

This shows general usage but is not a working example. For a working exaple see examples/led_nonblocking.rs.

use microbit::{
    display_pins,
    pac,
    display::{self, Frame, image::GreyscaleImage},
    hal::{self, gpio::{p0::Parts as P0Parts, p1::Parts as P1Parts}},
};
use embedded_hal::blocking::delay::DelayMs;

let p = pac::Peripherals::take().unwrap();

let mut timer = display::MicrobitDisplayTimer::new(p.TIMER1);
let mut pins = {
   let p0parts = P0Parts::new(p.P0);
   let p1parts = P1Parts::new(p.P1);
   display_pins!(p0parts, p1parts)
};

let mut display = display::Display::new();

// in your main function
{
    display::initialise_display(&mut timer, &mut pins);
    // This will probably need to be a static
    let mut FRAME = display::MicrobitFrame::const_default();

    let mut timer2 = hal::Timer::new(p.TIMER0);
    loop {
        FRAME.set(&GreyscaleImage::new(&[
            [0, 7, 0, 7, 0],
            [7, 0, 7, 0, 7],
            [7, 0, 0, 0, 7],
            [0, 7, 0, 7, 0],
            [0, 0, 7, 0, 0],
        ]));
        display.set_frame(&FRAME);
        timer2.delay_ms(1000u32);

        FRAME.set(&GreyscaleImage::new(&[
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
        ]));
        display.set_frame(&FRAME);
        timer2.delay_ms(1000u32);
    }
}

// in a timer interrupt
{
    display::handle_display_event(&mut display, &mut timer, &mut pins);
}

Coordinate system

The LEDs are identified using (x,y) coordinates as follows:

(0,0) ... (4,0)
 ...  ...  ...
(0,4) ... (4,4)

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 timings as used by the micro:bit MicroPython port (this is different to the 0 to 255 scale used by the micro:bit runtime).

The time slice for each level above 1 is approximately 1.9× the slice for the previous level.

An LED with brightness 9 is lit for one third of the time (because internally there are three ‘rows’ of LEDs which have to be addressed one at a time).

Images and Render

The Render trait defines the interface that an image-like type needs to provide in order to be displayed.

It contains a single function: brightness_at(x, y), returning a brightness level.

The image submodule provides two static image types implementing Render:

  • GreyscaleImage, allowing all 9 levels (using one byte for each LED)
  • BitImage, allowing only ‘on’ and ‘off’ (using five bytes)

Display

A Display instance controls the LEDs and programs a timer. There should normally be a single Display instance in the program.

Frames

Types implementing Render aren’t used directly with the Display; instead they’re used to update a MicrobitFrame instance which is in turn passed to the Display.

A MicrobitFrame instance is a ‘compiled’ representation of a 5×5 greyscale image, 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 MicrobitFrame 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.

Timer integration

The Display expects to control a single timer. It can use the micro:bit’s TIMER0, TIMER1, or TIMER2.

This uses a 6ms period to light each of the three internal LED rows, so that the entire display is updated every 18ms.

When rendering greyscale images, the Display requests extra interrupts within each 6ms period. It only requests interrupts for the greyscale levels which are actually required for what’s currently being displayed.

Technical details

The timer is set to 16-bit mode, using a 62.5kHz clock (16 µs ticks). It resets every 375 ticks.

Usage

Choose a timer to drive the display from (TIMER0, TIMER1, or TIMER2).

When your program starts:

In an interrupt handler for the timer, call handle_display_event().

To change what’s displayed: create a MicrobitFrame instance, use .set() to put an image (something implementing Render) in it, then call Display::set_frame(). Note you’ll have to use microbit::display::Frame to make set() available.

You can call set_frame() at any time, so long as you’re not interrupting, or interruptable by, handle_display_event().

Once you’ve called set_frame(), you are free to reuse the MicrobitFrame.

See led_rtfm example for a complete working example.

Modules

image

Static 5×5 greyscale and black-and-white images.

Structs

Display

Manages a small LED display.

MicrobitDisplayTimer

A TIMER peripheral programmed to manage the display.

MicrobitFrame

A ‘Compiled’ representation of a 5×5 image to be displayed.

Constants

MAX_BRIGHTNESS

The maximum brightness level for greyscale images (ie, 9; the minimum is 0).

Traits

Frame

A ‘Compiled’ representation of an image to be displayed.

Render

A trait providing the information that Display needs to render an image.

Functions

handle_display_event

Updates the LEDs and timer state during a timer interrupt.

initialise_display

Initialises the micro:bit hardware to use the display driver.