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];