segment_display/
lib.rs

1#![no_std]
2
3//! A platform agnostic driver to interface with 7-segments displays
4//! connected to shift registers
5//! 
6//! This is work-in-progress!
7//!
8//! Example
9//!
10//! ```no_run
11//!
12//!#![no_main]
13//!#![no_std]
14//!
15//!extern crate cortex_m;
16//!extern crate cortex_m_rt;
17//!extern crate nucleo_f401re as board;
18//!extern crate panic_semihosting;
19//!
20//!use cortex_m_rt::entry;
21//!
22//!use board::hal::delay::Delay;
23//!use board::hal::prelude::*;
24//!use board::hal::stm32;
25//!use board::spi::{self, Spi};
26//!use cortex_m::peripheral::Peripherals;
27//!
28//!use segment_display::SegmentDisplay;
29//!
30//!
31//!#[entry]
32//!fn main() -> ! {
33//!    let device = stm32::Peripherals::take().unwrap();
34//!    let core = Peripherals::take().unwrap();
35//!
36//!    let rcc = device.RCC.constrain();
37//!    let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
38//!
39//!    let gpiob = device.GPIOB.split();
40//!    let sck = gpiob.pb3.into_alternate_af5();
41//!    let miso = spi::NoMiso;
42//!    let mosi = gpiob.pb5.into_alternate_af5();
43//!    let latch = gpiob.pb4.into_push_pull_output();
44//!
45//!    let spi = Spi::spi1(
46//!        device.SPI1,
47//!        (sck, miso, mosi),
48//!        spi::Mode { polarity: spi::Polarity::IdleHigh, phase: spi::Phase::CaptureOnFirstTransition, },
49//!        4_000_000.hz(),
50//!        clocks,
51//!    );
52//!
53//!    let mut segment_display = SegmentDisplay::new(spi, latch);
54//!    let mut delay = Delay::new(core.SYST, clocks);
55//!
56//!    segment_display.write_str("HELO");
57//!
58//!    loop {
59//!        segment_display.refresh().unwrap();
60//!        delay.delay_us(1000_u16);
61//!    }
62//!}
63//!
64//! ```
65
66use embedded_hal::blocking::delay::DelayUs;
67use embedded_hal::blocking::spi;
68use embedded_hal::digital::OutputPin;
69
70pub struct SegmentDisplay<SPI, PIN> {
71    back_buffer: [u8; 4],
72    spi: SPI,
73    latch_pin: PIN,
74    current_digit: usize,
75}
76
77impl<SPI, PIN> SegmentDisplay<SPI, PIN>
78where
79    SPI: spi::Write<u8>,
80    PIN: OutputPin,
81{
82    /// Create a new SegmentDisplay
83    pub fn new(spi: SPI, latch_pin: PIN) -> Self {
84        Self {
85            back_buffer: [0xff; 4],
86            spi,
87            latch_pin,
88            current_digit: 0,
89        }
90    }
91
92    /// Release the SegmentDisplay and the resources
93    pub fn release(self) -> (SPI, PIN) {
94        (self.spi, self.latch_pin)
95    }
96
97    /// Refresh the display. Needs to be called periodically with a sufficientlty hight frequenzy
98    /// otherwise the display will flicker.
99    pub fn refresh(&mut self) -> Result<(), SPI::Error> {
100        let segments_and_select: [u8; 2] = [
101            // The segments in digit to turn on/off
102            self.back_buffer[self.current_digit],
103            // The current display selector.
104            1 << (4 - 1 - self.current_digit),
105        ];
106
107        self.current_digit = (self.current_digit + 1) & 0b11;
108
109        self.latch_pin.set_low();
110        let res = self.spi.write(&segments_and_select)?;
111        self.latch_pin.set_high();
112
113        Ok(res)
114    }
115
116    pub fn refresh_with_delay<DELAY>(&mut self, delay: &mut DELAY) -> Result<(), SPI::Error>
117    where
118        DELAY: DelayUs<u16>,
119    {
120        let segments_and_select: [u8; 2] = [
121            // The segments in digit to turn on/off
122            self.back_buffer[self.current_digit],
123            // The current display selector.
124            1 << (4 - 1 - self.current_digit),
125        ];
126
127        self.current_digit = (self.current_digit + 1) & 0b11;
128
129        self.latch_pin.set_low();
130        let res = self.spi.write(&segments_and_select)?;
131        delay.delay_us(100);
132        self.latch_pin.set_high();
133
134        Ok(res)
135    }
136
137    /// Write characters to the display
138    pub fn write_chars(&mut self, buf: [char; 4]) {
139        for (i, c) in buf.iter().enumerate() {
140            self.back_buffer[i] = Self::char_to_segment_code(*c);
141        }
142    }
143
144    /// Write a string to the display
145    pub fn write_str(&mut self, s: &str) {
146
147        self.back_buffer.iter_mut().for_each(|b| *b = !0);
148
149        for (i, c) in s.chars().take(4).enumerate() {
150            self.back_buffer[i] = Self::char_to_segment_code(c);
151        }
152    }
153
154    /// Write a number to the display
155    pub fn write_number(&mut self, num: usize) {
156        let mut num = num;
157
158        if num > 9999 {
159            num = 9999;
160        }
161
162        for (i, div) in [1000, 100, 10].iter().enumerate() {
163            let digit;
164            if num >= i {
165                digit = num / div;
166                num -= div * digit;
167            } else {
168                digit = 0;
169            }
170            self.back_buffer[i] = NUMERALS[digit];
171        }
172
173        self.back_buffer[3] = NUMERALS[num];
174    }
175
176    fn char_to_segment_code(c: char) -> u8 {
177        if c.is_ascii_digit() {
178            let cb = c as u8;
179            let idx = cb - ('0' as u8);
180            NUMERALS[idx as usize]
181        } else if c.is_ascii_alphabetic() {
182            let cb = (c as u8) & !0x20; // Convert to uppercase
183            let idx = cb - ('A' as u8);
184            LETTERS[idx as usize]
185        } else {
186            // Symbols
187            match c {
188                ' ' => 0b1111_1111,
189                '-' => 0b1011_1111,
190                '_' => 0b1111_0111,
191                _   => 0b1111_1111,
192            }
193        }
194    }
195}
196
197//           A
198//          ===
199//      F ||   || B
200//          =G=
201//      E ||   || C
202//          ===
203//           D
204
205
206static NUMERALS: [u8; 10] = [
207    //.GFE_DCBA
208    0b1100_0000,    // 0
209    0b1111_1001,    // 1
210    0b1010_0100,    // 2
211    0b1011_0000,    // 3
212    0b1001_1001,    // 4
213    0b1001_0010,    // 5
214    0b1000_0010,    // 6
215    0b1111_1000,    // 7
216    0b1000_0000,    // 8
217    0b1001_1000,    // 9
218];
219
220static LETTERS: [u8; 26] = [
221    0b1000_1000,    // A
222    0b1000_0011,    // B
223    0b1100_0110,    // C
224    0b1010_0001,    // D
225    0b1000_0110,    // E
226    0b1000_1110,    // F
227    0b1100_0010,    // G
228    0b1000_1001,    // H
229    0b1100_1111,    // I
230    0b1110_0001,    // J
231    0b1000_1010,    // K
232    0b1100_0111,    // L
233    0b1110_1010,    // M
234    0b1100_1000,    // N
235    0b1100_0000,    // O
236    0b1000_1100,    // P
237    0b1001_0100,    // Q
238    0b1100_1100,    // R
239    0b1001_0010,    // S
240    0b1000_0111,    // T
241    0b1100_0001,    // U
242    0b1100_0001,    // V
243    0b1101_0101,    // W
244    0b1000_1001,    // X
245    0b1001_0001,    // Y
246    0b1010_0100,    // Z
247];