capsules_extra/
led_matrix.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Service capsule for access to LEDs on a LED matrix.
6//!
7//! Usage
8//! -----
9//!
10//! ```rust,ignore
11//! let led_matrix = components::led_matrix_component_helper!(
12//!     nrf52833::gpio::GPIOPin,
13//!     nrf52::rtc::Rtc<'static>,
14//!     mux_alarm,
15//!     @fps => 60,
16//!     @cols => kernel::hil::gpio::ActivationMode::ActiveLow,
17//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_COLS[0]],
18//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_COLS[1]],
19//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_COLS[2]],
20//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_COLS[3]],
21//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_COLS[4]],
22//!     @rows => kernel::hil::gpio::ActivationMode::ActiveHigh,
23//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_ROWS[0]],
24//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_ROWS[1]],
25//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_ROWS[2]],
26//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_ROWS[3]],
27//!         &nrf52833_peripherals.gpio_port[LED_MATRIX_ROWS[4]]
28//!
29//! )
30//! .finalize(components::led_matrix_component_buf!(
31//!     nrf52833::gpio::GPIOPin,
32//!     nrf52::rtc::Rtc<'static>
33//! ));
34//!
35//! let led = static_init!(
36//!     capsules::led::LedDriver<
37//!         'static,
38//!         capsules::led_matrix::LedMatrixLed<
39//!             'static,
40//!             nrf52::gpio::GPIOPin<'static>,
41//!             capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52::rtc::Rtc<'static>>,
42//!         >,
43//!         25,
44//!     >,
45//!     capsules::led::LedDriver::new(components::led_matrix_leds!(
46//!         nrf52::gpio::GPIOPin<'static>,
47//!         capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52::rtc::Rtc<'static>>,
48//!         led_matrix,
49//!         (0, 0),
50//!         (1, 0),
51//!         (2, 0),
52//!         (3, 0),
53//!         (4, 0),
54//!         (0, 1),
55//!         (1, 1),
56//!         (2, 1),
57//!         (3, 1),
58//!         (4, 1),
59//!         (0, 2),
60//!         (1, 2),
61//!         (2, 2),
62//!         (3, 2),
63//!         (4, 2),
64//!         (0, 3),
65//!         (1, 3),
66//!         (2, 3),
67//!         (3, 3),
68//!         (4, 3),
69//!         (0, 4),
70//!         (1, 4),
71//!         (2, 4),
72//!         (3, 4),
73//!         (4, 4)
74//!     )),
75//! );
76
77use core::cell::Cell;
78
79use kernel::utilities::cells::TakeCell;
80use kernel::ErrorCode;
81
82use kernel::hil::gpio::{ActivationMode, Pin};
83use kernel::hil::led::Led;
84use kernel::hil::time::{Alarm, AlarmClient, ConvertTicks};
85
86/// Holds the array of LEDs and implements a `Driver` interface to
87/// control them.
88pub struct LedMatrixDriver<'a, L: Pin, A: Alarm<'a>> {
89    cols: &'a [&'a L],
90    rows: &'a [&'a L],
91    buffer: TakeCell<'a, [u8]>,
92    alarm: &'a A,
93    current_row: Cell<usize>,
94    timing: u8,
95    row_activation: ActivationMode,
96    col_activation: ActivationMode,
97}
98
99impl<'a, L: Pin, A: Alarm<'a>> LedMatrixDriver<'a, L, A> {
100    pub fn new(
101        cols: &'a [&'a L],
102        rows: &'a [&'a L],
103        buffer: &'a mut [u8],
104        alarm: &'a A,
105        col_activation: ActivationMode,
106        row_activation: ActivationMode,
107        refresh_rate: usize,
108    ) -> Self {
109        // Initialize all LEDs and turn them off
110        if (buffer.len() * 8) < cols.len() * rows.len() {
111            panic!("Matrix LED Driver: provided buffer is too small");
112        }
113
114        Self {
115            cols,
116            rows,
117            buffer: TakeCell::new(buffer),
118            alarm,
119            col_activation,
120            row_activation,
121            current_row: Cell::new(0),
122            timing: (1000 / (refresh_rate * rows.len())) as u8,
123        }
124    }
125
126    pub fn init(&self) {
127        for led in self.cols {
128            led.make_output();
129            self.col_clear(led);
130        }
131
132        for led in self.rows {
133            led.make_output();
134            self.row_clear(led);
135        }
136        self.next_row();
137    }
138
139    pub fn cols_len(&self) -> usize {
140        self.cols.len()
141    }
142
143    pub fn rows_len(&self) -> usize {
144        self.rows.len()
145    }
146
147    fn next_row(&self) {
148        self.row_clear(self.rows[self.current_row.get()]);
149        self.current_row
150            .set((self.current_row.get() + 1) % self.rows.len());
151        self.buffer.map(|bits| {
152            for led in 0..self.cols.len() {
153                let pos = self.current_row.get() * self.cols.len() + led;
154                if (bits[pos / 8] >> (pos % 8)) & 0x1 == 1 {
155                    self.col_set(self.cols[led]);
156                } else {
157                    self.col_clear(self.cols[led]);
158                }
159            }
160        });
161        self.row_set(self.rows[self.current_row.get()]);
162        let interval = self.alarm.ticks_from_ms(self.timing as u32);
163        self.alarm.set_alarm(self.alarm.now(), interval);
164    }
165
166    fn col_set(&self, l: &L) {
167        match self.col_activation {
168            ActivationMode::ActiveHigh => l.set(),
169            ActivationMode::ActiveLow => l.clear(),
170        }
171    }
172
173    fn col_clear(&self, l: &L) {
174        match self.col_activation {
175            ActivationMode::ActiveHigh => l.clear(),
176            ActivationMode::ActiveLow => l.set(),
177        }
178    }
179
180    fn row_set(&self, l: &L) {
181        match self.row_activation {
182            ActivationMode::ActiveHigh => l.set(),
183            ActivationMode::ActiveLow => l.clear(),
184        }
185    }
186
187    fn row_clear(&self, l: &L) {
188        match self.row_activation {
189            ActivationMode::ActiveHigh => l.clear(),
190            ActivationMode::ActiveLow => l.set(),
191        }
192    }
193
194    pub fn on(&self, col: usize, row: usize) -> Result<(), ErrorCode> {
195        self.on_index(row * self.rows.len() + col)
196    }
197
198    fn on_index(&self, led_index: usize) -> Result<(), ErrorCode> {
199        if led_index < self.rows.len() * self.cols.len() {
200            self.buffer
201                .map(|bits| bits[led_index / 8] |= 1 << (led_index % 8));
202            Ok(())
203        } else {
204            Err(ErrorCode::INVAL)
205        }
206    }
207
208    pub fn off(&self, col: usize, row: usize) -> Result<(), ErrorCode> {
209        self.off_index(row * self.rows.len() + col)
210    }
211
212    fn off_index(&self, led_index: usize) -> Result<(), ErrorCode> {
213        if led_index < self.rows.len() * self.cols.len() {
214            self.buffer
215                .map(|bits| bits[led_index / 8] &= !(1 << (led_index % 8)));
216            Ok(())
217        } else {
218            Err(ErrorCode::INVAL)
219        }
220    }
221
222    pub fn toggle(&self, col: usize, row: usize) -> Result<(), ErrorCode> {
223        self.toggle_index(row * self.rows.len() + col)
224    }
225
226    fn toggle_index(&self, led_index: usize) -> Result<(), ErrorCode> {
227        if led_index < self.rows.len() * self.cols.len() {
228            self.buffer
229                .map(|bits| bits[led_index / 8] ^= 1 << (led_index % 8));
230            Ok(())
231        } else {
232            Err(ErrorCode::INVAL)
233        }
234    }
235
236    fn read(&self, col: usize, row: usize) -> Result<bool, ErrorCode> {
237        if row < self.rows.len() && col < self.cols.len() {
238            let pos = row * self.rows.len() + col;
239            self.buffer.map_or(Err(ErrorCode::FAIL), |bits| {
240                match bits[pos / 8] & (1 << (pos % 8)) {
241                    0 => Ok(false),
242                    _ => Ok(true),
243                }
244            })
245        } else {
246            Err(ErrorCode::INVAL)
247        }
248    }
249}
250
251impl<'a, L: Pin, A: Alarm<'a>> AlarmClient for LedMatrixDriver<'a, L, A> {
252    fn alarm(&self) {
253        self.next_row();
254    }
255}
256
257// one Led from the matrix
258pub struct LedMatrixLed<'a, L: Pin, A: Alarm<'a>> {
259    matrix: &'a LedMatrixDriver<'a, L, A>,
260    row: usize,
261    col: usize,
262}
263
264impl<'a, L: Pin, A: Alarm<'a>> LedMatrixLed<'a, L, A> {
265    pub fn new(matrix: &'a LedMatrixDriver<'a, L, A>, col: usize, row: usize) -> Self {
266        if col >= matrix.cols_len() || row >= matrix.rows_len() {
267            panic!("LED at position ({}, {}) does not exist", col, row);
268        }
269        LedMatrixLed { matrix, row, col }
270    }
271}
272
273impl<'a, L: Pin, A: Alarm<'a>> Led for LedMatrixLed<'a, L, A> {
274    fn init(&self) {}
275
276    fn on(&self) {
277        let _ = self.matrix.on(self.col, self.row);
278    }
279
280    fn off(&self) {
281        let _ = self.matrix.off(self.col, self.row);
282    }
283
284    fn toggle(&self) {
285        let _ = self.matrix.toggle(self.col, self.row);
286    }
287
288    fn read(&self) -> bool {
289        self.matrix.read(self.col, self.row).unwrap_or(false)
290    }
291}