max7219_display/driver/
max7219.rs

1//! Core MAX7219 driver implementation
2
3use embedded_hal::spi::SpiDevice;
4
5use crate::{
6    MAX_DISPLAYS, NUM_DIGITS, Result,
7    error::Error,
8    registers::{DecodeMode, Register},
9};
10
11/// Driver for the MAX7219 LED display controller.
12/// Communicates over SPI using the embedded-hal `SpiDevice` trait.
13pub struct Max7219<SPI> {
14    spi: SPI,
15    buffer: [u8; MAX_DISPLAYS * 2],
16    device_count: usize,
17}
18
19impl<SPI> Max7219<SPI>
20where
21    SPI: SpiDevice,
22{
23    /// Creates a new MAX7219 driver instance with the given SPI interface.
24    ///
25    /// The SPI interface must use Mode 0, which means the clock is low when idle
26    /// and data is read on the rising edge of the clock signal.
27    ///
28    /// Defaults to a single device (can be daisy-chained using `with_device_count`).
29    ///
30    /// The SPI frequency must be 10 MHz or less, as required by the MAX7219 datasheet.
31    pub fn new(spi: SPI) -> Self {
32        Self {
33            spi,
34            device_count: 1, // Default to 1, use with_device_count to increase count
35            buffer: [0; MAX_DISPLAYS * 2],
36        }
37    }
38
39    /// Returns the number of MAX7219 devices managed by this driver.
40    ///
41    /// This corresponds to the number of daisy-chained MAX7219 units
42    /// initialized during driver setup.
43    pub fn device_count(&self) -> usize {
44        self.device_count
45    }
46
47    /// Sets the number of daisy-chained devices to control.
48    ///
49    /// # Errors
50    ///
51    /// Returns `Error::InvalidDisplayCount` if `count > MAX_DISPLAYS`.
52    ///
53    /// # Example
54    ///
55    /// ```rust,ignore
56    /// let driver = Max7219::new(spi).with_device_count(4)?;
57    /// ```
58    pub fn with_device_count(mut self, count: usize) -> Result<Self> {
59        if count > MAX_DISPLAYS {
60            return Err(Error::InvalidDeviceCount);
61        }
62        self.device_count = count;
63        Ok(self)
64    }
65
66    /// Initializes all configured displays.
67    pub fn init(&mut self) -> Result<()> {
68        self.power_on()?;
69
70        self.test_all(false)?;
71        self.set_scan_limit_all(NUM_DIGITS)?;
72        self.set_decode_mode_all(DecodeMode::NoDecode)?;
73
74        self.clear_all()?;
75        // self.power_off()?;
76        // self.power_on()?;
77
78        Ok(())
79    }
80
81    /// Writes a value to a specific register of a device in the daisy chain.
82    ///
83    /// Each MAX7219 device expects a 16-bit packet: 1 byte for the register address
84    /// and 1 byte for the data. To update one device in a daisy-chained series,
85    /// we prepare a full SPI buffer of `display_count * 2` bytes (2 bytes per display).
86    ///
87    /// This method writes the target register and data into the correct offset of
88    /// the shared buffer corresponding to the selected device (`device_index`),
89    /// and clears the rest of the buffer. Then the entire buffer is sent via SPI.
90    ///
91    /// The device at `device_index` will receive its register update, while other
92    /// devices in the chain will receive no-ops (zeros).
93    ///
94    /// # Arguments
95    ///
96    /// * `device_index` - Index of the device in the chain (0 = closest from MCU, N-1 = furthest to MCU).
97    /// * `register` - The register to write to (e.g., `Register::Shutdown`, `Register::Digit0`, etc.).
98    /// * `data` - The value to write to the register.
99    ///
100    /// # Errors
101    ///
102    /// Returns `Error::InvalidDisplayIndex` if the index is out of range, or an SPI error
103    /// if the transfer fails.
104    pub(crate) fn write_device_register(
105        &mut self,
106        device_index: usize,
107        register: Register,
108        data: u8,
109    ) -> Result<()> {
110        if device_index >= self.device_count {
111            return Err(Error::InvalidDeviceIndex);
112        }
113
114        self.buffer = [0; MAX_DISPLAYS * 2];
115
116        let offset = device_index * 2; // 2 bytes(16 bits packet) per display
117        self.buffer[offset] = register as u8;
118        self.buffer[offset + 1] = data;
119
120        self.spi.write(&self.buffer[0..self.device_count * 2])?;
121
122        Ok(())
123    }
124
125    /// Write each (register, data) tuple to its corresponding MAX7219 device in the daisy chain.
126    ///
127    /// The number of tuples in `ops` must exactly match `self.device_count`. Each entry
128    /// in `ops` is sent to the device at the same index: `ops[0]` to device 0, `ops[1]` to device 1, etc.
129    ///
130    /// The SPI buffer is filled in reverse order so that the first bytes clocked out
131    /// travel through the chain and reach the last device first.
132    ///
133    /// # Panics (only in debug builds)
134    /// - If `ops.len() != self.device_count`.
135    ///
136    /// # Errors
137    /// - Returns an SPI error if the write operation fails.
138    pub(crate) fn write_all_registers(&mut self, ops: &[(Register, u8)]) -> Result<()> {
139        // clear the buffer: 2 bytes per device
140        self.buffer = [0; MAX_DISPLAYS * 2];
141
142        // fill in reverse order so that SPI shifts into the last device first
143        for (i, &(reg, data)) in ops.iter().rev().enumerate() {
144            let offset = i * 2;
145            self.buffer[offset] = reg as u8;
146            self.buffer[offset + 1] = data;
147        }
148
149        // send exactly device_count packets
150        let len = self.device_count * 2;
151        self.spi.write(&self.buffer[..len])?;
152
153        Ok(())
154    }
155
156    // fn write_raw_register(&mut self, register: u8, data: u8) -> Result<(), SPI::Error> {
157    //     self.spi.write(&[register, data])
158    // }
159
160    /// Powers on all displays by writing `0x01` to the Shutdown register.
161    pub fn power_on(&mut self) -> Result<()> {
162        let ops = [(Register::Shutdown, 0x01); MAX_DISPLAYS];
163
164        self.write_all_registers(&ops[..self.device_count])
165    }
166
167    /// Powers off all displays by writing `0x00` to the Shutdown register.
168    pub fn power_off(&mut self) -> Result<()> {
169        let ops = [(Register::Shutdown, 0x00); MAX_DISPLAYS];
170
171        self.write_all_registers(&ops[..self.device_count])
172    }
173
174    /// Powers on a single device by writing `0x01` to the Shutdown register.
175    ///
176    /// # Arguments
177    ///
178    /// * `device_index` - The index of the display to power on.
179    pub fn power_on_device(&mut self, device_index: usize) -> Result<()> {
180        self.write_device_register(device_index, Register::Shutdown, 0x01)
181    }
182
183    /// Powers off a single device by writing `0x00` to the Shutdown register.
184    ///
185    /// # Arguments
186    ///
187    /// * `device_index` - The index of the device to power off.
188    pub fn power_off_device(&mut self, device_index: usize) -> Result<()> {
189        self.write_device_register(device_index, Register::Shutdown, 0x00)
190    }
191
192    /// Enables or disables display test mode on a specific device.
193    ///
194    /// When enabled, all LEDs on that device are lit regardless of current device data.
195    pub fn test_device(&mut self, device_index: usize, enable: bool) -> Result<()> {
196        let data = if enable { 0x01 } else { 0x00 };
197        self.write_device_register(device_index, Register::DisplayTest, data)
198    }
199
200    /// Enable or disable display test mode on all devices in one SPI transaction.
201    pub fn test_all(&mut self, enable: bool) -> Result<()> {
202        let data = if enable { 0x01 } else { 0x00 };
203        let ops: [(Register, u8); MAX_DISPLAYS] = [(Register::DisplayTest, data); MAX_DISPLAYS];
204        self.write_all_registers(&ops[..self.device_count])
205    }
206
207    /// Sets how many digits the MAX7219 should actively scan and display.
208    ///
209    /// This tells the chip how many digit outputs (DIG0 to DIG7) should be used.
210    /// The input value must be between 1 and 8:
211    /// - 1 means only digit 0 is used
212    /// - 8 means all digits (0 to 7) are used
213    ///
214    /// Internally, the value written to the chip is `limit - 1`, because the chip expects values from 0 to 7.
215    ///
216    /// This applies to a specific device in the daisy chain, selected by `device_index`.
217    ///
218    /// # Errors
219    /// Returns `Error::InvalidScanLimit` if the value is not in the range 1 to 8.
220    pub fn set_device_scan_limit(&mut self, device_index: usize, limit: u8) -> Result<()> {
221        if !(1..=8).contains(&limit) {
222            return Err(Error::InvalidScanLimit);
223        }
224
225        self.write_device_register(device_index, Register::ScanLimit, limit - 1)
226    }
227
228    /// Set scan‐limit on all devices in one go.
229    ///
230    /// `limit` must be in 1..=8. Internally sends `limit - 1` to each chip.
231    pub fn set_scan_limit_all(&mut self, limit: u8) -> Result<()> {
232        if !(1..=8).contains(&limit) {
233            return Err(Error::InvalidScanLimit);
234        }
235        let val = limit - 1;
236        let ops: [(Register, u8); MAX_DISPLAYS] = [(Register::ScanLimit, val); MAX_DISPLAYS];
237        self.write_all_registers(&ops[..self.device_count])
238    }
239
240    /// Code B decoding allows the MAX7219 to automatically convert values like `0-9`, `E`, `H`, `L`, etc.
241    /// into the corresponding 7-segment patterns, instead of requiring manual segment control.
242    ///
243    /// The `mode` parameter specifies which digits use automatic decoding.
244    /// Use [`DecodeMode`] variants
245    /// such as [`NoDecode`], [`Digit0`], [`Digits0To3`], or [`AllDigits`] based on which digits
246    /// should be decoded automatically.
247    ///
248    /// The `device_index` selects the target device. For a single device setup, use `0`.
249    pub fn set_device_decode_mode(&mut self, device_index: usize, mode: DecodeMode) -> Result<()> {
250        self.write_device_register(device_index, Register::DecodeMode, mode as u8)
251    }
252
253    /// Set decode‐mode on all devices in one go.
254    pub fn set_decode_mode_all(&mut self, mode: DecodeMode) -> Result<()> {
255        let byte = mode as u8;
256        let ops: [(Register, u8); MAX_DISPLAYS] = [(Register::DecodeMode, byte); MAX_DISPLAYS];
257        self.write_all_registers(&ops[..self.device_count])
258    }
259
260    /// Clears all digits by writing 0 to each digit register (DIG0 to DIG7).
261    ///
262    /// This turns off all segments on the display by sending 0x00 to each of the
263    /// digit registers (Register::Digit0 to Register::Digit7).
264    ///
265    /// This applies to a specific device in the daisy chain, selected by `device_index`.
266    pub fn clear_display(&mut self, device_index: usize) -> Result<()> {
267        for digit_register in Register::digits() {
268            self.write_device_register(device_index, digit_register, 0x00)?;
269        }
270        Ok(())
271    }
272
273    /// Clears all digits on all connected MAX7219 displays.
274    pub fn clear_all(&mut self) -> Result<()> {
275        for digit_register in Register::digits() {
276            let ops = [(digit_register, 0x00); MAX_DISPLAYS];
277            self.write_all_registers(&ops[..self.device_count])?;
278        }
279
280        Ok(())
281    }
282
283    /// Writes a raw value to the specified digit register (DIG0 to DIG7).
284    ///
285    /// This function gives you low-level control over the display by sending a
286    /// raw 8-bit pattern to the specified digit. Each bit in the `value` corresponds
287    /// to an individual segment (on 7-segment displays) or LED (on an LED matrix).
288    ///
289    /// **A typical 7-segment** display has the following layout:
290    ///
291    /// ```txt
292    ///     A
293    ///    ---
294    /// F |   | B
295    ///   |   |
296    ///    ---
297    /// E |   | C
298    ///   |   |
299    ///    ---   . DP
300    ///     D
301    /// ```
302    ///
303    /// | Byte        | 7  | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
304    /// |-------------|----|---|---|---|---|---|---|---|
305    /// | **Segment** | DP | A | B | C | D | E | F | G |
306    ///
307    /// For example, to display the number `1`, use the byte `0b00110000`,
308    /// which lights up segments B and C.
309    ///
310    /// # Example
311    ///
312    /// ```rust,ignore
313    /// display.write_raw_digit(0, Digit::D0, 0b00110000)?; // Shows '1'
314    /// ```
315    ///
316    /// **On an LED matrix (8x8)**, each digit register maps to a row, and each
317    /// bit in the value maps to a column (from left to right).
318    ///
319    /// > **Note:** Wiring and orientation can vary between modules. Some modules map rows top-to-bottom,
320    /// > others bottom-to-top; some wire columns left-to-right, others right-to-left.
321    /// > If the display appears mirrored or rotated, adjust your digit and bit mapping accordingly.
322    ///
323    /// Here is an example layout for the FC-16 module, where DIG0 corresponds to the top row (row 0),
324    /// and bit 0 maps to the rightmost column (column 0). So a value like `0b10101010` written to DIG0
325    /// would light up every alternate LED across the top row from left to right.
326    ///
327    /// ```txt
328    /// DIG0 -> Row 0: value = 0b10101010
329    ///
330    /// Matrix:
331    ///           Columns
332    ///            7 6 5 4 3 2 1 0
333    ///          +----------------
334    ///      0   | 1 0 1 0 1 0 1 0
335    ///      1   | ...
336    ///      2   | ...
337    /// Rows 3   | ...
338    ///      4   | ...
339    ///      5   | ...
340    ///      6   | ...
341    ///      7   | ...
342    /// ```
343    ///
344    /// This applies to a specific device in the daisy chain, selected by `device_index`.
345    ///
346    /// # Arguments
347    ///
348    /// - `device_index`: Index of the display in the daisy chain (0 = closest to the Microcontroller)
349    /// - `digit`: Which digit register to write to (`Digit::D0` to `Digit::D7`)
350    /// - `value`: The raw 8-bit data to send to the digit register
351    pub fn write_raw_digit(&mut self, device_index: usize, digit: u8, value: u8) -> Result<()> {
352        let digit_register = Register::try_digit(digit)?;
353        self.write_device_register(device_index, digit_register, value)
354    }
355
356    /// Sets the brightness intensity (0 to 15) for a specific device.
357    ///
358    /// # Arguments
359    ///
360    /// - `device_index`: Index of the display in the daisy chain (0 = closest to the Microcontroller)
361    /// - `intensity`: Brightness level from `0` to `15` (`0x00` to `0x0F`)
362    pub fn set_intensity(&mut self, device_index: usize, intensity: u8) -> Result<()> {
363        if intensity > 0x0F {
364            return Err(Error::InvalidIntensity);
365        }
366        self.write_device_register(device_index, Register::Intensity, intensity)
367    }
368
369    /// Set intensity for all displays
370    pub fn set_intensity_all(&mut self, intensity: u8) -> Result<()> {
371        let ops = [(Register::Intensity, intensity); MAX_DISPLAYS];
372        self.write_all_registers(&ops[..self.device_count])
373    }
374}
375
376#[cfg(test)]
377mod tests {
378    use super::*;
379    use crate::{MAX_DISPLAYS, NUM_DIGITS, registers::DecodeMode, registers::Register};
380    use embedded_hal_mock::eh1::{spi::Mock as SpiMock, spi::Transaction};
381
382    #[test]
383    fn test_new() {
384        let mut spi = SpiMock::new(&[]);
385        let driver = Max7219::new(&mut spi);
386        // Default device count => 1
387        assert_eq!(driver.device_count(), 1);
388
389        spi.done();
390    }
391
392    #[test]
393    fn test_with_device_count_valid() {
394        let mut spi = SpiMock::new(&[]);
395        let driver = Max7219::new(&mut spi);
396        let driver = driver
397            .with_device_count(4)
398            .expect("Should accept valid count");
399        assert_eq!(driver.device_count(), 4);
400        spi.done();
401    }
402
403    #[test]
404    fn test_with_device_count_invalid() {
405        let mut spi = SpiMock::new(&[]);
406        let driver = Max7219::new(&mut spi);
407        let result = driver.with_device_count(MAX_DISPLAYS + 1);
408        assert!(matches!(result, Err(Error::InvalidDeviceCount)));
409
410        spi.done();
411    }
412
413    #[test]
414    fn test_power_on() {
415        let expected_transactions = [
416            Transaction::transaction_start(),
417            Transaction::write_vec(vec![Register::Shutdown.addr(), 0x01]),
418            Transaction::transaction_end(),
419        ];
420        let mut spi = SpiMock::new(&expected_transactions);
421        let mut driver = Max7219::new(&mut spi);
422
423        driver.power_on().expect("Power on should succeed");
424        spi.done();
425    }
426
427    #[test]
428    fn test_power_off() {
429        let expected_transactions = [
430            Transaction::transaction_start(),
431            Transaction::write_vec(vec![Register::Shutdown.addr(), 0x00]),
432            Transaction::transaction_end(),
433        ];
434        let mut spi = SpiMock::new(&expected_transactions);
435        let mut driver = Max7219::new(&mut spi);
436
437        driver.power_off().expect("Power off should succeed");
438        spi.done();
439    }
440
441    #[test]
442    fn test_power_on_device() {
443        let expected_transactions = [
444            Transaction::transaction_start(),
445            Transaction::write_vec(vec![Register::Shutdown.addr(), 0x01]),
446            Transaction::transaction_end(),
447        ];
448        let mut spi = SpiMock::new(&expected_transactions);
449        let mut driver = Max7219::new(&mut spi);
450
451        driver
452            .power_on_device(0)
453            .expect("Power on display should succeed");
454        spi.done();
455    }
456
457    // Test with multiple devices - power_on
458    #[test]
459    fn test_power_on_multiple_devices() {
460        let device_count = 3;
461
462        let expected_transactions = [
463            Transaction::transaction_start(),
464            Transaction::write_vec(vec![
465                Register::Shutdown.addr(),
466                0x01,
467                Register::Shutdown.addr(),
468                0x01,
469                Register::Shutdown.addr(),
470                0x01,
471            ]),
472            Transaction::transaction_end(),
473        ];
474        let mut spi = SpiMock::new(&expected_transactions);
475        let mut driver = Max7219::new(&mut spi)
476            .with_device_count(device_count)
477            .expect("Should accept valid count");
478
479        driver.power_on().expect("Power on should succeed");
480        spi.done();
481    }
482
483    #[test]
484    fn test_power_off_device() {
485        let expected_transactions = [
486            Transaction::transaction_start(),
487            Transaction::write_vec(vec![
488                // For 4 devices
489                Register::NoOp.addr(),
490                0x00,
491                Register::NoOp.addr(),
492                0x00,
493                Register::Shutdown.addr(),
494                0x00,
495                Register::NoOp.addr(),
496                0x00,
497            ]),
498            Transaction::transaction_end(),
499        ];
500        let mut spi = SpiMock::new(&expected_transactions);
501        let mut driver = Max7219::new(&mut spi)
502            .with_device_count(4)
503            .expect("a valid device count");
504
505        driver
506            .power_off_device(2)
507            .expect("Power off display should succeed");
508        spi.done();
509    }
510
511    #[test]
512    fn test_power_device_invalid_index() {
513        let mut spi = SpiMock::new(&[]);
514        let mut driver = Max7219::new(&mut spi).with_device_count(1).unwrap();
515
516        let result = driver.power_on_device(1);
517        assert_eq!(result, Err(Error::InvalidDeviceIndex));
518
519        let result = driver.power_off_device(1);
520        assert_eq!(result, Err(Error::InvalidDeviceIndex));
521        spi.done();
522    }
523
524    #[test]
525    fn test_test_all_enable() {
526        let expected_transactions = [
527            Transaction::transaction_start(),
528            Transaction::write_vec(vec![
529                Register::DisplayTest.addr(),
530                0x01,
531                Register::DisplayTest.addr(),
532                0x01,
533                Register::DisplayTest.addr(),
534                0x01,
535                Register::DisplayTest.addr(),
536                0x01,
537            ]),
538            Transaction::transaction_end(),
539        ];
540        let mut spi = SpiMock::new(&expected_transactions);
541        let mut driver = Max7219::new(&mut spi)
542            .with_device_count(4)
543            .expect("valid device count");
544
545        driver
546            .test_all(true)
547            .expect("Test all enable should succeed");
548        spi.done();
549    }
550
551    #[test]
552    fn test_test_all_disable() {
553        let expected_transactions = [
554            Transaction::transaction_start(),
555            Transaction::write_vec(vec![Register::DisplayTest.addr(), 0x00]),
556            Transaction::transaction_end(),
557        ];
558        let mut spi = SpiMock::new(&expected_transactions);
559        let mut driver = Max7219::new(&mut spi);
560
561        driver
562            .test_all(false)
563            .expect("Test all disable should succeed");
564        spi.done();
565    }
566
567    #[test]
568    fn test_set_scan_limit_all_valid() {
569        let limit = 4;
570        let expected_transactions = [
571            Transaction::transaction_start(),
572            Transaction::write_vec(vec![Register::ScanLimit.addr(), limit - 1]),
573            Transaction::transaction_end(),
574        ];
575        let mut spi = SpiMock::new(&expected_transactions);
576        let mut driver = Max7219::new(&mut spi);
577
578        driver
579            .set_scan_limit_all(limit)
580            .expect("Set scan limit should succeed");
581        spi.done();
582    }
583
584    #[test]
585    fn test_set_scan_limit_all_invalid_low() {
586        let mut spi = SpiMock::new(&[]);
587        let mut driver = Max7219::new(&mut spi);
588
589        let result = driver.set_scan_limit_all(0);
590        assert_eq!(result, Err(Error::InvalidScanLimit));
591        spi.done();
592    }
593
594    #[test]
595    fn test_set_scan_limit_all_invalid_high() {
596        let mut spi = SpiMock::new(&[]); // No transactions expected for invalid input
597        let mut driver = Max7219::new(&mut spi);
598
599        let result = driver.set_scan_limit_all(9);
600        assert_eq!(result, Err(Error::InvalidScanLimit));
601        spi.done();
602    }
603
604    #[test]
605    fn test_set_decode_mode_all() {
606        let mode = DecodeMode::AllDigits;
607        let expected_transactions = [
608            Transaction::transaction_start(),
609            Transaction::write_vec(vec![
610                Register::DecodeMode.addr(),
611                mode.value(),
612                Register::DecodeMode.addr(),
613                mode.value(),
614                Register::DecodeMode.addr(),
615                mode.value(),
616                Register::DecodeMode.addr(),
617                mode.value(),
618            ]),
619            Transaction::transaction_end(),
620        ];
621        let mut spi = SpiMock::new(&expected_transactions);
622        let mut driver = Max7219::new(&mut spi)
623            .with_device_count(4)
624            .expect("valid device count");
625
626        driver
627            .set_decode_mode_all(mode)
628            .expect("Set decode mode should succeed");
629        spi.done();
630    }
631
632    #[test]
633    fn test_clear_display() {
634        let mut expected_transactions = Vec::new();
635        for digit_register in Register::digits() {
636            expected_transactions.push(Transaction::transaction_start());
637            expected_transactions.push(Transaction::write_vec(vec![digit_register.addr(), 0x00]));
638            expected_transactions.push(Transaction::transaction_end());
639        }
640
641        let mut spi = SpiMock::new(&expected_transactions);
642        let mut driver = Max7219::new(&mut spi);
643
644        driver
645            .clear_display(0)
646            .expect("Clear display should succeed");
647        spi.done();
648    }
649
650    #[test]
651    fn test_clear_display_invalid_index() {
652        let mut spi = SpiMock::new(&[]); // No transactions expected for invalid index
653        let mut driver = Max7219::new(&mut spi)
654            .with_device_count(1)
655            .expect("valid device count");
656
657        let result = driver.clear_display(1);
658        assert_eq!(result, Err(Error::InvalidDeviceIndex));
659        spi.done();
660    }
661
662    #[test]
663    fn test_write_raw_digit() {
664        let device_index = 0;
665        let digit = 3;
666        let data = 0b10101010;
667        let expected_transactions = [
668            Transaction::transaction_start(),
669            Transaction::write_vec(vec![Register::Digit3.addr(), data]),
670            Transaction::transaction_end(),
671        ];
672        let mut spi = SpiMock::new(&expected_transactions);
673        let mut driver = Max7219::new(&mut spi);
674
675        driver
676            .write_raw_digit(device_index, digit, data)
677            .expect("Write raw digit should succeed");
678        spi.done();
679    }
680
681    #[test]
682    fn test_write_raw_digit_invalid_digit() {
683        let mut spi = SpiMock::new(&[]); // No transactions expected for invalid digit
684        let mut driver = Max7219::new(&mut spi);
685
686        let result = driver.write_raw_digit(0, 8, 0x00); // Digit 8 is invalid
687
688        assert_eq!(result, Err(Error::InvalidDigit));
689
690        spi.done();
691    }
692
693    #[test]
694    fn test_set_intensity_valid() {
695        let device_index = 0;
696        let intensity = 0x0A;
697        let expected_transactions = [
698            Transaction::transaction_start(),
699            Transaction::write_vec(vec![Register::Intensity.addr(), intensity]),
700            Transaction::transaction_end(),
701        ];
702        let mut spi = SpiMock::new(&expected_transactions);
703        let mut driver = Max7219::new(&mut spi);
704
705        driver
706            .set_intensity(device_index, intensity)
707            .expect("Set intensity should succeed");
708        spi.done();
709    }
710
711    #[test]
712    fn test_set_intensity_invalid() {
713        let mut spi = SpiMock::new(&[]); // No transactions expected for invalid input
714        let mut driver = Max7219::new(&mut spi);
715
716        let result = driver.set_intensity(0, 0x10); // Invalid intensity > 0x0F
717        assert_eq!(result, Err(Error::InvalidIntensity));
718        spi.done();
719    }
720
721    #[test]
722    fn test_init() {
723        // Mock the sequence of calls made by init() for 1 device
724        // 1. power_on() -> Shutdown 0x01
725        // 2. test_all(false) -> DisplayTest 0x00
726        // 3. set_scan_limit_all(NUM_DIGITS) -> ScanLimit (NUM_DIGITS-1)
727        // 4. set_decode_mode_all(NoDecode) -> DecodeMode 0x00
728        // 5. clear_all() -> 8 separate calls to write_all_registers for each digit reg
729
730        // Use vec![] macro to create the vector with all expected transactions
731        let expected_transactions = vec![
732            // 1. power_on (write_all_registers)
733            Transaction::transaction_start(),
734            Transaction::write_vec(vec![Register::Shutdown.addr(), 0x01]),
735            Transaction::transaction_end(),
736            // 2. test_all(false) (write_all_registers)
737            Transaction::transaction_start(),
738            Transaction::write_vec(vec![Register::DisplayTest.addr(), 0x00]),
739            Transaction::transaction_end(),
740            // 3. set_scan_limit_all (write_all_registers)
741            Transaction::transaction_start(),
742            Transaction::write_vec(vec![Register::ScanLimit.addr(), NUM_DIGITS - 1]),
743            Transaction::transaction_end(),
744            // 4. set_decode_mode_all (write_all_registers)
745            Transaction::transaction_start(),
746            Transaction::write_vec(vec![
747                Register::DecodeMode.addr(),
748                DecodeMode::NoDecode.value(),
749            ]),
750            Transaction::transaction_end(),
751            // 5. clear_all() - 8 separate write_all_registers calls, one for each digit reg
752            // Unroll the loop for clarity and to include all transactions in the vec![] macro
753            Transaction::transaction_start(),
754            Transaction::write_vec(vec![Register::Digit0.addr(), 0x00]),
755            Transaction::transaction_end(),
756            Transaction::transaction_start(),
757            Transaction::write_vec(vec![Register::Digit1.addr(), 0x00]),
758            Transaction::transaction_end(),
759            Transaction::transaction_start(),
760            Transaction::write_vec(vec![Register::Digit2.addr(), 0x00]),
761            Transaction::transaction_end(),
762            Transaction::transaction_start(),
763            Transaction::write_vec(vec![Register::Digit3.addr(), 0x00]),
764            Transaction::transaction_end(),
765            Transaction::transaction_start(),
766            Transaction::write_vec(vec![Register::Digit4.addr(), 0x00]),
767            Transaction::transaction_end(),
768            Transaction::transaction_start(),
769            Transaction::write_vec(vec![Register::Digit5.addr(), 0x00]),
770            Transaction::transaction_end(),
771            Transaction::transaction_start(),
772            Transaction::write_vec(vec![Register::Digit6.addr(), 0x00]),
773            Transaction::transaction_end(),
774            Transaction::transaction_start(),
775            Transaction::write_vec(vec![Register::Digit7.addr(), 0x00]),
776            Transaction::transaction_end(),
777        ];
778
779        let mut spi = SpiMock::new(&expected_transactions);
780        let mut driver = Max7219::new(&mut spi);
781
782        driver.init().expect("Init should succeed");
783        spi.done();
784    }
785
786    #[test]
787    fn test_write_device_register_valid_index() {
788        let expected_transactions = [
789            Transaction::transaction_start(),
790            Transaction::write_vec(vec![
791                Register::Shutdown.addr(),
792                0x01,
793                0x00, // no-op for second device in chain
794                0x00,
795            ]),
796            Transaction::transaction_end(),
797        ];
798        let mut spi = SpiMock::new(&expected_transactions);
799        let mut driver = Max7219::new(&mut spi)
800            .with_device_count(2)
801            .expect("Should accept valid count");
802
803        driver
804            .write_device_register(0, Register::Shutdown, 0x01)
805            .expect("should write register");
806
807        spi.done();
808    }
809
810    #[test]
811    fn test_write_device_register_invalid_index() {
812        let mut spi = SpiMock::new(&[]); // No SPI transactions expected
813        let mut driver = Max7219::new(&mut spi)
814            .with_device_count(2)
815            .expect("Should accept valid count");
816
817        let result = driver.write_device_register(2, Register::Shutdown, 0x01); // Index 2 is invalid for device_count=2
818        assert_eq!(result, Err(Error::InvalidDeviceIndex));
819
820        spi.done();
821    }
822
823    #[test]
824    fn test_write_all_registers_valid() {
825        let expected_transactions = [
826            Transaction::transaction_start(),
827            Transaction::write_vec(vec![
828                Register::Intensity.addr(),
829                0x01,
830                Register::Intensity.addr(),
831                0x01,
832            ]),
833            Transaction::transaction_end(),
834        ];
835        let mut spi = SpiMock::new(&expected_transactions);
836        let mut driver = Max7219::new(&mut spi)
837            .with_device_count(2)
838            .expect("Should accept valid count");
839
840        driver
841            .write_all_registers(&[(Register::Intensity, 0x01), (Register::Intensity, 0x01)])
842            .expect("should  write all registers");
843
844        spi.done();
845    }
846
847    #[test]
848    fn test_test_device_enable_disable() {
849        let expected_transactions = [
850            Transaction::transaction_start(),
851            Transaction::write_vec(vec![Register::DisplayTest.addr(), 0x01]),
852            Transaction::transaction_end(),
853            Transaction::transaction_start(),
854            Transaction::write_vec(vec![Register::DisplayTest.addr(), 0x00]),
855            Transaction::transaction_end(),
856        ];
857        let mut spi = SpiMock::new(&expected_transactions);
858        let mut driver = Max7219::new(&mut spi);
859
860        driver
861            .test_device(0, true)
862            .expect("Enable test mode failed");
863        driver
864            .test_device(0, false)
865            .expect("Disable test mode failed");
866        spi.done();
867    }
868
869    #[test]
870    fn test_set_device_scan_limit_valid() {
871        let scan_limit = 4;
872
873        let expected_transactions = [
874            Transaction::transaction_start(),
875            Transaction::write_vec(vec![Register::ScanLimit.addr(), scan_limit - 1]),
876            Transaction::transaction_end(),
877        ];
878        let mut spi = SpiMock::new(&expected_transactions);
879        let mut driver = Max7219::new(&mut spi);
880
881        driver
882            .set_device_scan_limit(0, scan_limit)
883            .expect("Scan limit set failed");
884        spi.done();
885    }
886
887    #[test]
888    fn test_set_device_scan_limit_invalid() {
889        let mut spi = SpiMock::new(&[]);
890        let mut driver = Max7219::new(&mut spi);
891
892        let result = driver.set_device_scan_limit(0, 0); // invalid: below range
893        assert_eq!(result, Err(Error::InvalidScanLimit));
894
895        let result = driver.set_device_scan_limit(0, 9); // invalid: above range
896        assert_eq!(result, Err(Error::InvalidScanLimit));
897        spi.done();
898    }
899
900    #[test]
901    fn test_set_device_decode_mode() {
902        let mode = DecodeMode::Digits0To3;
903        let expected_transactions = [
904            Transaction::transaction_start(),
905            Transaction::write_vec(vec![Register::DecodeMode.addr(), mode.value()]),
906            Transaction::transaction_end(),
907        ];
908        let mut spi = SpiMock::new(&expected_transactions);
909        let mut driver = Max7219::new(&mut spi);
910
911        driver
912            .set_device_decode_mode(0, mode)
913            .expect("Set decode mode failed");
914        spi.done();
915    }
916
917    #[test]
918    fn test_set_intensity_all() {
919        let intensity = 0x05;
920        let expected_transactions = [
921            Transaction::transaction_start(),
922            Transaction::write_vec(vec![
923                Register::Intensity.addr(),
924                intensity,
925                Register::Intensity.addr(),
926                intensity,
927            ]),
928            Transaction::transaction_end(),
929        ];
930        let mut spi = SpiMock::new(&expected_transactions);
931        let mut driver = Max7219::new(&mut spi)
932            .with_device_count(2)
933            .expect("valid count");
934
935        driver
936            .set_intensity_all(intensity)
937            .expect("Set intensity all failed");
938        spi.done();
939    }
940}