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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
//! Low-level control of the micro:bit's 5×5 LED display.
//!
//! Provides an implementation of [`DisplayControl`].
//!
//! [`DisplayControl`]: tiny_led_matrix::DisplayControl

use crate::nrf51;
use tiny_led_matrix::DisplayControl;
use crate::gpio::DisplayPins;
use pin_constants::*;


/// Constants identifying GPIO pins used in the LED matrix.
///
/// These constants and convenience functions may be used when working
/// directly with a [`DisplayPort`].
pub mod pin_constants {
    const fn bit_range(lo: usize, count: usize) -> u32 {
        ((1<<count) - 1) << lo
    }

    /// The number of column pins (9).
    pub const MATRIX_COLS : usize = 9;

    /// Number in the GPIO port of the first column pin
    pub const FIRST_COL_PIN : usize = 4;
    /// Number in the GPIO port of the last column pin
    pub const LAST_COL_PIN : usize = FIRST_COL_PIN + MATRIX_COLS - 1;
    /// u32 bitmask representing the GPIO port numbers of the column pins
    pub const COL_PINS_MASK : u32 = bit_range(FIRST_COL_PIN, MATRIX_COLS);

    /// The number of row pins (3).
    pub const MATRIX_ROWS : usize = 3;
    /// Number in the GPIO port of the first row pin
    pub const FIRST_ROW_PIN : usize = 13;
    /// Number in the GPIO port of the last row pin
    pub const LAST_ROW_PIN : usize = FIRST_ROW_PIN + MATRIX_ROWS - 1;
    /// u32 bitmask representing the GPIO port numbers of the row pins
    pub const ROW_PINS_MASK : u32 = bit_range(FIRST_ROW_PIN, MATRIX_ROWS);

    /// Returns the number in the GPIO port of the specified matrix column pin.
    ///
    /// `col` should be in 0..9 .
    pub const fn col_pin_number(col: usize) -> u32 {
        (FIRST_COL_PIN + col) as u32
    }

    /// Returns the number in the GPIO port of the specified matrix row pin.
    ///
    /// `row` should be in 0..3 .
    pub const fn row_pin_number(row: usize) -> u32 {
        (FIRST_ROW_PIN + row) as u32
    }
}


/// Write access to the GPIO pins connected to the 5×5 LED display.
///
/// `DisplayPort` permits writing to the display's GPIO pins and ignores
/// requests to write to any other part of the GPIO port.
///
/// `DisplayPort` implements the [`DisplayControl`] trait, so it can be used
/// with a [`Display`].
///
/// To light an LED, set its row pin and clear its column pin.
///
/// Use the [`pin_constants`] to find the GPIO pin numbers for each row
/// and column.
///
/// See the [DAL documentation] for how these rows and columns correspond to
/// the physical LED layout.
///
/// # Example
///
/// ```
/// use rmicrobit::prelude::*;
/// use rmicrobit::gpio::PinsByKind;
/// use rmicrobit::pin_constants::{col_pin_number, row_pin_number, COL_PINS_MASK};
/// let p: nrf51::Peripherals = _;
/// let PinsByKind {display_pins, ..} = p.GPIO.split_by_kind();
/// let mut display_port = DisplayPort::new(display_pins);
/// // Row whose third column is the top-right led
/// const UPPER_RIGHT_ROW : u32 = row_pin_number(0);
/// // Row whose third column is the bottom-left led
/// const LOWER_LEFT_ROW : u32 = row_pin_number(2);
///
/// // Set all cols except the third high
/// display_port.set(COL_PINS_MASK ^ 1<<col_pin_number(2));
///
/// // Light the top-right LED
/// display_port.set(1<<UPPER_RIGHT_ROW);
/// // (sleep a short time here)
/// // Clear the top-right LED
/// display_port.clear(1<<UPPER_RIGHT_ROW);
/// // (sleep a short time here)
///
/// // Light the bottom-left LED
/// display_port.set(1<<LOWER_LEFT_ROW);
/// // (sleep a short time here)
/// // Clear the bottom-left LED
/// display_port.clear(1<<LOWER_LEFT_ROW);
/// // (sleep a short time here)
/// ```
///
/// A runnable example is available as `examples/use_display_port.rs`.
///
/// [`Display`]: tiny_led_matrix::Display
/// [`DisplayControl`]: tiny_led_matrix::DisplayControl
/// [DAL documentation]: https://lancaster-university.github.io/microbit-docs/ubit/display/
pub struct DisplayPort(DisplayPins);

