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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Provides userspace access to 7 segment digit displays.
//!
//! This capsule was developed using the following components:
//! - Microbit_v2
//! - Edge Connector Breakout Board for Microbit (PPMB00126)
//! - 7 segment display with 4 digits (3461BS-1)
//! - breadboard, 220 ohms resistances and jump wires
//!
//! Usage
//! -----
//!
//! Example of use for a display with 4 digits and the Microbit:
//! Microbit Pins: <https://tech.microbit.org/hardware/schematic/>
//! 4 digit 7 segment display pinout: <https://www.dotnetlovers.com/images/4digit7segmentdisplay85202024001AM.jpg>
//!
//! ```rust,ignore
//! const NUM_DIGITS: usize = 4;
//! const DIGITS: [Pin; 4] = [Pin::P1_02, Pin::P0_12, Pin::P0_30, Pin::P0_09]; // [D1, D2, D3, D4]
//! const SEGMENTS: [Pin; 7] = [
//!     Pin::P0_02, // A
//!     Pin::P0_03, // B
//!     Pin::P0_04, // C
//!     Pin::P0_31, // D
//!     Pin::P0_28, // E
//!     Pin::P0_10, // F
//!     Pin::P1_05, // G
//! ];
//! const DOT: Pin = Pin::P0_11;
//!
//! let segment_array = static_init!(
//!     [&'static nrf52::gpio::GPIOPin<'static>; 8],
//!     [
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[SEGMENTS[0]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[SEGMENTS[1]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[SEGMENTS[2]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[SEGMENTS[3]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[SEGMENTS[4]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[SEGMENTS[5]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[SEGMENTS[6]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[DOT]
//!         ),
//!     ]
//! );
//!
//! let digit_array = static_init!(
//!     [&'static nrf52::gpio::GPIOPin<'static>; 4],
//!     [
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[DIGITS[0]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[DIGITS[1]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[DIGITS[2]]
//!         ),
//!         static_init!(
//!             &'static nrf52::gpio::GPIOPin<'static>,
//!             &nrf52833_peripherals.gpio_port[DIGITS[3]]
//!         ),
//!     ]
//! );
//!
//! let buffer = static_init!([u8; 4], [0; 4]);
//!
//! let digit_display = static_init!(
//!     capsules::digits::DigitsDriver<
//!         'static,
//!         nrf52::gpio::GPIOPin<'static>,
//!         capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52::rtc::Rtc<'static>>,
//!     >,
//!     capsules::digits::DigitsDriver::new(
//!         segment_array,
//!         digit_array,
//!         buffer,
//!         virtual_alarm_digit,
//!         kernel::hil::gpio::ActivationMode::ActiveLow,
//!         kernel::hil::gpio::ActivationMode::ActiveHigh,
//!         60
//!     ),
//! );
//! ```
//!
//! virtual_alarm_digit.set_alarm_client(digit_display);
//!
//! digit_display.init();
//!
//!
//! Syscall Interface
//! -----------------
//!
//! ### Command
//!
//! All operations are synchronous, so this capsule only uses the `command`
//! syscall.
//!
//! #### `command_num`
//!
//! - `0`: Driver Check.
//!   - `data1`: Unused.
//!   - `data2`: Unused.
//!   - Return: Number of digits.
//! - `1`: Prints one digit at the requested position.
//!   - `data1`: The position of the digit. Starts at 1.
//!   - `data2`: The digit to be represented, from 0 to 9.
//!   - Return: `Ok(())` if the digit index was valid, `INVAL` otherwise.
//! - `2`: Clears all digits currently being displayed.
//!   - `data1`: Unused.
//!   - `data2`: Unused.
//! - `3`: Print a dot at the requested digit position.
//!   - `data1`: The position of the dot. Starts at 1.
//!   - Return: `Ok(())` if the index was valid, `INVAL` otherwise.
//! - `4`: Print a custom pattern for a digit on a certain position.
//!   - `data1`: The position of the digit. Starts at 1.
//!   - `data2`: The custom pattern to be represented.
//!   - Return: `Ok(())` if the index was valid, `INVAL` otherwise.
//! - `5`: Return the number of digits on the display being used.
//!   - `data1`: Unused.
//!   - `data2`: Unused.

use core::cell::Cell;

use kernel::hil::gpio::{ActivationMode, Pin};
use kernel::hil::time::{Alarm, AlarmClient, ConvertTicks};
use kernel::syscall::{CommandReturn, SyscallDriver};
use kernel::utilities::cells::TakeCell;
use kernel::ErrorCode;
use kernel::ProcessId;

/// Syscall driver number.
use capsules_core::driver;
pub const DRIVER_NUM: usize = driver::NUM::SevenSegment as usize;

/// Digit patterns
//
//      A
//      _
//   F |_| B       center = G
//   E |_| C . Dp
//      D
//
const DIGITS: [u8; 10] = [
    // pattern: 0bDpGFEDCBA
    0b00111111, // 0
    0b00000110, // 1
    0b01011011, // 2
    0b01001111, // 3
    0b01100110, // 4
    0b01101101, // 5
    0b01111101, // 6
    0b00100111, // 7
    0b01111111, // 8
    0b01101111, // 9
];

/// Holds an array of digits and an array of segments for each digit.

pub struct SevenSegmentDriver<'a, P: Pin, A: Alarm<'a>, const NUM_DIGITS: usize> {
    /// An array of 8 segment pins (7 for digit segments and one dot segment)
    segments: &'a [&'a P; 8],
    /// An array of `NUM_DIGITS` digit pins, each one corresponding to one digit on the display
    /// For each digit selected, a pattern of lit and unlit segments will be represented
    digits: &'a [&'a P; NUM_DIGITS],
    /// A buffer which contains the patterns displayed for each digit
    /// Each element of the buffer array represents the pattern for one digit, and
    /// is a sequence of bits that have the value 1 for a lit segment and the value 0 for
    /// an unlit segment.
    buffer: TakeCell<'a, [u8; NUM_DIGITS]>,
    alarm: &'a A,
    current_digit: Cell<usize>,
    /// How fast the driver should switch between digits (ms)
    timing: u8,
    segment_activation: ActivationMode,
    digit_activation: ActivationMode,
}

impl<'a, P: Pin, A: Alarm<'a>, const NUM_DIGITS: usize> SevenSegmentDriver<'a, P, A, NUM_DIGITS> {
    pub fn new(
        segments: &'a [&'a P; 8],
        digits: &'a [&'a P; NUM_DIGITS],
        buffer: &'a mut [u8; NUM_DIGITS],
        alarm: &'a A,
        segment_activation: ActivationMode,
        digit_activation: ActivationMode,
        refresh_rate: usize,
    ) -> Self {
        // Check if the buffer has enough space to hold patterns for all digits
        if (buffer.len() * 8) < segments.len() * digits.len() {
            panic!("Digits Driver: provided buffer is too small");
        }

        Self {
            segments,
            digits,
            buffer: TakeCell::new(buffer),
            alarm,
            segment_activation,
            digit_activation,
            current_digit: Cell::new(0),
            timing: (1000 / (refresh_rate * digits.len())) as u8,
        }
    }

    /// Initialize the digit and segment pins.
    /// Does not override pins if they have already been initialized for another driver.
    pub fn init(&self) {
        for segment in self.segments {
            segment.make_output();
            self.segment_clear(segment);
        }

        for digit in self.digits {
            digit.make_output();
            self.digit_clear(digit);
        }

        self.next_digit();
    }

    /// Returns the number of digits on the display.
    pub fn digits_len(&self) -> usize {
        self.digits.len()
    }

    /// Represents each digit with its corresponding pattern.
    fn next_digit(&self) {
        self.digit_clear(self.digits[self.current_digit.get()]);
        self.current_digit
            .set((self.current_digit.get() + 1) % self.digits.len());
        self.buffer.map(|bits| {
            for segment in 0..self.segments.len() {
                let location = self.current_digit.get() * self.segments.len() + segment;
                if (bits[location / 8] >> (location % 8)) & 0x1 == 1 {
                    self.segment_set(self.segments[segment]);
                } else {
                    self.segment_clear(self.segments[segment]);
                }
            }
        });
        self.digit_set(self.digits[self.current_digit.get()]);
        let interval = self.alarm.ticks_from_ms(self.timing as u32);
        self.alarm.set_alarm(self.alarm.now(), interval);
    }

    fn segment_set(&self, p: &P) {
        match self.segment_activation {
            ActivationMode::ActiveHigh => p.set(),
            ActivationMode::ActiveLow => p.clear(),
        }
    }

    fn segment_clear(&self, p: &P) {
        match self.segment_activation {
            ActivationMode::ActiveHigh => p.clear(),
            ActivationMode::ActiveLow => p.set(),
        }
    }

    fn digit_set(&self, p: &P) {
        match self.digit_activation {
            ActivationMode::ActiveHigh => p.set(),
            ActivationMode::ActiveLow => p.clear(),
        }
    }

    fn digit_clear(&self, p: &P) {
        match self.digit_activation {
            ActivationMode::ActiveHigh => p.clear(),
            ActivationMode::ActiveLow => p.set(),
        }
    }

    /// Sets the pattern for the digit on the requested position.
    fn print_digit(&self, position: usize, digit: usize) -> Result<(), ErrorCode> {
        if position <= self.digits.len() {
            self.buffer.map(|bits| bits[position - 1] = DIGITS[digit]);
            Ok(())
        } else {
            Err(ErrorCode::INVAL)
        }
    }

    /// Clears all digits currently being displayed.
    fn clear_digits(&self) -> Result<(), ErrorCode> {
        self.buffer.map(|bits| {
            for index in 0..self.digits.len() {
                bits[index] = 0;
            }
        });
        Ok(())
    }

    /// Prints a dot at the requested digit position.
    fn print_dot(&self, position: usize) -> Result<(), ErrorCode> {
        if position <= self.digits.len() {
            self.buffer.map(|bits| {
                // set the first bit of the digit on this position
                bits[position - 1] |= 1 << (self.segments.len() - 1);
            });
            Ok(())
        } else {
            Err(ErrorCode::INVAL)
        }
    }

    /// Prints a custom pattern at a requested position.
    fn print(&self, position: usize, pattern: u8) -> Result<(), ErrorCode> {
        if position <= self.digits.len() {
            self.buffer.map(|bits| {
                bits[position - 1] = pattern;
            });
            Ok(())
        } else {
            Err(ErrorCode::INVAL)
        }
    }
}

impl<'a, P: Pin, A: Alarm<'a>, const NUM_DIGITS: usize> AlarmClient
    for SevenSegmentDriver<'a, P, A, NUM_DIGITS>
{
    fn alarm(&self) {
        self.next_digit();
    }
}

impl<'a, P: Pin, A: Alarm<'a>, const NUM_DIGITS: usize> SyscallDriver
    for SevenSegmentDriver<'a, P, A, NUM_DIGITS>
{
    /// Control the digit display.
    ///
    /// ### `command_num`
    ///
    /// - `0`: Driver existence check.
    /// - `1`: Prints one digit at the requested position. Returns `INVAL` if the
    ///        position is not valid.
    /// - `2`: Clears all digits currently being displayed.
    /// - `3`: Print a dot at the requested digit position. Returns
    ///        `INVAL` if the position is not valid.
    /// - `4`: Print a custom pattern for a certain digit. Returns
    ///        `INVAL` if the position is not valid.
    /// - `5`: Returns the number of digits on the display. This will always be 0 or
    ///        greater, and therefore also allows for checking for this driver.
    fn command(
        &self,
        command_num: usize,
        data1: usize,
        data2: usize,
        _: ProcessId,
    ) -> CommandReturn {
        match command_num {
            // Check existence
            0 => CommandReturn::success(),

            // Print one digit
            1 => CommandReturn::from(self.print_digit(data1, data2)),

            // Clear all digits
            2 => CommandReturn::from(self.clear_digits()),

            // Print dot
            3 => CommandReturn::from(self.print_dot(data1)),

            // Print a custom pattern
            4 => CommandReturn::from(self.print(data1, data2 as u8)),

            // Return number of digits
            5 => CommandReturn::success_u32(self.digits.len() as u32),

            // default
            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
        }
    }

    fn allocate_grant(&self, _processid: ProcessId) -> Result<(), kernel::process::Error> {
        Ok(())
    }
}