tmledkey_hal_drv/
lib.rs

1//! This crate provides interface to work with Titanmec LED drives for [7 segment displays](https://en.wikipedia.org/wiki/Seven-SEG_display).
2//! Titanmec produce different types of MCUs that are using common 2 pin or 3 pin serial like interfaces.
3//! The most popular models for DIY projects are TM1637 and TM1638.
4//! Besides display driver capabilities this MCUs are also provide key-scan functionality.
5//!
6//! This driver utilize embedded_hal functionality, thus it could work on different kind of hardware via HAL.
7//!
8//! # This is low level API
9//! **Read data sheet first** if you want to understand how to work with MCU.
10//! API provided here only wraps data transfer protocols.
11//! You still have to send and receive raw data as bytes.
12//! In order to understand meaning of this bytes you should have some knowledges.
13//!
14//! # Need code examples?
15//! Just explore repository [examples folder](https://github.com/rustrum/tmledkey-hal-drv).
16//!
17//! *I just have no capacity to maintain actual code samples in every place.*
18//!
19//! # Features
20//!
21//! Some functionality of this library are splitted into features.
22//! I hope that it may help to reduce resulted firmware size.
23//!
24//! - **cldkio** - functions to work with 2 wire interfaces
25//! - **clkdiostb** - functions to work with 3 wire interfaces
26//! - **keys** - key scan support
27//! - **fx** - tiny effects api (depends on "galloc")
28//! - **galloc** - functionality that require to have global allocator in your application
29//!
30//! You should look into Cargo.toml in [source code](https://github.com/rustrum/tmledkey-hal-drv)
31//! to get better understanding how does this features combined together.
32//!
33//! # Handling delays
34//!
35//! I've found out that handling delays is a very tricky for some hardware.
36//! Plus HAL implementation gives DelayXX and Timer traits thus it is not obvious what kind of trait should be preferred.
37//!
38//! In order to simplify delay approach I decided that
39//! **you have to implement delay logic on your side** and wrap it in lambda.
40//! It is up to you what kind of delay approach you will use.
41//! Just keep in mind that delays have to be precise.
42//!
43//! Some functions accepts delay value as input parameter.
44//! It is kinda weird, but it would allow you to reduce bus communication delays if your circuit configuration allows you to.
45//! In other cases you can use pre defined delay values.
46//!
47#![no_std]
48#![allow(non_upper_case_globals)]
49#[cfg(feature = "galloc")]
50extern crate alloc;
51
52pub mod utils;
53
54#[cfg(feature = "fx")]
55pub mod fx;
56
57#[cfg(feature = "demo")]
58pub mod demo;
59
60use embedded_hal::digital::v2::{InputPin, OutputPin};
61
62#[cfg(not(any(feature = "clkdio", feature = "clkdiostb")))]
63compile_error!("Either feature \"clkdio\" or \"clkdiostb\" must be enabled for this crate. Otherwise there is no reason to use it");
64
65/// Describes possible error mostly related to low level interaction with MCU.
66/// At least it should give you an insight about what goes wrong.
67#[derive(Debug)]
68pub enum TmError {
69    Dio,
70    /// ACK error with tricky code.
71    /// Code could help to get more detailed information about exact place where error occurred.
72    Ack(u8),
73    Clk,
74    Stb,
75    /// There was some errors in user input
76    Input,
77}
78
79/// Expecting to have delay after start sequence or previous send call
80#[inline]
81fn tm_bus_send<DIO, CLK, D>(
82    dio: &mut DIO,
83    clk: &mut CLK,
84    delay_us: &mut D,
85    bus_delay_us: u16,
86    mut byte: u8,
87) -> Result<(), TmError>
88where
89    DIO: InputPin + OutputPin,
90    CLK: OutputPin,
91    D: FnMut(u16) -> (),
92{
93    for _ in 0..8 {
94        clk.set_low().map_err(|_| TmError::Clk)?;
95        // This delay can be skipped, but data transfer become unstable
96        delay_us(bus_delay_us);
97
98        let high = byte & 0b1 != 0;
99        byte = byte >> 1;
100        if high {
101            dio.set_high().map_err(|_| TmError::Dio)?;
102        } else {
103            dio.set_low().map_err(|_| TmError::Dio)?;
104        }
105        delay_us(bus_delay_us);
106
107        clk.set_high().map_err(|_| TmError::Clk)?;
108        delay_us(bus_delay_us);
109    }
110    Ok(())
111}
112
113/// Expecting to have delay after start sequence or previous send call
114#[inline]
115#[cfg(feature = "keys")]
116fn tm_bus_read<DIO, CLK, D>(
117    dio: &mut DIO,
118    clk: &mut CLK,
119    delay_us: &mut D,
120    bus_delay_us: u16,
121) -> Result<u8, TmError>
122where
123    DIO: InputPin + OutputPin,
124    CLK: OutputPin,
125    D: FnMut(u16) -> (),
126{
127    let mut byte = 0;
128    for i in 0..8 {
129        clk.set_low().map_err(|_| TmError::Clk)?;
130        delay_us(bus_delay_us);
131        // Looks like MCU changes dio at low CLK
132        clk.set_high().map_err(|_| TmError::Clk)?;
133        if dio.is_high().map_err(|_| TmError::Dio)? {
134            byte = byte | 0x80 >> i;
135        }
136        delay_us(bus_delay_us);
137    }
138    Ok(byte)
139}
140
141#[cfg(feature = "clkdio")]
142fn tm_bus_dio_wait_ack<DIO, D>(
143    dio: &mut DIO,
144    delay_us: &mut D,
145    bus_delay_us: u16,
146    expect_high: bool,
147    err: u8,
148) -> Result<(), TmError>
149where
150    DIO: InputPin + OutputPin,
151    D: FnMut(u16) -> (),
152{
153    for _ in 0..5 {
154        if expect_high == dio.is_high().map_err(|_| TmError::Dio)? {
155            return Ok(());
156        }
157        delay_us(bus_delay_us);
158    }
159
160    Err(TmError::Ack(err))
161}
162
163/// Expecting to have initial state on bus CLK is UP and DIO is UP.
164#[inline]
165#[cfg(feature = "clkdio")]
166fn tm_bus_2wire_start<DIO, CLK, D>(
167    dio: &mut DIO,
168    clk: &mut CLK,
169    delay_us: &mut D,
170    bus_delay_us: u16,
171) -> Result<(), TmError>
172where
173    DIO: InputPin + OutputPin,
174    CLK: OutputPin,
175    D: FnMut(u16) -> (),
176{
177    clk.set_high().map_err(|_| TmError::Clk)?;
178    dio.set_low().map_err(|_| TmError::Dio)?;
179    delay_us(bus_delay_us);
180    Ok(())
181}
182
183#[inline]
184#[cfg(feature = "clkdio")]
185fn tm_bus_2wire_stop<DIO, CLK, D>(
186    dio: &mut DIO,
187    clk: &mut CLK,
188    delay_us: &mut D,
189    bus_delay_us: u16,
190) -> Result<(), TmError>
191where
192    DIO: InputPin + OutputPin,
193    CLK: OutputPin,
194    D: FnMut(u16) -> (),
195{
196    dio.set_low().map_err(|_| TmError::Dio)?;
197    delay_us(bus_delay_us);
198
199    clk.set_high().map_err(|_| TmError::Clk)?;
200    delay_us(bus_delay_us);
201
202    dio.set_high().map_err(|_| TmError::Dio)?;
203    tm_bus_dio_wait_ack(dio, delay_us, bus_delay_us, true, 255)?;
204    delay_us(bus_delay_us);
205    Ok(())
206}
207
208/// Should be called right after send
209#[cfg(feature = "clkdio")]
210fn tm_bus_2wire_ack<DIO, CLK, D>(
211    dio: &mut DIO,
212    clk: &mut CLK,
213    delay_us: &mut D,
214    bus_delay_us: u16,
215    err_code: u8,
216    verify_last: bool,
217) -> Result<(), TmError>
218where
219    DIO: InputPin + OutputPin,
220    CLK: OutputPin,
221    D: FnMut(u16) -> (),
222{
223    // 8th cycle falling edge
224    dio.set_high().map_err(|_| TmError::Dio)?;
225    clk.set_low().map_err(|_| TmError::Clk)?;
226    delay_us(bus_delay_us);
227
228    // Ensure that DIO was pulled down at 8th cycle falling edge
229    tm_bus_dio_wait_ack(dio, delay_us, bus_delay_us, false, err_code + 1)?;
230
231    // 9th cycle rising edge
232    clk.set_high().map_err(|_| TmError::Clk)?;
233    delay_us(bus_delay_us);
234
235    // Ensure DIO still low at 9th cycle rising edge
236    tm_bus_dio_wait_ack(dio, delay_us, bus_delay_us, false, err_code + 2)?;
237
238    // 9th cycle falling edge
239    clk.set_low().map_err(|_| TmError::Clk)?;
240    delay_us(bus_delay_us);
241
242    // Ensure DIO was released and now it is up
243    if verify_last {
244        // No need to check last ACK for reading mode
245        tm_bus_dio_wait_ack(dio, delay_us, bus_delay_us, true, err_code + 3)?;
246    }
247
248    Ok(())
249}
250
251#[inline]
252#[cfg(feature = "clkdio")]
253fn tm_bus_2wire_send_byte_ack<DIO, CLK, D>(
254    dio: &mut DIO,
255    clk: &mut CLK,
256    delay_us: &mut D,
257    bus_delay_us: u16,
258    byte: u8,
259    err_code: u8,
260) -> Result<(), TmError>
261where
262    DIO: InputPin + OutputPin,
263    CLK: OutputPin,
264    D: FnMut(u16) -> (),
265{
266    tm_bus_send(dio, clk, delay_us, bus_delay_us, byte)?;
267    let verify_last = byte & COM_DATA_READ != COM_DATA_READ;
268    tm_bus_2wire_ack(dio, clk, delay_us, bus_delay_us, err_code, verify_last)
269}
270
271#[inline]
272#[cfg(all(feature = "keys", feature = "clkdio"))]
273fn tm_bus_2wire_read_byte_ack<DIO, CLK, D>(
274    dio: &mut DIO,
275    clk: &mut CLK,
276    delay_us: &mut D,
277    bus_delay_us: u16,
278    err_code: u8,
279) -> Result<u8, TmError>
280where
281    DIO: InputPin + OutputPin,
282    CLK: OutputPin,
283    D: FnMut(u16) -> (),
284{
285    let result = tm_bus_read(dio, clk, delay_us, bus_delay_us);
286    tm_bus_2wire_ack(dio, clk, delay_us, bus_delay_us, err_code, true)?;
287    result
288}
289
290/// Send one or several bytes to MCU via 2 wire interface (DIO,CLK).
291///
292/// According to datasheet it can be single command byte or a sequence starting with command byte followed by several data bytes.
293///
294/// Arguments:
295///  - `dio`, `clk` - MCU interface pins
296///  - `delay_us` - closure that provides delay functionality
297///  - `delay_value` - delay value in us, depends of MCU you are using and circuit features
298///  - `bytes` - slice of bytes to send
299#[inline]
300#[cfg(feature = "clkdio")]
301pub fn tm_send_bytes_2wire<DIO, CLK, D>(
302    dio: &mut DIO,
303    clk: &mut CLK,
304    delay_us: &mut D,
305    delay_value: u16,
306    bytes: &[u8],
307) -> Result<(), TmError>
308where
309    DIO: InputPin + OutputPin,
310    CLK: OutputPin,
311    D: FnMut(u16) -> (),
312{
313    tm_bus_2wire_start(dio, clk, delay_us, delay_value)?;
314
315    let mut send = Err(TmError::Input);
316    let mut iter = 10;
317    for bt in bytes {
318        send = tm_bus_2wire_send_byte_ack(dio, clk, delay_us, delay_value, bt.clone(), iter);
319        if send.is_err() {
320            break;
321        }
322        iter += 10;
323    }
324
325    let stop = tm_bus_2wire_stop(dio, clk, delay_us, delay_value);
326    if send.is_err() {
327        send
328    } else {
329        stop
330    }
331}
332
333/// Reads key scan data as byte via 2 wire interface (DIO,CLK).
334///
335/// Arguments:
336///  - `dio`, `clk` - MCU interface pins
337///  - `delay_us` - closure that provides delay functionality
338///  - `delay_value` - delay value in us, depends of MCU you are using and circuit features
339#[inline]
340#[cfg(all(feature = "keys", feature = "clkdio"))]
341pub fn tm_read_byte_2wire<DIO, CLK, D>(
342    dio: &mut DIO,
343    clk: &mut CLK,
344    delay_us: &mut D,
345    delay_value: u16,
346) -> Result<u8, TmError>
347where
348    DIO: InputPin + OutputPin,
349    CLK: OutputPin,
350    D: FnMut(u16) -> (),
351{
352    tm_bus_2wire_start(dio, clk, delay_us, delay_value)?;
353
354    tm_bus_2wire_send_byte_ack(dio, clk, delay_us, delay_value, COM_DATA_READ, 230)?;
355
356    let read = tm_bus_2wire_read_byte_ack(dio, clk, delay_us, delay_value, 240);
357
358    let stop = tm_bus_2wire_stop(dio, clk, delay_us, delay_value);
359    if stop.is_err() {
360        if read.is_err() {
361            return read;
362        } else {
363            return Err(stop.err().unwrap());
364        }
365    }
366    read
367}
368
369/// Send bytes using 3 wire interface (DIO,CLK,STB).
370///
371/// According to datasheet it can be single command byte or a sequence starting with command byte followed by several data bytes.
372///
373/// **Keep in mind** that for this interface you should send 2 bytes for each display.
374/// For TM1638 second byte stands for segments 9-12 which has no meaning for 8 segment display.
375/// Thus in most cases you would send each 2nd data byte as empty.
376///
377/// Arguments:
378///  - `dio`, `clk`, `stb` - MCU interface pins
379///  - `delay_us` - closure that provides delay functionality
380///  - `delay_value` - delay value in us, depends of MCU you are using and your features
381///  - `bytes` - slice of bytes to send
382#[inline]
383#[cfg(feature = "clkdiostb")]
384pub fn tm_send_bytes_3wire<DIO, CLK, STB, D>(
385    dio: &mut DIO,
386    clk: &mut CLK,
387    stb: &mut STB,
388    delay_us: &mut D,
389    delay_value: u16,
390    bytes: &[u8],
391) -> Result<(), TmError>
392where
393    DIO: InputPin + OutputPin,
394    CLK: OutputPin,
395    STB: OutputPin,
396    D: FnMut(u16) -> (),
397{
398    delay_us(delay_value);
399    stb.set_low().map_err(|_| TmError::Stb)?;
400    delay_us(delay_value);
401
402    let mut send = Err(TmError::Input);
403    for bt in bytes {
404        send = tm_bus_send(dio, clk, delay_us, delay_value, bt.clone());
405        if send.is_err() {
406            break;
407        }
408        // Notice: When read data, set instruction from the 8th rising edge of clock
409        // to CLK falling edge to read data that demand a waiting time T wait(min 1μS).
410        //delayer();
411    }
412    delay_us(delay_value);
413    stb.set_high().map_err(|_| TmError::Stb)?;
414    clk.set_high().map_err(|_| TmError::Stb)?;
415    dio.set_high().map_err(|_| TmError::Stb)?;
416    send
417}
418
419/// Read **read_count** of bytes into response array from MCU using 3 wire interface (DIO,CLK,STB).
420///
421/// Response array has fixed size of 4, so you can read up to 4 bytes there.
422///
423/// Arguments:
424///  - `dio`, `clk`, `stb` - MCU interface pins
425///  - `delay_us` - closure that provides delay functionality
426///  - `delay_value` - delay value in us, depends of MCU you are using and circuit features
427///  - `read_count` - number of bytes to read into output array
428#[inline]
429#[cfg(all(feature = "keys", feature = "clkdiostb"))]
430pub fn tm_read_bytes_3wire<DIO, CLK, STB, D>(
431    dio: &mut DIO,
432    clk: &mut CLK,
433    stb: &mut STB,
434    delay_us: &mut D,
435    delay_value: u16,
436    read_count: u8,
437) -> Result<[u8; 4], TmError>
438where
439    DIO: InputPin + OutputPin,
440    CLK: OutputPin,
441    STB: OutputPin,
442    D: FnMut(u16) -> (),
443{
444    let mut read_err = None;
445    let mut response = [0_u8; 4];
446
447    if read_count <= 0 || read_count > response.len() as u8 {
448        return Err(TmError::Input);
449    }
450
451    delay_us(delay_value);
452    stb.set_low().map_err(|_| TmError::Stb)?;
453    delay_us(delay_value);
454
455    let res_init = tm_bus_send(dio, clk, delay_us, delay_value, COM_DATA_READ);
456    dio.set_high().map_err(|_| TmError::Stb)?;
457    if res_init.is_err() {
458        read_err = Some(res_init.unwrap_err());
459    } else {
460        // Notice: When read data, set instruction from the 8th rising edge of clock
461        // to CLK falling edge to read data that demand a waiting time T wait(min 1μS).
462        delay_us(delay_value);
463        for i in 0..(read_count as usize) {
464            match tm_bus_read(dio, clk, delay_us, delay_value) {
465                Ok(b) => {
466                    response[i] = b;
467                }
468                Err(e) => {
469                    read_err = Some(e);
470                    break;
471                }
472            }
473        }
474    }
475
476    stb.set_high().map_err(|_| TmError::Stb)?;
477    clk.set_high().map_err(|_| TmError::Stb)?;
478    dio.set_high().map_err(|_| TmError::Stb)?;
479
480    if read_err.is_some() {
481        return Err(read_err.unwrap());
482    }
483    Ok(response)
484}
485
486/// Number of bytes that can be read from from TM1638 response.
487pub const TM1638_RESPONSE_SIZE: u8 = 4;
488/// Maximum number of display segments supported by this MCU.
489pub const TM1638_MAX_SEGMENTS: u8 = 10;
490
491/// Number of bytes that can be read from from TM1637 response.
492pub const TM1637_RESPONSE_SIZE: u8 = 1;
493/// Maximum number of display segments supported by this MCU.
494pub const TM1637_MAX_SEGMENTS: u8 = 6;
495
496/// Proven working delay for TM1637, it can be lower depending of your pull-up resistor characteristics.
497pub const TM1637_BUS_DELAY_US: u16 = 475;
498
499/// Proven working delay for TM1638
500pub const TM1638_BUS_DELAY_US: u16 = 1;
501
502/// Universal delay for TM serial protocol.
503/// This value should fit all configurations, but you should prefer to use values that fits exact MCU chip version.
504pub const BUS_DELAY_US: u16 = 500;
505
506/// Data control instruction set
507pub const COM_DATA: u8 = 0b01000000;
508
509/// Display control instruction set
510pub const COM_DISPLAY: u8 = 0b10000000;
511
512/// Address instruction set
513pub const COM_ADDRESS: u8 = 0b11000000;
514
515/// Address adding mode (write to display)
516pub const COM_DATA_ADDRESS_ADD: u8 = COM_DATA | 0b000000;
517/// Data fix address mode (write to display)
518pub const COM_DATA_ADDRESS_FIXED: u8 = COM_DATA | 0b000100;
519/// Read key scan data
520pub const COM_DATA_READ: u8 = COM_DATA | 0b000010;
521
522/// Display ON max brightness.
523/// Can be combined with masked bytes to adjust brightness level
524pub const COM_DISPLAY_ON: u8 = 0b10001000;
525/// Display brightness mask
526pub const DISPLAY_BRIGHTNESS_MASK: u8 = 0b00000111;
527// Display OFF
528pub const COM_DISPLAY_OFF: u8 = 0b10000000;
529
530/// Segment A - top
531pub const SEG_1: u8 = 0b1;
532/// Segment B - top right
533pub const SEG_2: u8 = 0b10;
534/// Segment C - bottom right
535pub const SEG_3: u8 = 0b100;
536/// Segment D - bottom
537pub const SEG_4: u8 = 0b1000;
538/// Segment E - bottom left
539pub const SEG_5: u8 = 0b10000;
540/// Segment F - top left
541pub const SEG_6: u8 = 0b100000;
542/// Segment G - middle
543pub const SEG_7: u8 = 0b1000000;
544/// Segment DP (eight) - dot or colon
545pub const SEG_8: u8 = 0b10000000;
546
547/// Used with 3 wire interface for second byte
548pub const SEG_9: u8 = SEG_1;
549/// Used with 3 wire interface for second byte
550pub const SEG_10: u8 = SEG_2;
551/// Used with 3 wire interface for second byte
552pub const SEG_11: u8 = SEG_3;
553/// Used with 3 wire interface for second byte
554pub const SEG_12: u8 = SEG_4;
555
556pub const CHAR_0: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_6;
557pub const CHAR_1: u8 = SEG_2 | SEG_3;
558pub const CHAR_2: u8 = SEG_1 | SEG_2 | SEG_4 | SEG_5 | SEG_7;
559pub const CHAR_3: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_7;
560pub const CHAR_4: u8 = SEG_2 | SEG_3 | SEG_6 | SEG_7;
561pub const CHAR_5: u8 = SEG_1 | SEG_3 | SEG_4 | SEG_6 | SEG_7;
562pub const CHAR_6: u8 = SEG_1 | SEG_3 | SEG_4 | SEG_5 | SEG_6 | SEG_7;
563pub const CHAR_7: u8 = SEG_1 | SEG_2 | SEG_3;
564pub const CHAR_8: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_6 | SEG_7;
565pub const CHAR_9: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_6 | SEG_7;
566pub const CHAR_A: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_5 | SEG_6 | SEG_7;
567pub const CHAR_a: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_7;
568pub const CHAR_b: u8 = SEG_3 | SEG_4 | SEG_5 | SEG_6 | SEG_7;
569pub const CHAR_C: u8 = SEG_1 | SEG_4 | SEG_5 | SEG_6;
570pub const CHAR_c: u8 = SEG_4 | SEG_5 | SEG_7;
571pub const CHAR_d: u8 = SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_7;
572pub const CHAR_E: u8 = SEG_1 | SEG_4 | SEG_5 | SEG_6 | SEG_7;
573pub const CHAR_e: u8 = SEG_1 | SEG_2 | SEG_4 | SEG_5 | SEG_6 | SEG_7;
574pub const CHAR_F: u8 = SEG_1 | SEG_5 | SEG_6 | SEG_7;
575pub const CHAR_G: u8 = SEG_1 | SEG_3 | SEG_4 | SEG_5 | SEG_6;
576pub const CHAR_H: u8 = SEG_2 | SEG_3 | SEG_5 | SEG_6 | SEG_7;
577pub const CHAR_h: u8 = SEG_3 | SEG_5 | SEG_6 | SEG_7;
578pub const CHAR_I: u8 = SEG_2 | SEG_3;
579pub const CHAR_i: u8 = SEG_3;
580pub const CHAR_J: u8 = SEG_2 | SEG_3 | SEG_4 | SEG_5;
581pub const CHAR_L: u8 = SEG_4 | SEG_5 | SEG_6;
582pub const CHAR_l: u8 = SEG_4 | SEG_5;
583pub const CHAR_N: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_5 | SEG_6;
584pub const CHAR_n: u8 = SEG_3 | SEG_5 | SEG_7;
585pub const CHAR_O: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_6;
586pub const CHAR_o: u8 = SEG_3 | SEG_4 | SEG_5 | SEG_7;
587pub const CHAR_P: u8 = SEG_1 | SEG_2 | SEG_5 | SEG_6 | SEG_7;
588pub const CHAR_q: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_6 | SEG_7;
589pub const CHAR_R: u8 = SEG_1 | SEG_5 | SEG_6;
590pub const CHAR_r: u8 = SEG_5 | SEG_7;
591pub const CHAR_S: u8 = SEG_1 | SEG_3 | SEG_4 | SEG_6 | SEG_7;
592pub const CHAR_t: u8 = SEG_4 | SEG_5 | SEG_6 | SEG_7;
593pub const CHAR_U: u8 = SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_6;
594pub const CHAR_u: u8 = SEG_3 | SEG_4 | SEG_5;
595pub const CHAR_y: u8 = SEG_2 | SEG_3 | SEG_4 | SEG_6 | SEG_7;
596pub const CHAR_CYR_E: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_7;
597pub const CHAR_CYR_B: u8 = SEG_1 | SEG_3 | SEG_4 | SEG_5 | SEG_6 | SEG_7;
598pub const CHAR_DEGREE: u8 = SEG_1 | SEG_2 | SEG_6 | SEG_7;
599pub const CHAR_MINUS: u8 = SEG_7;
600pub const CHAR_UNDERSCORE: u8 = SEG_4;
601pub const CHAR_BRACKET_LEFT: u8 = SEG_1 | SEG_4 | SEG_5 | SEG_6;
602pub const CHAR_BRACKET_RIGHT: u8 = SEG_1 | SEG_2 | SEG_3 | SEG_4;
603
604/// List of digit characters where values correlates with array index 0-9.
605pub const DIGITS: [u8; 10] = [
606    CHAR_0, CHAR_1, CHAR_2, CHAR_3, CHAR_4, CHAR_5, CHAR_6, CHAR_7, CHAR_8, CHAR_9,
607];