Registers Module
In this chapter, we will work on the registers module(registers.rs). We could use the register values directly, but using enums is more idiomatic and provides better type safety. We will define two enums here: Register and DecodeMode.
The Register enum holds the addresses of each digit register and control register values, which we saw in the data transfer section. The DecodeMode enum is for configuring automatic digit decoding on seven-segment displays. For LED matrix displays, it should be set to NoDecode.
Add the neceessary imports for the module:
#![allow(unused)]
fn main() {
use crate::{Result, error::Error};
}
The Register Enum
In this enum, we declare all the MAX7219 registers, with each name matching its register address. To make working with these registers easier, we also implement some basic traits that let us copy and compare the register values.
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Register {
/// No-op register
NoOp = 0x00,
/// Digit 0 register
Digit0 = 0x01,
/// Digit 1 register
Digit1 = 0x02,
/// Digit 2 register
Digit2 = 0x03,
/// Digit 3 register
Digit3 = 0x04,
/// Digit 4 register
Digit4 = 0x05,
/// Digit 5 register
Digit5 = 0x06,
/// Digit 6 register
Digit6 = 0x07,
/// Digit 7 register
Digit7 = 0x08,
/// Decode mode register
DecodeMode = 0x09,
/// Intensity register
Intensity = 0x0A,
/// Scan limit register
ScanLimit = 0x0B,
/// Shutdown register
Shutdown = 0x0C,
/// Display test register
DisplayTest = 0x0F,
}
}
Next, we will add some functions that will be helpful later.
First, we will add the addr method that converts a register into its raw u8 value. This is useful when we need to send the register address to the MAX7219 chip.
Next, we will add try_digit function that takes a number and tries to convert it into the matching digit register (Digit0 to Digit7). If the number is out of range, it returns an invalid digit error. If you are wondering why I didn’t implement the TryFrom trait, it is because I want to accept only digit registers. Later in the code, when the user sends a digit as input, we need a function to check if it is a valid digit. By the way, this is an internal function we will use only inside the crate to validate user input. So, we mark it with pub(crate).
We also declare another helper function that gives us an iterator. This returns all the digit registers. This is useful when you want to loop through all digits - for example, to update every row or column on the display.
#![allow(unused)]
fn main() {
impl Register {
/// Convert register to u8 value
pub const fn addr(self) -> u8 {
self as u8
}
/// Try to convert a digit index (0-7) into a corresponding `Register::DigitN`.
pub(crate) fn try_digit(digit: u8) -> Result<Self> {
match digit {
0 => Ok(Register::Digit0),
1 => Ok(Register::Digit1),
2 => Ok(Register::Digit2),
3 => Ok(Register::Digit3),
4 => Ok(Register::Digit4),
5 => Ok(Register::Digit5),
6 => Ok(Register::Digit6),
7 => Ok(Register::Digit7),
_ => Err(Error::InvalidDigit),
}
}
/// Returns an iterator over all digit registers (Digit0 to Digit7).
///
/// Useful for iterating through display rows or columns when writing
/// to all digits of a MAX7219 device in order.
pub fn digits() -> impl Iterator<Item = Register> {
[
Register::Digit0,
Register::Digit1,
Register::Digit2,
Register::Digit3,
Register::Digit4,
Register::Digit5,
Register::Digit6,
Register::Digit7,
]
.into_iter()
}
}
}