1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
//! This crate provides a platform agnostic driver for the MAX7219 LED Driver IC.
//!
//! This driver was built using the [embedded-hal](https://docs.rs/embedded-hal/0.2.7/embedded_hal/) traits.
//!
//! ## Usage
//!
//! An updated version of the library should be available on crates.io. Add the following to your Cargo.toml to get is a dependency.
//! ```rust
//! [dependencies]
//! max7219-driver = "*"
//! ```
//!
//! ### Instantiating
//!
//! Create an instance of the driver with the `new` method, by passing SPI and Output pin instances.
//! ```rust
//! use max7219-driver::MAX7219;
//! let spi = // SPI instantiation code
//! let cs = // Output pin instantiation code
//! let mut max7219 = MAX7219::new(spi, cs).unwrap();
//! ```
//!
//! ### Initializing
//!
//! Initialize the driver instance with the `init_display` method. A boolean needs to be specified to indicate whether to clear the display after init or not.
//! ```rust
//! max7219.init_display(true);
//! ```
#![no_std]
use embedded_hal as hal;
use hal::blocking::spi::Write;
use hal::digital::v2::OutputPin;
use hal::spi::{Mode, Phase, Polarity};
/// This is a constant for convenience so that the SPI module can be configured with the correct SPI mode compatible the MAX7219 device
pub const MODE: Mode = Mode {
phase: Phase::CaptureOnFirstTransition,
polarity: Polarity::IdleLow,
};
/// The MAX7219 Driver that initializes and communicates with the MAX7219 IC or chain of ICs.
pub struct MAX7219<SPI, CS> {
spi: SPI,
cs: CS,
}
///
/// Possible Errors that can be raised either
/// during communication with the MAX7219 chip over SPI
/// or controlling the chip select pin.
///
#[derive(Debug)]
pub enum DriverError {
/// An error occurred when working with SPI
SpiError,
/// An error occurred when working with a PIN
PinError,
}
impl<SPI, CS> MAX7219<SPI, CS>
where
SPI: Write<u8>,
CS: OutputPin,
{
///
/// Constructor method. Creates a new instance of the MAX7219 driver.
///
pub fn new(spi: SPI, cs: CS) -> Result<Self, DriverError> {
let max7219 = MAX7219 { spi: spi, cs: cs };
Ok(max7219)
}
///
/// Transmits raw data to the MAX7219 IC.
///
/// # Arguments
///
/// * `arr` - slice of data that needs to be transmitted
///
/// # Errors
///
/// * `DriverError` - returned if there is an error during transfer or addressing device
///
pub fn transmit_raw_data(&mut self, arr: &[u8]) -> Result<(), DriverError> {
self.cs.set_low().map_err(|_| DriverError::PinError)?;
let transfer = self.spi.write(&arr).map_err(|_| DriverError::SpiError);
self.cs.set_high().map_err(|_| DriverError::PinError)?;
transfer
}
///
/// Configures the power mode of the MAX7219 IC.
///
/// # Arguments
///
/// * `mode` - one of the options in the `Shutdown` enum
///
pub fn config_power_mode(&mut self, mode: Shutdown) -> () {
let data: u8 = match mode {
Shutdown::NormalOperation => 0x01,
Shutdown::ShutDownMode => 0x00,
};
let send_array: [u8; 2] = [Command::Shutdown as u8, data];
// Transmit Data
self.transmit_raw_data(&send_array).unwrap();
}
///
/// Configures the decode mode on the input sent to the MAX7219 IC.
///
/// # Arguments
///
/// * `mode` - one of the options in the `DecodeMode` enum
///
pub fn config_decode_mode(&mut self, mode: DecodeMode) -> () {
// - Prepare Information to be Sent
// 8-bit Data/Command Corresponding to No Decode Mode
let data: u8 = match mode {
DecodeMode::NoDecode => 0x00,
DecodeMode::CodeB0 => 0x01,
DecodeMode::CodeB30 => 0x0F,
DecodeMode::CodeB70 => 0xFF,
};
// Package into array to pass to SPI write method
// Write method will grab array and send all data in it
let send_array: [u8; 2] = [Command::DecodeMode as u8, data];
// Transmit Data
self.transmit_raw_data(&send_array).unwrap();
}
///
/// Configures the intensity of the LEDs on the display connected to the MAX7219 IC.
///
/// # Arguments
///
/// * `mode` - one of the options in the `Intensity` enum
///
pub fn config_intensity(&mut self, mode: Intensity) -> () {
// - Prepare Information to be Sent
// 8-bit Data/Command Corresponding to No Decode Mode
let data: u8 = match mode {
Intensity::Min => 0x00,
Intensity::Ratio3_32 => 0x01,
Intensity::Ratio5_32 => 0x02,
Intensity::Ratio7_32 => 0x03,
Intensity::Ratio9_32 => 0x04,
Intensity::Ratio11_32 => 0x05,
Intensity::Ratio13_32 => 0x06,
Intensity::Ratio15_32 => 0x07,
Intensity::Ratio17_32 => 0x08,
Intensity::Ratio19_32 => 0x09,
Intensity::Ratio21_32 => 0x0A,
Intensity::Ratio23_32 => 0x0B,
Intensity::Ratio25_32 => 0x0C,
Intensity::Ratio27_32 => 0x0D,
Intensity::Ratio29_32 => 0x0E,
Intensity::Max => 0x0F,
};
// Package into array to pass to SPI write method
// Write method will grab array and send all data in it
let send_array: [u8; 2] = [Command::Intensity as u8, data];
// Transmit Data
self.transmit_raw_data(&send_array).unwrap();
}
///
/// Configures the scanlimit for the MAX7219 IC.
/// Applicable mostly to seven segment displays if certain digits (ex. on the left)
/// need not to be shown.
///
/// # Arguments
///
/// * `mode` - one of the options in the `ScanLimit` enum
///
pub fn config_scan_limit(&mut self, mode: ScanLimit) -> () {
// - Prepare Information to be Sent
// 8-bit Data/Command Corresponding to No Decode Mode
let data: u8 = match mode {
ScanLimit::Display0Only => 0x00,
ScanLimit::Display0And1 => 0x01,
ScanLimit::Display0To2 => 0x02,
ScanLimit::Display0To3 => 0x03,
ScanLimit::Display0To4 => 0x04,
ScanLimit::Display0To5 => 0x05,
ScanLimit::Display0To6 => 0x06,
ScanLimit::Display0To7 => 0x07,
};
// Package into array to pass to SPI write method
// Write method will grab array and send all data in it
let send_array: [u8; 2] = [Command::ScanLimit as u8, data];
// Transmit Data
self.transmit_raw_data(&send_array).unwrap();
}
///
/// Method to perform a visual test of the display.
/// If performing a test, display needs to be put back in normal operation mode after done.
///
/// # Arguments
///
/// * `mode` - one of the options in the `DisplayTest` enum
///
pub fn display_test(&mut self, mode: DisplayTest) -> () {
// - Prepare Information to be Sent
// 8-bit Data/Command Corresponding to No Decode Mode
let data: u8 = match mode {
DisplayTest::NormalOperationMode => 0x00,
DisplayTest::DisplayTestMode => 0x01,
};
// Package into array to pass to SPI write method
// Write method will grab array and send all data in it
let send_array: [u8; 2] = [Command::DisplayTest as u8, data];
// Transmit Data
self.transmit_raw_data(&send_array).unwrap();
}
///
/// Method to draw a row if the MAX7219 is driving an 8x8 LED dot matrix display.
/// Alternatively method will draw a digit in case the MAX7219 is driving a seven-segment display
///
/// # Arguments
///
/// * `digit_addr` - one of the options in the `DigitRowAddress` enum
/// * `led_data` - the led row or seven segment digit activation data
///
pub fn draw_row_or_digit(&mut self, digit_addr: DigitRowAddress, led_data: u8) -> () {
// Can do an exhaustive match of DigitRowAddress
// let addr: u8 = match digit_addr {
// DigitRowAddress::Digit0 => 0x01,
// DigitRowAddress::Digit1 => 0x02,
// DigitRowAddress::Digit2 => 0x03,
// DigitRowAddress::Digit3 => 0x04,
// DigitRowAddress::Digit4 => 0x05,
// DigitRowAddress::Digit5 => 0x06,
// DigitRowAddress::Digit6 => 0x07,
// DigitRowAddress::Digit7 => 0x08,
// };
// OR typecast DigitRowAddress passed through digit_addr as u8
let send_array: [u8; 2] = [digit_addr as u8, led_data];
self.transmit_raw_data(&send_array).unwrap();
()
}
///
/// Method to clear the display.
///
pub fn clear_display(&mut self) -> () {
for i in 1..9 {
self.transmit_raw_data(&[i]).unwrap();
}
}
///
/// Method to initialize the MAX7219 and the connected display.
/// This method has to be called before doing any display operations otherwise the display will not operate properly.
/// The method provides an option to leave the display uncleared after initalization.
///
/// # Arguments
///
/// * `clr_display` - Boolean that reflects whether the display should be cleared or not after init
///
pub fn init_display(&mut self, clr_display: bool) -> () {
// 1.a) Power Up Device
self.config_power_mode(Shutdown::NormalOperation);
// 1.b) Set up Decode Mode
self.config_decode_mode(DecodeMode::NoDecode);
// 1.c) Configure Scan Limit
self.config_scan_limit(ScanLimit::Display0To7);
// 1.d) Configure Intensity
self.config_intensity(Intensity::Ratio15_32);
// 1.e) Optional Screen Clear on Init
if clr_display {
self.clear_display();
}
}
}
/// Enumeration of commands in the register map of the MAX7219.
pub enum Command {
NoOp = 0x00,
DecodeMode = 0x09,
Intensity = 0x0A,
ScanLimit = 0x0B,
Shutdown = 0x0C,
DisplayTest = 0x0F,
}
/// Enumeration of the MAX7219 power modes.
pub enum Shutdown {
ShutDownMode,
NormalOperation,
}
/// Enumeration of the MAX7219 decode modes for BCD encoded input.
pub enum DecodeMode {
NoDecode = 0x00,
CodeB0 = 0x01,
CodeB30 = 0x0F,
CodeB70 = 0xFF,
}
/// Enumeration of the MAX7219 supported LED intensity values.
pub enum Intensity {
Min = 0x00,
Ratio3_32 = 0x01,
Ratio5_32 = 0x02,
Ratio7_32 = 0x03,
Ratio9_32 = 0x04,
Ratio11_32 = 0x05,
Ratio13_32 = 0x06,
Ratio15_32 = 0x07,
Ratio17_32 = 0x08,
Ratio19_32 = 0x09,
Ratio21_32 = 0x0A,
Ratio23_32 = 0x0B,
Ratio25_32 = 0x0C,
Ratio27_32 = 0x0D,
Ratio29_32 = 0x0E,
Max = 0x0F,
}
/// Enumeration of the MAX7219 display scan limits
pub enum ScanLimit {
Display0Only = 0x00,
Display0And1 = 0x01,
Display0To2 = 0x02,
Display0To3 = 0x03,
Display0To4 = 0x04,
Display0To5 = 0x05,
Display0To6 = 0x06,
Display0To7 = 0x07,
}
/// Enumeration of the MAX7219 display test modes
pub enum DisplayTest {
NormalOperationMode = 0x00,
DisplayTestMode = 0x01,
}
/// Enumeration of the MAX7219 digit/row addresses
#[repr(u8)]
#[derive(Debug)]
pub enum DigitRowAddress {
Digit0 = 0x01,
Digit1 = 0x02,
Digit2 = 0x03,
Digit3 = 0x04,
Digit4 = 0x05,
Digit5 = 0x06,
Digit6 = 0x07,
Digit7 = 0x08,
}
// Implement TryFrom Trait on RowAddress to retrieve corresponding digit
impl TryFrom<u8> for DigitRowAddress {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use DigitRowAddress::*;
Ok(match value {
0x01 => Digit0,
0x02 => Digit1,
0x03 => Digit2,
0x04 => Digit3,
0x05 => Digit4,
0x06 => Digit5,
0x07 => Digit6,
0x08 => Digit7,
invalid => return Err(invalid),
})
}
}