Skip to main content

tmledkey_hal_drv/
utils.rs

1//! Helpful functions to work with MCUs raw data.
2//!
3//! Some utility functions are depended on `Vec` which is require you
4//! to have properly configure global memory allocator.
5//!
6//! If you are developing for embedded devices
7//! than you probably does not have global memory allocator by default.
8#[cfg(feature = "galloc")]
9use alloc::vec::Vec;
10use core::ops::Deref;
11
12use super::{CHAR_MINUS, DIGITS, SEG_8};
13
14const INT_CONVERT_MAX_SIZE: usize = 11;
15
16/// Represents conversion result from integer to 8 segment bytemask array.
17/// You could deref this structure as slice.
18#[derive(Debug)]
19pub struct IntConvertResult {
20    offset: usize,
21    bytes: [u8; INT_CONVERT_MAX_SIZE],
22}
23
24impl IntConvertResult {
25    fn new() -> IntConvertResult {
26        IntConvertResult {
27            offset: INT_CONVERT_MAX_SIZE,
28            bytes: [0; INT_CONVERT_MAX_SIZE],
29        }
30    }
31
32    fn last(&self) -> u8 {
33        self.bytes[self.bytes.len() - 1]
34    }
35
36    fn set_last(&mut self, byte: u8) {
37        self.bytes[self.bytes.len() - 1] = byte;
38    }
39
40    fn add_first(&mut self, byte: u8) {
41        if self.offset == 0 {
42            return;
43        }
44
45        self.offset -= 1;
46        self.bytes[self.offset] = byte;
47    }
48
49    fn add_last(&mut self, byte: u8) {
50        if self.offset == 0 {
51            return;
52        }
53
54        self.offset -= 1;
55
56        for i in self.offset..(self.bytes.len() - 1) {
57            self.bytes[i] = self.bytes[i + 1];
58        }
59        self.bytes[self.bytes.len() - 1] = byte;
60    }
61
62    fn remove_last(&mut self) {
63        if self.is_empty() {
64            return;
65        }
66
67        if self.len() > 1 {
68            for i in ((self.offset + 1)..self.bytes.len()).rev() {
69                self.bytes[i] = self.bytes[i - 1];
70            }
71        }
72        self.offset += 1;
73    }
74
75    fn len(&self) -> usize {
76        let len = self.bytes.len() - self.offset;
77        if len > 0 {
78            len
79        } else {
80            0
81        }
82    }
83
84    fn is_empty(&self) -> bool {
85        self.len() == 0
86    }
87}
88
89impl Deref for IntConvertResult {
90    type Target = [u8];
91    fn deref(&self) -> &Self::Target {
92        &self.bytes[self.offset..self.bytes.len()]
93    }
94}
95
96/// Represents conversion result from float/double to 8 segment bytemask array.
97/// You could deref this structure as slice.
98#[derive(Debug)]
99pub struct DoubleConvertResult {
100    offset: usize,
101    bytes: [u8; INT_CONVERT_MAX_SIZE * 2],
102}
103
104impl DoubleConvertResult {
105    fn new(head: &[u8], tail: &[u8]) -> DoubleConvertResult {
106        let mut offset = INT_CONVERT_MAX_SIZE * 2;
107        let mut bytes = [0; INT_CONVERT_MAX_SIZE * 2];
108        let len = head.len() + tail.len();
109        if len <= INT_CONVERT_MAX_SIZE * 2 {
110            offset -= len;
111            let mut idx = offset;
112            for i in 0..head.len() {
113                bytes[idx] = head[i];
114                idx += 1;
115            }
116            for i in 0..tail.len() {
117                bytes[idx] = tail[i];
118                idx += 1;
119            }
120        }
121
122        DoubleConvertResult {
123            offset: offset,
124            bytes: bytes,
125        }
126    }
127}
128
129impl Deref for DoubleConvertResult {
130    type Target = [u8];
131    fn deref(&self) -> &Self::Target {
132        &self.bytes[self.offset..self.bytes.len()]
133    }
134}
135
136/// Convert given integer value to appropriate bytes vector.
137/// Adds minus sign for negative values
138pub fn int_to_bytes(value: i32) -> IntConvertResult {
139    let mut result = IntConvertResult::new();
140    let mut v = if value < 0 { value * -1 } else { value };
141    while v > 0 {
142        result.add_first(DIGITS[(v % 10) as usize]);
143        v /= 10;
144    }
145    if value < 0 {
146        result.add_first(CHAR_MINUS);
147    }
148    if result.is_empty() {
149        result.add_first(DIGITS[0]);
150    }
151    result
152}
153
154/// Convert given float to appropriate bytes vector.
155/// Adds minus sign for negative values, tries to ignore float garbage.
156/// Always adds dot and zero for fractional part like "1.0"
157pub fn float_to_bytes(value: f32) -> DoubleConvertResult {
158    float_to_bytes_ex(value, 10, false)
159}
160
161/// Extended float to bytes convertor.
162/// Adds minus sign for negative values, tries to ignore float garbage.
163/// Always adds dot and zero for fractional part like "1.0"
164///
165/// Arguments:
166///  - `value` - any positive or negative value
167///  - `precision` - cut off fractional part above this value
168///  - `zero_pad` - pads fractional part with zeros according to `precision` value
169pub fn float_to_bytes_ex(value: f32, precision: u8, zero_pad: bool) -> DoubleConvertResult {
170    let whole = value as i32;
171    let mut wresult = int_to_bytes(whole);
172
173    let mut fract_part = fractional_part_to_bytes(value, precision);
174    if zero_pad {
175        while fract_part.len() < precision as usize {
176            fract_part.add_last(DIGITS[0]);
177        }
178    }
179
180    let with_dot = wresult.last() | SEG_8;
181    wresult.set_last(with_dot);
182    DoubleConvertResult::new(&wresult, &fract_part)
183}
184
185fn fractional_part_to_bytes(value: f32, precision: u8) -> IntConvertResult {
186    let mut v = if value < 0.0 { value * -1.0 } else { value };
187    let whole = v as i32;
188    v -= whole as f32;
189
190    let mut all_zero = true;
191    let mut zeroes = 0;
192
193    let mut result = IntConvertResult::new();
194    for _ in 0..precision {
195        v *= 10.0;
196        let d = v as i32 % 10;
197        if d == 0 {
198            zeroes += 1;
199        } else {
200            zeroes = 0;
201            all_zero = false;
202        }
203        result.add_last(DIGITS[(d % 10) as usize]);
204        if zeroes >= 4 {
205            // Skip further processing 4 or more zeroes chain found
206            // Quick hack to avoid float garbage
207            for _ in 1..=zeroes {
208                result.remove_last();
209            }
210            break;
211        }
212    }
213    if all_zero {
214        let mut z = IntConvertResult::new();
215        z.add_last(DIGITS[0]);
216        z
217    } else {
218        result
219    }
220}
221
222/// Duplicate amount of bytes by adding 0 byte after each input byte.
223/// Can be used for 3 wire interfaces with TM1638 where 2 bytes used to write display state.
224///
225/// This method **require "galloc"** feature to be enabled.
226#[cfg(feature = "galloc")]
227pub fn double_bytes(input: &[u8]) -> Vec<u8> {
228    let mut double_byte = Vec::<u8>::new();
229    for i in 0..input.len() {
230        double_byte.push(input[i]);
231        double_byte.push(0);
232    }
233    double_byte
234}
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239
240    #[test]
241    fn int_to_bytes_output() {
242        assert_eq!(int_to_bytes(0).as_ref(), &[DIGITS[0]]);
243
244        assert_eq!(
245            int_to_bytes(1234567890).as_ref(),
246            &[
247                DIGITS[1], DIGITS[2], DIGITS[3], DIGITS[4], DIGITS[5], DIGITS[6], DIGITS[7],
248                DIGITS[8], DIGITS[9], DIGITS[0]
249            ]
250        );
251
252        assert_eq!(
253            int_to_bytes(-1234567890).as_ref(),
254            &[
255                CHAR_MINUS, DIGITS[1], DIGITS[2], DIGITS[3], DIGITS[4], DIGITS[5], DIGITS[6],
256                DIGITS[7], DIGITS[8], DIGITS[9], DIGITS[0]
257            ]
258        );
259
260        assert_eq!(
261            int_to_bytes(-1234).as_ref(),
262            &[CHAR_MINUS, DIGITS[1], DIGITS[2], DIGITS[3], DIGITS[4]]
263        );
264    }
265
266    #[test]
267    fn float_to_bytes_test() {
268        assert_eq!(float_to_bytes(0.0).deref(), &[DIGITS[0] | SEG_8, DIGITS[0]]);
269
270        assert_eq!(
271            float_to_bytes(-12345.0).deref(),
272            &[
273                CHAR_MINUS,
274                DIGITS[1],
275                DIGITS[2],
276                DIGITS[3],
277                DIGITS[4],
278                DIGITS[5] | SEG_8,
279                DIGITS[0]
280            ]
281        );
282
283        assert_eq!(
284            float_to_bytes(-5.012).deref(),
285            &[
286                CHAR_MINUS,
287                DIGITS[5] | SEG_8,
288                DIGITS[0],
289                DIGITS[1],
290                DIGITS[2]
291            ]
292        );
293        assert_eq!(
294            float_to_bytes(12345.0).deref(),
295            &[
296                DIGITS[1],
297                DIGITS[2],
298                DIGITS[3],
299                DIGITS[4],
300                DIGITS[5] | SEG_8,
301                DIGITS[0]
302            ]
303        );
304
305        assert_eq!(
306            float_to_bytes(-5.20000123).deref(),
307            &[CHAR_MINUS, DIGITS[5] | SEG_8, DIGITS[2]]
308        );
309    }
310
311    #[test]
312    fn float_to_bytes_ex_test() {
313        assert_eq!(
314            float_to_bytes_ex(0.0, 2, true).deref(),
315            &[DIGITS[0] | SEG_8, DIGITS[0], DIGITS[0]]
316        );
317
318        assert_eq!(
319            float_to_bytes_ex(0.123, 2, true).deref(),
320            &[DIGITS[0] | SEG_8, DIGITS[1], DIGITS[2]]
321        );
322
323        assert_eq!(
324            float_to_bytes_ex(0.123, 5, true).deref(),
325            &[
326                DIGITS[0] | SEG_8,
327                DIGITS[1],
328                DIGITS[2],
329                DIGITS[3],
330                DIGITS[0],
331                DIGITS[0]
332            ]
333        );
334    }
335
336    #[test]
337    #[cfg(feature = "galloc")]
338    fn double_bytes_test() {
339        let input: [u8; 4] = [1, 2, 3, 4];
340        let check: [u8; 8] = [1, 0, 2, 0, 3, 0, 4, 0];
341        let result = double_bytes(&input);
342        assert_eq!(check, result.as_slice());
343    }
344}