diff --git a/max72xx/max72xx.go b/max72xx/max72xx.go index 27de1b539..bb8f0e485 100644 --- a/max72xx/max72xx.go +++ b/max72xx/max72xx.go @@ -4,20 +4,39 @@ package max72xx import ( "machine" + + "tinygo.org/x/drivers" ) type Device struct { - bus machine.SPI + bus drivers.SPI cs machine.Pin + n uint8 // Number of MAX7219 devices in series } -// NewDriver creates a new max7219 connection. The SPI wire must already be configured +const maxNumberOfDevices = 8 + +// NewDevice creates a new max7219 connection. The SPI wire must already be configured // The SPI frequency must not be higher than 10MHz. // parameter cs: the datasheet also refers to this pin as "load" pin. -func NewDevice(bus machine.SPI, cs machine.Pin) *Device { +func NewDevice(bus drivers.SPI, cs machine.Pin) *Device { return &Device{ bus: bus, cs: cs, + n: 1, + } +} + +// NewDeviceN creates a new max7219 connection with n devices in series. The SPI wire must already be configured +func NewDeviceN(bus drivers.SPI, cs machine.Pin, n uint8) *Device { + if n < 1 || n > maxNumberOfDevices { + n = 1 + } + + return &Device{ + bus: bus, + cs: cs, + n: n, } } @@ -31,7 +50,7 @@ func (driver *Device) Configure() { // SetScanLimit sets the scan limit. Maximum is 8. // Example: a 4 digit 7SegmentDisplay has a scan limit of 4 func (driver *Device) SetScanLimit(digitNumber uint8) { - driver.WriteCommand(REG_SCANLIMIT, digitNumber-1) + driver.writeToAll(REG_SCANLIMIT, digitNumber-1) } // SetIntensity sets the intensity of the diplays. @@ -40,7 +59,7 @@ func (driver *Device) SetIntensity(intensity uint8) { if intensity > 0x0F { intensity = 0x0F } - driver.WriteCommand(REG_INTENSITY, intensity) + driver.writeToAll(REG_INTENSITY, intensity) } // SetDecodeMode sets the decode mode for 7 segment displays. @@ -51,34 +70,34 @@ func (driver *Device) SetIntensity(intensity uint8) { func (driver *Device) SetDecodeMode(digitNumber uint8) { switch digitNumber { case 1: // only decode first digit - driver.WriteCommand(REG_DECODE_MODE, 0x01) + driver.writeToAll(REG_DECODE_MODE, 0x01) case 2, 3, 4: // decode digits 3-0 - driver.WriteCommand(REG_DECODE_MODE, 0x0F) + driver.writeToAll(REG_DECODE_MODE, 0x0F) case 8: // decode 8 digits - driver.WriteCommand(REG_DECODE_MODE, 0xFF) + driver.writeToAll(REG_DECODE_MODE, 0xFF) default: - driver.WriteCommand(REG_DECODE_MODE, 0x00) + driver.writeToAll(REG_DECODE_MODE, 0x00) } } // StartShutdownMode sets the IC into a low power shutdown mode. func (driver *Device) StartShutdownMode() { - driver.WriteCommand(REG_SHUTDOWN, 0x00) + driver.writeToAll(REG_SHUTDOWN, 0x00) } // StartShutdownMode sets the IC into normal operation mode. func (driver *Device) StopShutdownMode() { - driver.WriteCommand(REG_SHUTDOWN, 0x01) + driver.writeToAll(REG_SHUTDOWN, 0x01) } // StartDisplayTest starts a display test. func (driver *Device) StartDisplayTest() { - driver.WriteCommand(REG_DISPLAY_TEST, 0x01) + driver.writeToAll(REG_DISPLAY_TEST, 0x01) } // StopDisplayTest stops the display test and gets into normal operation mode. func (driver *Device) StopDisplayTest() { - driver.WriteCommand(REG_DISPLAY_TEST, 0x00) + driver.writeToAll(REG_DISPLAY_TEST, 0x00) } func (driver *Device) writeByte(data byte) { @@ -92,3 +111,69 @@ func (driver *Device) WriteCommand(register, data byte) { driver.writeByte(data) driver.cs.High() } + +// WriteCommandN sends a command to a specific MAX7219 in the chain +// +// The MAX7219 uses a 16-bit serial protocol divided into two 8-bit segments: +// - The first 8 bits (D15 to D8) represent the address of the register to write to. +// - The second 8 bits (D7 to D0) represent the data to be written to that register. +// +// Register Addresses: +// - 0x00: No-Op Register +// - 0x01 to 0x08: Digit Registers (control individual digits/segments) +// - 0x09: Decode Mode Register (configures decoding mode for digits) +// - 0x0A: Intensity Register (sets brightness level) +// - 0x0B: Scan Limit Register (sets number of digits to display) +// - 0x0C: Shutdown Register (controls shutdown mode) +// - 0x0F: Display Test Register (tests the display) +// +// Examples: +// To set the intensity to a medium level, send the following 16-bit data: +// +// WriteCommandN(0, Command{Register: REG_INTENSITY, Data: 10}) +// +// Example: To set digit 1 on the first max72xx to display the number 5 and DP, send the following 16-bit data: +// +// WriteCommandN(0, REG_DIGIT1, 5 | BcdDot}) +func (driver *Device) WriteCommandN(deviceNum uint8, register, data byte) { + if deviceNum >= driver.n { + deviceNum = 0 + } + + tmp := make([]struct { + Register byte + Data byte + }, driver.n) + for i := range tmp { + tmp[i].Register = REG_NOOP + tmp[i].Data = 0x00 + } + tmp[deviceNum].Data = data + tmp[deviceNum].Register = register + + driver.cs.Low() + for _, d := range tmp { + driver.writeByte(d.Register) + driver.writeByte(d.Data) + } + driver.cs.High() +} + +// writeToAll sends the same command to all devices in the chain +func (driver *Device) writeToAll(register, data byte) { + tmp := make([]struct { + Register byte + Data byte + }, driver.n) + for i := range tmp { + tmp[i].Register = register + tmp[i].Data = data + } + + driver.cs.Low() + for _, d := range tmp { + driver.writeByte(d.Register) + driver.writeByte(d.Data) + } + driver.cs.High() +} diff --git a/max72xx/registers.go b/max72xx/registers.go index ce3511d27..4d6cc91b5 100644 --- a/max72xx/registers.go +++ b/max72xx/registers.go @@ -16,3 +16,16 @@ const ( REG_SHUTDOWN byte = 0x0C // turn on for no shutdown mode REG_DISPLAY_TEST byte = 0x0F // turn off for no display test ) + +// BCD (B) codes for the 7-segment display +// B coding uses the first 4 bits to represent the character and bit 7 to represent the dot. +// 0 - 9 represent the numbers 0 to 9 +const ( + BcdDash byte = 10 + BcdE byte = 11 + BcdH byte = 12 + BcdL byte = 13 + BcdP byte = 14 + BcdBlank byte = 15 + BcdDot byte = 128 // 7 bit must be | with the character +)