1use 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
86pub 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        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
257pub 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}