impl DisplayPort {

    /// Takes ownership of the display's GPIO pins and returns a `DisplayPort`.
    ///
    /// Sets the pins to output mode.
    ///
    /// Sets all the pins low (blanking the display).
    // Note we never call any methods on the nrf51-hal Pins held in
    // DisplayPins; we just use them as a token proving that nothing else is
    // talking to this part of the GPIO space.
    pub fn new(pins: DisplayPins) -> DisplayPort {
        let mut port = DisplayPort(pins);
        port.reset();
        port.blank();
        port
    }

    /// Gives the underlying `DisplayPins` instance back.
    pub fn free(self) -> DisplayPins {
        self.0
    }

    /// Sets all the pins to output mode.
    fn reset(&mut self) {
        // NOTE(unsafe) writes restricted to pins we own.
        unsafe {
            let gpio = &*nrf51::GPIO::ptr();
            for ii in FIRST_COL_PIN ..= LAST_COL_PIN {
                gpio.pin_cnf[ii].write(|w| w.dir().output());
            }
            for ii in FIRST_ROW_PIN ..= LAST_ROW_PIN {
                gpio.pin_cnf[ii].write(|w| w.dir().output());
            }
        }
    }

    /// Sets the specified pins high, leaving the others unchanged.
    ///
    /// The u32 `pins` parameter is a bitmask representing the set of pins to
    /// affect: a 1 in bit position *n* says to set GPIO pin *n*.
    ///
    /// Bits in `pins` not representing row or column pins are ignored.
    pub fn set(&mut self, pins: u32) {
        let to_set = pins & (ROW_PINS_MASK | COL_PINS_MASK);
        // NOTE(unsafe) writes restricted to affecting pins we own.
        unsafe {
            let gpio = &*nrf51::GPIO::ptr();
            gpio.outset.write(|w| { w.bits(to_set) });
        }
    }

    /// Sets the specified pins low, leaving the others unchanged.
    ///
    /// The u32 `pins` parameter is a bitmask representing the set of pins to
    /// affect: a 1 in bit position *n* says to clear GPIO pin *n*.
    ///
    /// Bits in `pins` not representing row or column pins are ignored.
    pub fn clear(&mut self, pins: u32) {
        let to_clear = pins & (ROW_PINS_MASK | COL_PINS_MASK);
        // NOTE(unsafe) writes restricted to affecting pins we own.
        unsafe {
            let gpio = &*nrf51::GPIO::ptr();
            gpio.outclr.write(|w| { w.bits(to_clear) });
        }
    }

    /// Sets all pins low, blanking the display.
    pub fn blank(&mut self) {
        self.clear(ROW_PINS_MASK | COL_PINS_MASK);
    }
}


/// Implementation of [`DisplayControl`] for the micro:bit's GPIO peripheral.
///
/// This controls the micro:bit's 5×5 LED display.
///
/// [`DisplayControl`]: tiny_led_matrix::DisplayControl
impl DisplayControl for DisplayPort {

    fn initialise_for_display(&mut self) {
        // Do nothing: new() has set the pin direction.
    }

    fn display_row_leds(&mut self, row: usize, cols: u32) {
        // In `cols`, the least-significant bit represents column 0, and so on.
        // To light an LED, we set the row bit and clear the col bit.
        let rows_to_set = 1<<(FIRST_ROW_PIN+row);
        let rows_to_clear = ROW_PINS_MASK ^ rows_to_set;
        let cols_to_clear = cols << FIRST_COL_PIN;
        let cols_to_set = COL_PINS_MASK ^ cols_to_clear;

        self.set(rows_to_set | cols_to_set);
        self.clear(rows_to_clear | cols_to_clear);
    }

    fn light_current_row_leds(&mut self, cols: u32) {
        // In `cols`, the least-significant bit represents column 0, and so on.
        self.clear(cols << FIRST_COL_PIN)
    }

}