diff --git a/_Boards/Atmel/Board_Nano/MFBoards.h b/_Boards/Atmel/Board_Nano/MFBoards.h index 86d0ace3..f022c2e4 100644 --- a/_Boards/Atmel/Board_Nano/MFBoards.h +++ b/_Boards/Atmel/Board_Nano/MFBoards.h @@ -43,7 +43,7 @@ #define MAX_BUTTONS 18 #endif #ifndef MAX_LEDSEGMENTS -#define MAX_LEDSEGMENTS 1 +#define MAX_LEDSEGMENTS 4 #endif #ifndef MAX_ENCODERS #define MAX_ENCODERS 9 @@ -95,7 +95,6 @@ #ifndef MF_MAX_DEVICEMEM #define MF_MAX_DEVICEMEM 300 // max. memory size for devices #endif - #endif // MFBoards.h \ No newline at end of file diff --git a/_Boards/Atmel/Board_ProMicro/MFBoards.h b/_Boards/Atmel/Board_ProMicro/MFBoards.h index feadacc4..61eb6bd7 100644 --- a/_Boards/Atmel/Board_ProMicro/MFBoards.h +++ b/_Boards/Atmel/Board_ProMicro/MFBoards.h @@ -43,7 +43,7 @@ #define MAX_BUTTONS 18 #endif #ifndef MAX_LEDSEGMENTS -#define MAX_LEDSEGMENTS 1 +#define MAX_LEDSEGMENTS 4 #endif #ifndef MAX_ENCODERS #define MAX_ENCODERS 9 diff --git a/_Boards/Atmel/Board_Uno/MFBoards.h b/_Boards/Atmel/Board_Uno/MFBoards.h index 4b12451a..6846aef5 100644 --- a/_Boards/Atmel/Board_Uno/MFBoards.h +++ b/_Boards/Atmel/Board_Uno/MFBoards.h @@ -43,7 +43,7 @@ #define MAX_BUTTONS 18 #endif #ifndef MAX_LEDSEGMENTS -#define MAX_LEDSEGMENTS 1 +#define MAX_LEDSEGMENTS 4 #endif #ifndef MAX_ENCODERS #define MAX_ENCODERS 9 diff --git a/platformio.ini b/platformio.ini index 92f017c8..de33b48c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,7 @@ ; Common build settings across all devices [env] lib_deps = - https://github.com/MobiFlight/LedControl#1.1.0 + ;https://github.com/MobiFlight/LedControl#1.1.0 waspinator/AccelStepper @ 1.61 https://github.com/MobiFlight/LiquidCrystal_I2C#v1.1.4 https://github.com/MobiFlight/Arduino-CmdMessenger#4.2.1 diff --git a/src/MF_Segment/LedControl_dual.cpp b/src/MF_Segment/LedControl_dual.cpp new file mode 100644 index 00000000..e6926c1e --- /dev/null +++ b/src/MF_Segment/LedControl_dual.cpp @@ -0,0 +1,492 @@ +// ======================================================================= +// @file LedControl_dual.cpp +// +// @project MobiFlight custom Firmware +// +// @author GiorgioCC (g.crocic@gmail.com) - 2023-06-29 +// @modifiedby GiorgioCC - 2023-07-09 20:13 +// +// A library for controlling LED 7-segment displays with either +// a MAX7219/MAX7221 or a TM1637 (4/6 digit) driver +// Portions of code derived from: +// - LedControl - A library for controlling Leds with a MAX7219/MAX7221 +// Copyright (c) 2007 Eberhard Fahle +// - TM1637TinyDisplay - TM1637 Tiny Display library by Jason A. Cox +// (https://github.com/jasonacox) +// +// ======================================================================= + +#include "LedControl_dual.h" + +// ======================================================================= +// Common Definitions +// ======================================================================= + +// Segments to be switched on for characters and digits on 7-Segment Displays +// bit/segment sequence: dABCDEFG +// A +// --- +// F | | B +// -G- +// E | | C +// --- .d +// D + +const static byte charTable[128] PROGMEM = { + // 0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 + 0b01111110, 0b00110000, 0b01101101, 0b01111001, 0b00110011, 0b01011011, 0b01011111, 0b01110000, + // 8 ,9 ,A ,B ,C ,D ,E ,F + 0b01111111, 0b01111011, 0b01110111, 0b00011111, 0b00001101, 0b00111101, 0b01001111, 0b01000111, + // <0x10> ,<0x11> ,<0x12> ,<0x13> ,<0x14> ,<0x15> ,<0x16> ,<0x17> + 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, + // <0x18> ,<0x19> ,<0x1A> ,<0x1B> ,<0x1C> ,<0x1D> ,<0x1E> ,<0x1F> + 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, + // (space) ,! ," ,# ,$ ,% ,& ,' + 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, + // ( ,) ,* ,+ ,, ,- ,. ,/ + 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000000, 0b00000001, 0b10000000, 0b00100101, + // 0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 + 0b01111110, 0b00110000, 0b01101101, 0b01111001, 0b00110011, 0b01011011, 0b01011111, 0b01110000, + // 8 ,9 ,: ,; ,< ,= ,> ,? + 0b01111111, 0b01111011, 0b00000000, 0b00000000, 0b00000000, 0b00001001, 0b00000000, 0b00000000, + // @ ,A ,0b ,C ,D ,E ,F ,G + 0b00000000, 0b01110111, 0b01111111, 0b01001110, 0b01111110, 0b01001111, 0b01000111, 0b01011111, + // H ,I ,J ,K ,L ,M ,N ,O + 0b00110111, 0b00110000, 0b01111100, 0b00000000, 0b00001110, 0b00000000, 0b01110110, 0b01111110, + // P ,Q ,R ,S ,T ,U ,V ,W + 0b01100111, 0b01111110, 0b01110111, 0b01011011, 0b01000110, 0b00111110, 0b00000000, 0b00000000, + // X ,Y ,Z ,[ ,\ ,] ,^ ,_ + 0b00000000, 0b00110011, 0b01101101, 0b01001110, 0b00010011, 0b01111000, 0b00000000, 0b00001000, + // ` ,a ,0b ,c ,d ,e ,f ,g + 0b00000000, 0b01111101, 0b00011111, 0b00001101, 0b00111101, 0b01001111, 0b01000111, 0b01111011, + // h ,i ,j ,k ,l ,m ,n ,o + 0b00110111, 0b00110000, 0b00111000, 0b00000000, 0b00000110, 0b00000000, 0b00010101, 0b00011101, + // p ,q ,r ,s ,t ,u ,v ,w + 0b01100111, 0b01110011, 0b00000101, 0b01011011, 0b00001111, 0b00011100, 0b00000000, 0b00000000, + // x ,y ,z ,{ ,| ,} ,~ ,(delete) + 0b00000000, 0b00100111, 0b01101101, 0b01001110, 0b00000000, 0b01111000, 0b00000000, 0b00000000}; + +// ======================================================================= +// MAX72xx Definitions +// ======================================================================= + +// the opcodes for the MAX7221 and MAX7219 +enum { + OP_NOOP = 0, + OP_DIGIT0, + OP_DIGIT1, + OP_DIGIT2, + OP_DIGIT3, + OP_DIGIT4, + OP_DIGIT5, + OP_DIGIT6, + OP_DIGIT7, + OP_DECODEMODE, + OP_INTENSITY, + OP_SCANLIMIT, + OP_SHUTDOWN, + OP_DISPLAYTEST = 15 +}; + +#ifdef LEDCONTROL_NO_BUF + uint8_t LedControl::rawdata[16] = {0}; +#endif + +// ======================================================================= +// TM1637 Definitions +// ======================================================================= + +// TM1637 Commands +// +// Communication Sequence (Automatic Address) +// Cmd1: Start + CmdSetData + ACK + Stop + +// Cmd2+Data: Start + CmdSetAddress + ACK + (Data + ACK) * N + +// Cmd3: Start + CmdDisplay + ACK + Stop +// +// Communication Sequence (Fixed Address) +// Cmd1: Start + CmdSetData + ACK + Stop +// Cmd2+Data: (Start + Command + ACK + Data + ACK) * N + Stop + +// Cmd3: Start + Command3 + ACK + Stop +// +// CmdSetData - Data command settings (byte) - TM1637_I2C_COMM1 +// B7 B6 B5 B4 B3 B2 B1 B0 - Function Description +// 0 1 0 0 _ _ 0 0 - (Data read/write) Write data to display register +// 0 1 0 0 _ _ 1 0 - (Data read/write) Read key scan data +// 0 1 0 0 _ 0 _ _ - (Address mode) Automatic address adding +// 0 1 0 0 _ 1 _ _ - (Address mode) Fixed address +// 0 1 0 0 0 _ _ _ - (Test mode) Normal mode +// 0 1 0 0 1 _ _ _ - (Test mode) Test mode +// +// CmdSetAddress - Set Address - Digit (byte) - TM1637_I2C_COMM2 +// B7 B6 B5 B4 B3 B2 B1 B0 - Function Description +// 1 1 0 0 0 0 0 0 - Digit 1 - C0H Grid1 +// 1 1 0 0 0 0 0 1 - Digit 2 - C1H Grid2 +// 1 1 0 0 0 0 1 0 - Digit 3 - C2H Grid3 +// 1 1 0 0 0 0 1 1 - Digit 4 - C3H Grid4 +// 1 1 0 0 0 1 0 0 - Digit 5 - C4H Grid5 +// 1 1 0 0 0 1 0 1 - Digit 6 - C5H Grid6 +// +// CmdDisplay - Set Display - Digit (byte) - TM1637_I2C_COMM3 +// B7 B6 B5 B4 B3 B2 B1 B0 - Function Description +// 1 0 0 0 _ 0 0 0 - Brightness - Pulse width is set as 1/16 +// 1 0 0 0 _ 0 0 1 - Brightness - Pulse width is set as 2/16 +// 1 0 0 0 _ 0 1 0 - Brightness - Pulse width is set as 4/16 +// 1 0 0 0 _ 0 1 1 - Brightness - Pulse width is set as 10/16 +// 1 0 0 0 _ 1 0 0 - Brightness - Pulse width is set as 11/16 +// 1 0 0 0 _ 1 0 1 - Brightness - Pulse width is set as 12/16 +// 1 0 0 0 _ 1 1 0 - Brightness - Pulse width is set as 13/16 +// 1 0 0 0 _ 1 1 1 - Brightness - Pulse width is set as 14/16 +// 1 0 0 0 0 _ _ _ - Display OFF +// 1 0 0 0 1 _ _ _ - Display ON + +#define TM1637_I2C_COMM1 0x40 // CmdSetData 0b01000000 +#define TM1637_I2C_COMM2 0xC0 // CmdSetAddress 0b11000000 +#define TM1637_I2C_COMM3 0x80 // CmdDisplay 0b10000000 +#define TM1637_I2C_COMM1F 0x44 // CmdSetData - fixedAddress 0b11000100 +// ======================================================================= + +// Digit sequence map for 6 digit displays +const uint8_t digitmap[] = {2, 1, 0, 5, 4, 3}; + +// Configuration: +// csPin < 0xFD -> MAX72xx +// csPin = 0xFD -> TM1637 4-digit +// csPin = 0xFE -> TM1637 6-digit +// csPin = 0xFF -> uninitialized +void LedControl::begin(uint8_t dataPin, uint8_t clkPin, uint8_t csPin, uint8_t numDevices) +{ + IO_DTA = dataPin; + IO_CLK = clkPin; + IO_CS = csPin; + if (isMAX()) { + if ((numDevices - 1) > 7) numDevices = 8; + maxUnits = numDevices; + pinMode(IO_DTA, OUTPUT); + pinMode(IO_CLK, OUTPUT); + pinMode(IO_CS, OUTPUT); + digitalWrite(IO_CS, HIGH); + for (uint8_t i = 0; i < maxUnits; i++) { + spiTransfer(i, OP_DISPLAYTEST, 0); + setScanLimit(i, 7); // scanlimit is set to max on startup + spiTransfer(i, OP_DECODEMODE, 0); // decode is done in source + clearDisplay(i); + shutdown(i, true); // we go into shutdown-mode on startup + } + } else { + maxUnits = (IO_CS == 0xFD ? 4 : 6); + // Both pins are set as inputs, allowing the pull-up resistors to pull them up + pinMode(IO_CLK, INPUT_PULLUP); + pinMode(IO_DTA, INPUT_PULLUP); + digitalWrite(IO_CLK, LOW); // Prepare '0' value as dominant + digitalWrite(IO_DTA, LOW); // Prepare '0' value as dominant + clearDisplay(0); + //setIntensity(0, MAX_BRIGHTNESS); + brightness = MAX_BRIGHTNESS; + shutdown(0, true); + } +} + +void LedControl::shutdown(uint8_t addr, bool b) +{ + if (isMAX()) { + if (addr >= maxUnits) return; + spiTransfer(addr, OP_SHUTDOWN, b ? 0 : 1); + } else { + uint8_t bri = brightness >> 1; + if(!b) bri |= 0x08; + // Write COMM3 + intensity + start(); + writeByte(TM1637_I2C_COMM3 + bri); + stop(); + } +} + +void LedControl::setIntensity(uint8_t addr, uint8_t intensity) +{ + if (intensity > 15) intensity = 15; + brightness = intensity; + if (isMAX()) { + if (addr >= maxUnits) return; + spiTransfer(addr, OP_INTENSITY, brightness); + } else { + if(intensity > 0) { + if(intensity > 1) intensity >>= 1; + intensity |= 0x08; + } + // Write COMM3 + intensity + start(); + writeByte(TM1637_I2C_COMM3 + intensity); + stop(); + } +} + +void LedControl::clearDisplay(uint8_t addr) +{ + if (isMAX()) { + if (addr >= maxUnits) return; + for (uint8_t i = 0; i < 8; i++) { + spiTransfer(addr, i + 1, 0); + } + } else { +#ifdef LEDCONTROL_NO_BUF + for(uint8_t i = 0; i < 8; i++) { + writeOneDigit(i, 0); + } +#else + memset(rawdata, 0, maxUnits); + writeBuffer(); +#endif + } +} + +void LedControl::setDigit(uint8_t addr, uint8_t digit, uint8_t value, bool dp, bool sendNow) +{ + if (addr >= maxUnits) return; + if ((value > 15) && (value != '-')) value = (uint8_t)' '; // Use space for invalid digit + if (dp) value |= 0x80; + setPattern(addr, digit, value, sendNow); +} + +void LedControl::setChar(uint8_t addr, uint8_t digit, char value, bool dp, bool sendNow) +{ + uint8_t v; + if (addr >= maxUnits) return; + v = (uint8_t)value; // Get rid of signedness + if (v > 127) v = 32; // undefined: replace with space char + if (dp) v |= 0x80; + setPattern(addr, digit, v, sendNow); +} + +void LedControl::setPattern(uint8_t addr, uint8_t digit, uint8_t value, bool sendNow) +{ + if(digit > getDigitCount()-1) return; + uint8_t v; + v = pgm_read_byte_near(charTable + (value & 0x7F)); + if (isMAX()) { + if (value & 0x80) v |= 0x80; + spiTransfer(addr, digit + 1, v); // Always send immediately for MAX + } else { + // Original data for MAX has the bit sequence: dABCDEFG + // Common TM1637 boards are connected so that they require: dGFEDCBA + // For the least effort, we alter the byte to be transmitted as: ABCDEFGd + // and then just transmit them reversed (from LSb to MSb) + v <<= 1; + if (value & 0x80) v |= 0x01; +#ifdef LEDCONTROL_NO_BUF + writeOneDigit(digit, v); +#else + rawdata[(maxUnits-1) - digit] = v; // Change only the individual affected digit in static buffer + if(sendNow) { + writeDigits(digit, 1); + } +#endif + } +} + +// ------------------------------------------------ +// MAX-specific driver methods +// ------------------------------------------------ + +void LedControl::setScanLimit(uint8_t addr, uint8_t limit) { + if(!isMAX()) return; + if(addr>=maxUnits) return; + if(limit > 7) return; + spiTransfer(addr, OP_SCANLIMIT,limit); +} + +void LedControl::spiTransfer(uint8_t addr, uint8_t opcode, uint8_t data) +{ + uint8_t offset = addr * 2; + uint8_t maxBytes = maxUnits * 2; + + // for (uint8_t i = 0; i < maxBytes; i++) rawdata[i] = (byte)0; + memset(rawdata, 0, maxBytes); + rawdata[offset + 1] = opcode; + rawdata[offset] = data; + + digitalWrite(IO_CS, LOW); + for (uint8_t i = maxBytes; i > 0; i--) { + // shiftOut(IO_DTA, IO_CLK, MSBFIRST, rawdata[i - 1]); + byte dta = rawdata[i - 1]; + for (uint8_t m = 0x80; m != 0; m >>= 1) { + // MSB first + digitalWrite(IO_DTA, (dta & m)); + digitalWrite(IO_CLK, HIGH); + digitalWrite(IO_CLK, LOW); + } + } + digitalWrite(IO_CS, HIGH); +} + +// ------------------------------------------------ +// TM-specific driver methods +// ------------------------------------------------ + +void LedControl::start() +{ + pinMode(IO_DTA, OUTPUT); + bitDelay(); +} + +void LedControl::stop() +{ + pinMode(IO_DTA, OUTPUT); + bitDelay(); + pinMode(IO_CLK, INPUT); + bitDelay(); + pinMode(IO_DTA, INPUT); + bitDelay(); +} + +bool LedControl::writeByte(uint8_t data, bool rvs) +{ + uint8_t msk = (rvs ? 0x80 : 0x01); + for (uint8_t i = 0; i < 8; i++) { + // CLK low + pinMode(IO_CLK, OUTPUT); + bitDelay(); + // Set data bit + pinMode(IO_DTA, (data & msk) ? INPUT : OUTPUT); + bitDelay(); + // CLK high + pinMode(IO_CLK, INPUT); + bitDelay(); + data = (rvs ? data << 1 : data >> 1); + } + // Wait for acknowledge + // CLK to zero + pinMode(IO_CLK, OUTPUT); + pinMode(IO_DTA, INPUT); + bitDelay(); + // CLK to high + pinMode(IO_CLK, INPUT); + bitDelay(); + uint8_t ack = digitalRead(IO_DTA); + if (ack == 0) pinMode(IO_DTA, OUTPUT); + bitDelay(); + pinMode(IO_CLK, OUTPUT); + bitDelay(); + return ack; +} + +#ifdef LEDCONTROL_NO_BUF + +void LedControl::writeOneDigit(uint8_t ndigit, uint8_t pattern) +{ + uint8_t b; + // Write COMM1 + start(); + writeByte(TM1637_I2C_COMM1F); // TM1637_I2C_COMM1 is also fine + stop(); + + start(); + ndigit = (maxUnits-1)-ndigit; + b = ((maxUnits == 4) ? ndigit : digitmap[ndigit]); + writeByte(TM1637_I2C_COMM2 + b); + // Write only raw data bit-reversed (to use the existing data in MAX-format) + writeByte(pattern, true); + stop(); +} + +#endif + +// ========================================================= +// Methods for extended library +// ========================================================= + +#ifndef LEDCONTROL_NO_BUF + +void LedControl::writeDigits(uint8_t startd, uint8_t len) +{ + bool is4Digit = (maxUnits == 4); + uint8_t b; + + // Write COMM1 + start(); + writeByte(TM1637_I2C_COMM1); + stop(); + + uint8_t pos = (maxUnits-1) - startd; + b = (is4Digit ? pos : digitmap[pos+len-1]); + + start(); + writeByte(TM1637_I2C_COMM2 + b); + // Write the data bytes + if(pos + len > maxUnits) len = maxUnits - pos; + uint8_t k; + for (b = 0; b < len; b++) { + k = (is4Digit ? b : len - b - 1); + writeByte(rawdata[pos+k], true); + } + stop(); +} + +#endif + +#ifdef LEDCONTROL_EXTENDED + +void LedControl::showNumber(uint8_t addr, int32_t num, bool isHex, uint8_t dots, bool leading_zero, uint8_t roffset) +{ + uint8_t digits[8]; + uint8_t pos; + uint8_t maxlen = getDigitCount(); + bool minusRequired = (num < 0); + if(minusRequired) num = -num; + + for (uint8_t i = 0; i < 8; i++) digits[i] = (uint8_t)' '; + + pos = (maxlen-1)-roffset; // Reverse to use as counter + if (num == 0) { + if(leading_zero) { + for (uint8_t i = 0; i < pos; i++) { + digits[i] = '0'; + } + } + digits[pos] = '0'; + } else { + uint8_t dval; + do { + if (num == 0) { + if(leading_zero) { + digits[pos] = '0'; + if (minusRequired && pos == 0) digits[0] = '-'; + } else { + if (minusRequired) digits[pos] = '-'; + pos = 0; + } + } else { + if(!isHex) { + dval = num % 10; + digits[pos] = '0'+dval; + } else { + dval = num & 0x0F; + digits[pos] = ((dval > 9) ? 'A'-10 : '0') + dval; + } + } + if(!isHex) { + num /= 10; + } else { + num >>= 4; + } + } while((pos--) > 0); + } + showString(addr, (char*)digits, 0, dots); +} + +void LedControl::showString(uint8_t addr, char* s, uint8_t loffset, uint8_t dots) +{ + uint8_t maxlen = getDigitCount(); + uint8_t msk = 0x80 >> loffset; + for(uint8_t d = loffset; d < maxlen && (*s != 0); d++) { + uint8_t pos = (maxlen-1)-d; + setChar(addr, pos, *s++, ((dots & msk)!=0), false); + msk>>=1; + } + +#ifndef LEDCONTROL_NO_BUF + if(!isMAX()) writeBuffer(); +#endif +} + +#endif +// end \ No newline at end of file diff --git a/src/MF_Segment/LedControl_dual.h b/src/MF_Segment/LedControl_dual.h new file mode 100644 index 00000000..076ecfe6 --- /dev/null +++ b/src/MF_Segment/LedControl_dual.h @@ -0,0 +1,186 @@ +// ======================================================================= +// @file LedControl_dual.h +// +// @project MobiFlight custom Firmware +// +// @author GiorgioCC (g.crocic@gmail.com) - 2023-06-29 +// @modifiedby GiorgioCC - 2023-07-04 15:45 +// +// A library for controlling LED 7-segment displays with either +// a MAX7219/MAX7221 or a TM1637 (4/6 digit) driver +// Portions of code derived from: +// - LedControl - A library for controlling Leds with a MAX7219/MAX7221 +// Copyright (c) 2007 Eberhard Fahle +// - TM1637TinyDisplay - TM1637 Tiny Display library by Jason A. Cox +// (https://github.com/jasonacox) +// +// ======================================================================= +// This is basically a mix of two drivers in the same library (with common +// code parts factorized as much as possible): +// the type of driver to be used is determined with the value passed for +// the argument in the begin() call. +// Since the TM1637 does not use a CS line, special values (which are +// invalid for a MAX72xx) are used to configure the display as a TM1637. +// Specifically, csPin = 0xFD -> TM1637 4-digit, 0xFE -> TM1637 6-digit +// ======================================================================= +// Method signatures (and purpose) have been kept exactly as in the original +// LedControl library used in MF firmware, so the interface doesn't change; +// non-relevant arguments (particularly: for TM's) are ignored. +// A few methods (mostly for internal use) have been added. +// + +#ifndef __LEDCONTROL_DUAL__H__ +#define __LEDCONTROL_DUAL__H__ + +// This constant reduces buffer usage: a single (static) 16-byte buffer +// is used for all objects. However, for TM1637s, data is written +// byte-by-byte, making transmission of data blocks slower. +//#define LEDCONTROL_NO_BUF + +// This constant adds methods to print a decimal/hex number or a string +// (as opposite to writing individual chars). +#define LEDCONTROL_EXTENDED + +#include +// #include +#include + +#ifdef __AVR__ + #include +#elif defined(ESP8266) || defined(ESP32) + #include +#else + #define pgm_read_byte(addr) \ + (*(const unsigned char *)(addr)) // workaround for non-AVR +#endif + +// ======================================================================= +// TM1637 Definitions +// ======================================================================= + +#define DEFAULT_BIT_DELAY 100 + +// ======================================================================= + +class LedControl +{ +public: + enum { TM_4D = 0xFD, TM_6D = 0xFE, UNINITIALIZED = 0xFF}; + enum { ZERO_BRIGHTNESS = 0, MIN_BRIGHTNESS = 1, MAX_BRIGHTNESS = 15 }; + +private: + // Common + uint8_t IO_DTA = UNINITIALIZED; + uint8_t IO_CLK = UNINITIALIZED; + uint8_t IO_CS = UNINITIALIZED; + // MAX: Number of chained units + // TM: Number of digits (4 or 6) +#ifdef LEDCONTROL_NO_BUF + // For TM, buffer can't be static (= shared): either we are building + // the extended version (which adds a per-unit buffer instead of the static one) + // or we are forced to resort to digit-by-digit output + static +#endif + uint8_t rawdata[16]; + uint8_t maxUnits = 0; // MAX: N. of chained units; TM: N. of digits + uint8_t brightness = MAX_BRIGHTNESS; + void setPattern(uint8_t addr, uint8_t digit, uint8_t value, bool sendNow = true); + + // MAX-specific + void setScanLimit(uint8_t addr, uint8_t limit); + void spiTransfer(uint8_t addr, uint8_t opcode, uint8_t data); + + // TM-specific + // uint8_t dpSet = 0; + void bitDelay() { delayMicroseconds(DEFAULT_BIT_DELAY); }; + void start(void); + void stop(void); + bool writeByte(uint8_t data, bool rvs = false); + +#ifdef LEDCONTROL_NO_BUF + void writeOneDigit(uint8_t ndigit, uint8_t val); +#else + // Has buffer available + void writeDigits(uint8_t ndigit, uint8_t len); + void writeBuffer(void) { writeDigits(maxUnits-1, maxUnits); }; +#endif + +public: + LedControl() {}; + + void begin(uint8_t dataPin, uint8_t clkPin, uint8_t csPin, uint8_t numDevices = 1); + + bool isMAX(void) { return (IO_CS + 1) < TM_6D; } + uint8_t getDeviceCount(void) { return (isMAX() ? maxUnits : 1); }; + uint8_t getDigitCount(void) { return (isMAX() ? 8 : maxUnits); }; + + void shutdown(uint8_t addr, bool status); + void setIntensity(uint8_t addr, uint8_t intensity); + void clearDisplay(uint8_t addr = 0); + + // Display a hexadecimal digit. + // Params: + // addr address of the display (ignored for TM) + // digit the position of the digit on the display (0 is RIGHTMOST) + // value the value to be displayed. (0x00..0x0F) + // dp sets the decimal point. + // sendnow If false, buffers chars rather than sending them immediately (TM only; + // requires a sendAll() afterwards). + // Ignored for MAX, or if LEDCONTROL_NO_BUF is defined. + void setDigit(uint8_t addr, uint8_t digit, uint8_t value, bool dp = false, bool sendNow = true); + + // Display a character. + // There are only a few characters that make sense here : + //'0','1','2','3','4','5','6','7','8','9','0', + // 'A','b','c','d','E','F','H','L','P', + // '.','-','_',' ' + // Params: + // addr address of the display (ignored for TM) + // digit the position of the character on the display (0 is RIGHTMOST) + // value the character to be displayed. + // dp sets the decimal point. + // sendnow If false, buffers chars rather than sending them immediately (TM only; + // requires a sendAll() afterwards). + // Ignored for MAX, or if LEDCONTROL_NO_BUF is defined. + void setChar(uint8_t addr, uint8_t digit, char value, bool dp = false, bool sendNow = true); + +#ifndef LEDCONTROL_NO_BUF + // Sends the whole (previously filled) buffer content. + void sendAll(void) { writeBuffer(); }; +#endif + +#ifdef LEDCONTROL_EXTENDED + // Display a decimal number, with dot control + // + // Display the given argument as a decimal number. The dots between the digits + // can be individually controlled. + // + // @param addr The number of the chained device (MAX only, unused for TM) + // @param num The number to be shown + // @param isHex for Hex representation + // @param dots Dot/Colon enable. The argument is a bitmask, with each bit corresponding to a dot + // between the digits (RIGHT aligned). + // @param leading_zero When true, leading zeros are displayed. Otherwise unnecessary digits are + // blank. + // @param rstart The position of the LEAST significant digit (N-1 = leftmost, 0 = rightmost) + void showNumber(uint8_t addr, int32_t num, bool isHex = false, uint8_t dots = 0, + bool leading_zero = false, uint8_t roffset = 0); + + // Display a string + // + // Display the given string and if more than 4 characters, will scroll message on display + // + // @param addr The number of the chained device (MAX only, unused for TM) + // @param s The string to be shown + // @param lstart The position of the most significant digit (0 - leftmost, N-1 - rightmost) + // @param dots Dot/Colon enable. The argument is a bitmask, with each bit corresponding to a dot + // between the digits (LEFT aligned) + // See showString_P function for reading PROGMEM read-only flash memory space instead of RAM + void showString(uint8_t addr, char* s, uint8_t loffset = 0, uint8_t dots = 0); + //void showString_P(uint8_t addr, const char s[], uint8_t pos = 0, uint8_t dots = 0); + +#endif + +}; + +#endif //!__LEDCONTROL_DUAL__H__ diff --git a/src/MF_Segment/MFSegments.h b/src/MF_Segment/MFSegments.h index 461a5210..4e6dd92e 100644 --- a/src/MF_Segment/MFSegments.h +++ b/src/MF_Segment/MFSegments.h @@ -7,7 +7,7 @@ #pragma once #include -#include +#include class MFSegments {