diff --git a/README.md b/README.md index cf7161c..9c1f5b1 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,42 @@ -# TM1638 library for Arduino +# Optimized TM1638 library for Arduino -This is a 3-pin serial TM1638 chip library for Arduino. It supports a combined LED driver controller and key-scan interface. +This is a 3-pin serial TM1638 chip library for Arduino, optimized for size and speed. It supports a combined LED driver controller and key-scan interface. ![TM1638 chip](https://raw.githubusercontent.com/Erriez/ErriezTM1638/master/extras/TM1638_pins.jpg) Displaying numbers, characters and reading keys depends on the hardware wiring and is not part of this library. A fully operational example for a board with 8 7-segment displays, 8 dual color LED's and 8 buttons which uses this library is available here: [JY-LKM1638](https://github.com/Erriez/ErriezLKM1638). - - ## Hardware Connect power and 3 data pins to an Arduino board DIGITAL pins: -* VDD (Power 5V +/- 10%) +* VDD (Power 3.3V - 5V) * GND (Ground) * DIO (Bi-directional data input/output) * STB (Chip select) * CLK (Clock) The following TM1638 pins should be connected to LED's and buttons in a matrix: -* K1~K3 (Key-scan data input) +* K1~K3 (Key-scan data input to read multiple key presses at the same time) * SEG/GRID (Output for LED matrix) +### Pins +| Pin | TM1638 | Arduino UNO / Nano / Mega2560 / Leonardo / Pro Micro | Node MCU | LOLIN32 | +| :--: | :----: | :--------------------------------------------------: | :------: | :-----: | +| 1 | VCC | 5V (or 3.3V) | GND | GND | +| 2 | GND | GND | 3V3 | 3V3 | +| 3 | CLK | 2 (Digital pin) | D2 | 0 | +| 4 | DIO | 3 (Digital pin) | D3 | 4 | +| 5 | STB0 | 4 (Digital pin) | D4 | 5 | -## Supported Arduino boards - -* All ATMega328P MCU: - * Arduino UNO - * Arduino Nano -* All ATMega32U4 MCU's: - * Arduino Leonardo - * Pro Micro -* All ATMega2560 MCU's: - * Arduino Mega2560 - - -* Other Arduino AVR MCU's may work, but are not tested. -* Other targets such as ESP8266/Lolin32 are not tested. -* The chip requires a 5V power supply and does not work at 3.3V. * Check maximum regulator / diode current to prevent a burnout when using lots of LED's. Some boards can provide only 100mA, others 800mA max. -* The DIO data pin requires a bi-directional level converter when connecting to 3.3V digital pins. - - - -## Library dependencies - -- None - - - -## Documentation - -[TM1638 Datasheet](https://github.com/Erriez/ErriezTM1638/blob/master/extras/TM1638_datasheet.pdf) - ## Example Examples | TM1638 | [Example](https://github.com/Erriez/ErriezTM1638/blob/master/examples/Example/Example.ino) - - ## Usage **Initialization** @@ -71,16 +46,20 @@ Examples | TM1638 | [Example](https://github.com/Erriez/ErriezTM1638/blob/master #include "TM1638.h" // Connect display pins to the Arduino DIGITAL pins -#define DIO_PIN 2 -#define SCL_PIN 3 -#define STB_PIN 4 - -// Create TM1638 object -TM1638 tm1638(DIO_PIN, SCL_PIN, STB_PIN); +#define TM1638_SCL_PIN 2 +#define TM1638_DIO_PIN 3 +#define TM1638_STB0_PIN 4 + +// Create tm1638 object +TM1638 tm1638(TM1638_SCL_PIN, TM1638_DIO_PIN, TM1638_STB0_PIN); + +void setup() +{ + // Initialize TM1638 + tm1638.begin(); +} ``` - - **Display on/off** ```c++ @@ -91,8 +70,6 @@ tm1638.displayOff(); tm1638.displayOn(); ``` - - **Turn all LED's off** ```c++ @@ -100,25 +77,36 @@ tm1638.displayOn(); tm1638.clear(); ``` - - -**Get key-scan** +**Get keys** ```c++ // Get 32-bit key-scan -uint32_t keys = tm1638.getKeyScan(); +uint32_t keys = tm1638.getKeys(); ``` +**Write Byte to display register** +```c++ +// Write segment LED's to the first display registers 0x00..0x0F with value 0x00..0xff to +// display numbers and characters. Just an example which depends on the hardware: +tm1638.writeData(0x01, 0x01); +``` -**Write display register** +**Write buffer to display registers** ```c++ -// Write segment LED's to the first display register -// The LED's turned on depends on your hardware SEG/GRID connections -// Experiment with the registers 0x00..0x0F value 0x00..0xff to display numbers -// and characters, for example: -tm1638.writeDisplayRegister(0x01, 0x01); +// Creat buffer with LED's +uint8_t buf[] = { 0b10000110, 0b00111111, 0b00111111, 0b00111111, 0b00111111, 0b00111111}; + +// Write buffer to TM1638 +tm1638.writeData(0x00, buf, sizeof(buf)); ``` +## Library dependencies + +- None + +## Documentation + +[TM1638 Datasheet](https://github.com/Erriez/ErriezTM1638/blob/master/extras/TM1638_datasheet.pdf) diff --git a/examples/Example/Example.ino b/examples/Example/Example.ino index d1df044..dc0fa0e 100644 --- a/examples/Example/Example.ino +++ b/examples/Example/Example.ino @@ -30,61 +30,76 @@ #include // Connect display pins to the Arduino DIGITAL pins -#define DIO_PIN 2 -#define SCL_PIN 3 -#define STB_PIN 4 +#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MICRO) || \ + defined(ARDUINO_AVR_PRO) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_AVR_LEONARDO) +#define TM1638_SCL_PIN 2 +#define TM1638_DIO_PIN 3 +#define TM1638_STB0_PIN 4 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) || defined(ARDUINO_ESP8266_NODEMCU) +#define TM1638_SCL_PIN D2 +#define TM1638_DIO_PIN D3 +#define TM1638_STB0_PIN D4 +#elif defined(ARDUINO_LOLIN32) +#define TM1638_SCL_PIN 0 +#define TM1638_DIO_PIN 4 +#define TM1638_STB0_PIN 5 +#else +#error "May work, but not tested on this target" +#endif + +// Create tm1638 object +TM1638 tm1638(TM1638_SCL_PIN, TM1638_DIO_PIN, TM1638_STB0_PIN); -TM1638 tm1638(DIO_PIN, SCL_PIN, STB_PIN); - -uint32_t keysLast = 0; void setup() { - Serial.begin(115200); - while (!Serial) { - ; - } - Serial.println(F("TM1638 example")); + Serial.begin(115200); + while (!Serial) { + ; + } + Serial.println(F("TM1638 example")); - // Turn display off (All LED's off) - tm1638.displayOff(); + // Initialize TM1638 + tm1638.begin(); - // Set brightness 0..7 - tm1638.setBrightness(3); + // Turn display off (All LED's off) + tm1638.displayOff(); - // Turn display on - tm1638.displayOn(); + // Clear display + tm1638.clear(); - // Clear display - tm1638.clear(); + // Set brightness 0..7 + tm1638.setBrightness(3); - // Write segment LED's to the first display register - // The LED's turned on depends on the connection of your board - // Experiment with the registers 0x00..0x0F value 0x00..0xff to display - // numbers and characters - tm1638.writeDisplayRegister(0x01, 0x01); + // Turn display on + tm1638.displayOn(); + + // Write segment LED's to the first display registers 0x00..0x0F with value 0x00..0xff to + // display numbers and characters. Just an example which depends on the hardware: + tm1638.writeData(0x01, 0x01); } void loop() { - uint32_t keys; - - // Read 32-bit keys - keys = tm1638.getKeyScan(); - - // Check key down - if (keysLast != keys) { - keysLast = keys; - - Serial.print(F("Keys: 0x")); - Serial.println(keys, HEX); - - if (keys) { - // Write segment LED's to first display register - tm1638.writeDisplayRegister(0, 0b00111111); - } else { - // Write segment LED's to first display register - tm1638.writeDisplayRegister(0, 0b00000110); + static uint32_t keysLast = 0; + uint32_t keys; + + // Read 32-bit keys + keys = tm1638.getKeys(); + + // Check key down + if (keysLast != keys) { + keysLast = keys; + + Serial.print(F("Keys: 0x")); + Serial.println(keys, HEX); + + if (keys) { + // Write segment LED's to first display register + tm1638.writeData(0, 0b00111111); + } else { + // Write segment LED's to first display register + tm1638.writeData(0, 0b00000110); + } } - } } diff --git a/library.properties b/library.properties index ca3d99e..e5b1df5 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=Erriez TM1638 -version=1.0.0 +version=1.1.0 author=Erriez maintainer=Erriez sentence=TM1638 button and LED controller library for Arduino -paragraph=This is a TM1638 LED driver controller with key-scan interface library for Arduino. +paragraph=This is an optimized TM1638 LED driver controller with key-scan interface library for Arduino. category=Display url=https://github.com/Erriez/ArduinoLibraryTM1638 architecture=* diff --git a/src/TM1638.cpp b/src/TM1638.cpp index d802ad0..36921b0 100644 --- a/src/TM1638.cpp +++ b/src/TM1638.cpp @@ -32,28 +32,66 @@ /*! * \brief TM1638 constructor - * \param dioPin TM1638 DIO pin. + * \details + * Easy to remember pin argument order: C-lock, D-ata, E-nable * \param sclPin TM1638 SCL pin. + * \param dioPin TM1638 DIO pin. * \param stbPin TM1638 STB pin. */ -TM1638::TM1638(uint8_t dioPin, uint8_t sclPin, uint8_t stbPin) : - _dioPin(dioPin), _clkPin(sclPin), _stbPin(stbPin), _displayOn(true), _brightness(5) +TM1638::TM1638(uint8_t clkPin, uint8_t dioPin, uint8_t stbPin) : + _displayOn(true), _brightness(5) +{ +#ifdef __AVR + // Calculate bit and port register for fast pin read and writes (AVR targets only) + _clkPort = digitalPinToPort(clkPin); + _dioPort = digitalPinToPort(dioPin); + _stbPort = digitalPinToPort(stbPin); + + _clkBit = digitalPinToBitMask(clkPin); + _dioBit = digitalPinToBitMask(dioPin); + _stbBit = digitalPinToBitMask(stbPin); +#else + // Use the slow digitalRead() and digitalWrite() functions for non-AVR targets + _dioPin = dioPin; + _clkPin = clkPin; + _stbPin = stbPin; +#endif +} + +/*! + * \brief Initialize controller. + */ +void TM1638::begin() { // Initialize pins - digitalWrite(_stbPin, HIGH); - digitalWrite(_dioPin, LOW); - digitalWrite(_clkPin, LOW); + TM1638_STB_HIGH(); + TM1638_DIO_LOW(); + TM1638_CLK_LOW(); // Set pin mode - pinMode(_dioPin, OUTPUT); - pinMode(_clkPin, OUTPUT); - pinMode(_stbPin, OUTPUT); - - // Clear display - clear(); + TM1638_DIO_OUTPUT(); + TM1638_CLK_OUTPUT(); + TM1638_STB_OUTPUT(); // Write _displayOn and _brightness to display control register writeDisplayControl(); + + // Data write with auto address increment + writeCommand(TM1638_CMD_DATA | TM1638_DATA_WRITE | TM1638_DATA_AUTO_INC_ADDR); +} + +/*! + * \brief Disable pins. + */ +void TM1638::end() +{ + TM1638_DIO_INPUT(); + TM1638_CLK_INPUT(); + TM1638_STB_INPUT(); + + TM1638_DIO_LOW(); + TM1638_CLK_LOW(); + TM1638_STB_LOW(); } /*! @@ -94,81 +132,114 @@ void TM1638::setBrightness(uint8_t brightness) */ void TM1638::clear() { - writeCommand(TM1638_WRITE_DATA); - - digitalWrite(_stbPin, LOW); + // Write buffer to display registers + TM1638_STB_LOW(); + writeByte((uint8_t)(TM1638_CMD_ADDR | 0x00)); + for (uint8_t i = 0; i < TM1638_NUM_GRIDS; i++) { + writeByte(0x00); + } + TM1638_STB_HIGH(); +} - // Clear all display registers to turn all LED's off - writeByte(TM1638_DISPLAY_ADDR); - for (uint8_t i = 0; i < 16; i++) { - writeByte(0); +/*! + * \brief Write display register + * \param address Display address 0x00..0x0F + * \param data Value 0x00..0xFF + */ +void TM1638::writeData(uint8_t address, uint8_t data) +{ + if (address <= TM1638_NUM_GRIDS) { + // Write byte to display register + TM1638_STB_LOW(); + writeByte((uint8_t)(TM1638_CMD_ADDR | address)); + writeByte(data); + TM1638_STB_HIGH(); } +} - digitalWrite(_stbPin, HIGH); +/*! + * \brief Write buffer to multiple display registers + * \details + * Write buffer to TM1638 with auto address increment + * \param address + * Display address 0x00..0x0F + * \param buf + * Buffer + * \param len + * Buffer length + */ +void TM1638::writeData(uint8_t address, const uint8_t *buf, uint8_t len) +{ + if ((address + len) <= TM1638_NUM_GRIDS) { + // Write buffer to display registers + TM1638_STB_LOW(); + writeByte((uint8_t)(TM1638_CMD_ADDR | address)); + for (uint8_t i = 0; i < len; i++) { + writeByte(buf[i]); + } + TM1638_STB_HIGH(); + } } /*! * \brief Get key states. * \return One or more buttons. One bit per button. */ -uint32_t TM1638::getKeyScan() +uint32_t TM1638::getKeys() { uint32_t keys = 0; - digitalWrite(_stbPin, LOW); - - // Read 4 key-scan registers - writeByte(TM1638_READ_KEYS); + // Read 4 Bytes key-scan registers + TM1638_STB_LOW(); + writeByte(TM1638_CMD_DATA | TM1638_DATA_READ_KEYS); for (uint8_t i = 0; i < 4; i++) { keys |= ((uint32_t)readByte() << (i * 8)); } - - digitalWrite(_stbPin, HIGH); + TM1638_STB_HIGH(); return keys; } +// ------------------------------------------------------------------------------------------------- /*! - * \brief Write display control. + * \brief Write display control register. + * \details + * Set brightness and display on/off */ void TM1638::writeDisplayControl() { - // Write to display control register - writeCommand(TM1638_WRITE_DISPLAY_CTRL | - (_displayOn ? 0x08 : 0x00) | - _brightness); + writeCommand((uint8_t)(TM1638_CMD_CTRL | + (_displayOn ? TM1638_CTRL_DISPLAY_ON : TM1638_CTRL_DISPLAY_OFF) | + _brightness)); } /*! - * \brief Write display register - * \param address Display address 0x00..0x0F - * \param data Value 0x00..0xFF + * \brief Write command to TM1638. */ -void TM1638::writeDisplayRegister(uint8_t address, uint8_t data) +void TM1638::writeCommand(uint8_t cmd) { - if (address <= 0x0F) { - // Disable auto address increment - writeCommand(TM1638_ADDRESS_FIXED); - - // Write to display register - digitalWrite(_stbPin, LOW); - writeByte(TM1638_DISPLAY_ADDR | address); - writeByte(data); - digitalWrite(_stbPin, HIGH); - } + TM1638_STB_LOW(); + writeByte(cmd); + TM1638_STB_HIGH(); } /*! - * \brief Write command. - * \param cmd Please refer to the datasheet for a list with supported commands. + * \brief Write byte to TM1638. + * \param data 8-bit value. */ -void TM1638::writeCommand(uint8_t cmd) +void TM1638::writeByte(uint8_t data) { - // Write command - digitalWrite(_stbPin, LOW); - writeByte(cmd); - digitalWrite(_stbPin, HIGH); - delayMicroseconds(1); + for (uint8_t i = 0; i < 8; i++) { + TM1638_CLK_LOW(); + TM1638_PIN_DELAY(); + if (data & (1 << i)) { + TM1638_DIO_HIGH(); + } else { + TM1638_DIO_LOW(); + } + TM1638_CLK_HIGH(); + TM1638_PIN_DELAY(); + } } /*! @@ -179,43 +250,17 @@ uint8_t TM1638::readByte() { uint8_t data = 0; - // Set DIO pin to input - digitalWrite(_dioPin, LOW); - pinMode(_dioPin, INPUT); - - // Read 8-bit + TM1638_DIO_INPUT(); for (uint8_t i = 0; i < 8; i++) { - digitalWrite(_clkPin, LOW); - delayMicroseconds(1); - if (digitalRead(_dioPin)) { + TM1638_CLK_LOW(); + TM1638_PIN_DELAY(); + if (TM1638_DIO_READ()) { data |= (1 << i); } - digitalWrite(_clkPin, HIGH); + TM1638_CLK_HIGH(); + TM1638_PIN_DELAY(); } - - // Restore DIO pin to output - pinMode(_dioPin, OUTPUT); + TM1638_DIO_OUTPUT(); return data; } - -/*! - * \brief Write byte to TM1638. - * \param data 8-bit value. - */ -void TM1638::writeByte(uint8_t data) -{ - // Write 8-bit - for (uint8_t i = 0; i < 8; i++) { - digitalWrite(_clkPin, LOW); - - if (data & (1 << i)) { - digitalWrite(_dioPin, HIGH); - } else { - digitalWrite(_dioPin, LOW); - } - - digitalWrite(_clkPin, HIGH); - delayMicroseconds(1); - } -} diff --git a/src/TM1638.h b/src/TM1638.h index 808d67e..ca03703 100644 --- a/src/TM1638.h +++ b/src/TM1638.h @@ -79,42 +79,113 @@ #include -// Instructions -#define TM1638_WRITE_DISPLAY_CTRL 0x80 //!< Display control address write -#define TM1638_DISPLAY_ADDR 0xc0 //!< Display address - -// Data instructions -#define TM1638_WRITE_DATA 0x40 //!< Write data to register -#define TM1638_READ_KEYS 0x42 //!< Read key-scan data -#define TM1638_ADDRESS_FIXED 0x44 //!< Set fixed address - +// Commands +#define TM1638_CMD_DATA 0x40 //!< Display data command +#define TM1638_CMD_CTRL 0x80 //!< Display control command +#define TM1638_CMD_ADDR 0xc0 //!< Display address command + +// Data command bits +#define TM1638_DATA_WRITE 0x00 //!< Write data +#define TM1638_DATA_READ_KEYS 0x02 //!< Read keys +#define TM1638_DATA_AUTO_INC_ADDR 0x00 //!< Auto increment address +#define TM1638_DATA_FIXED_ADDR 0x04 //!< Fixed address + +// Control command bits +#define TM1638_CTRL_PULSE_1_16 0x00 //!< Pulse width 1/16 +#define TM1638_CTRL_PULSE_2_16 0x01 //!< Pulse width 2/16 +#define TM1638_CTRL_PULSE_4_16 0x02 //!< Pulse width 4/16 +#define TM1638_CTRL_PULSE_10_16 0x03 //!< Pulse width 10/16 +#define TM1638_CTRL_PULSE_11_16 0x04 //!< Pulse width 11/16 +#define TM1638_CTRL_PULSE_12_16 0x05 //!< Pulse width 12/16 +#define TM1638_CTRL_PULSE_13_16 0x06 //!< Pulse width 13/16 +#define TM1638_CTRL_PULSE_14_16 0x07 //!< Pulse width 14/16 +#define TM1638_CTRL_DISPLAY_OFF 0x00 //!< Display off +#define TM1638_CTRL_DISPLAY_ON 0x08 //!< Display on + +//!< Pin defines +#define TM1638_NUM_GRIDS 16 //!< Number of grid registers + +#ifdef __AVR +#define TM1638_CLK_LOW() { *portOutputRegister(_clkPort) &= ~_clkBit; } +#define TM1638_CLK_HIGH() { *portOutputRegister(_clkPort) |= _clkBit; } +#define TM1638_CLK_INPUT() { *portModeRegister(_clkPort) &= ~_clkBit; } +#define TM1638_CLK_OUTPUT() { *portModeRegister(_clkPort) |= _clkBit; } + +#define TM1638_DIO_LOW() { *portOutputRegister(_dioPort) &= ~_dioBit; } +#define TM1638_DIO_HIGH() { *portOutputRegister(_dioPort) |= _dioBit; } +#define TM1638_DIO_INPUT() { *portModeRegister(_dioPort) &= ~_dioBit; } +#define TM1638_DIO_OUTPUT() { *portModeRegister(_dioPort) |= _dioBit; } +#define TM1638_DIO_READ() ( *portInputRegister(_dioPort) & _dioBit ) + +#define TM1638_STB_LOW() { *portOutputRegister(_stbPort) &= ~_stbBit; } +#define TM1638_STB_HIGH() { *portOutputRegister(_stbPort) |= _stbBit; } +#define TM1638_STB_INPUT() { *portModeRegister(_stbPort) &= ~_stbBit; } +#define TM1638_STB_OUTPUT() { *portModeRegister(_stbPort) |= _stbBit; } +#else +#define TM1638_CLK_LOW() { digitalWrite(_clkPin, LOW); } +#define TM1638_CLK_HIGH() { digitalWrite(_clkPin, HIGH); } +#define TM1638_CLK_INPUT() { pinMode(_clkPin, INPUT); } +#define TM1638_CLK_OUTPUT() { pinMode(_clkPin, OUTPUT); } + +#define TM1638_DIO_LOW() { digitalWrite(_dioPin, LOW); } +#define TM1638_DIO_HIGH() { digitalWrite(_dioPin, HIGH); } +#define TM1638_DIO_INPUT() { pinMode(_dioPin, INPUT); } +#define TM1638_DIO_OUTPUT() { pinMode(_dioPin, OUTPUT); } +#define TM1638_DIO_READ() ( digitalRead(_dioPin) ) + +#define TM1638_STB_LOW() { digitalWrite(_stbPin, LOW); } +#define TM1638_STB_HIGH() { digitalWrite(_stbPin, HIGH); } +#define TM1638_STB_INPUT() { pinMode(_stbPin, INPUT); } +#define TM1638_STB_OUTPUT() { pinMode(_stbPin, OUTPUT); } +#endif + +//#define delayNanoseconds(__ns) os_delay_cycles((double)(F_CPU) * ((double)__ns)/1.0e9 + 0.5) + +// Delay defines +#if F_CPU >= 20000000UL +#define TM1638_PIN_DELAY() { delayMicroseconds(1); } +#else +#define TM1638_PIN_DELAY() +#endif //! TM1638 class class TM1638 { public: - TM1638(uint8_t dioPin, uint8_t sclPin, uint8_t stbPin); + TM1638(uint8_t clkPin, uint8_t dioPin, uint8_t stbPin); + virtual void begin(); + virtual void end(); virtual void displayOn(); virtual void displayOff(); virtual void setBrightness(uint8_t brightness); virtual void clear(); - virtual uint32_t getKeyScan(); - virtual void writeDisplayRegister(uint8_t address, uint8_t data); + virtual void writeData(uint8_t address, uint8_t data); + virtual void writeData(uint8_t address, const uint8_t *buf, uint8_t len); + virtual uint32_t getKeys(); protected: - virtual void writeCommand(uint8_t cmd); - virtual void writeDisplayControl(); - virtual uint8_t readByte(); - virtual void writeByte(uint8_t data); - -private: - uint8_t _dioPin; +#ifdef __AVR + uint8_t _clkPort; //!< Clock port in IO pin register + uint8_t _dioPort; //!< Data port in IO pin register + uint8_t _stbPort; //!< Enable port in IO pin register + + uint8_t _clkBit; //!< Clock bit number in IO pin register + uint8_t _dioBit; //!< Data bit number in IO pin register + uint8_t _stbBit; //!< Enable bit number in IO pin register +#else uint8_t _clkPin; + uint8_t _dioPin; uint8_t _stbPin; +#endif bool _displayOn; uint8_t _brightness; + + virtual void writeDisplayControl(); + virtual void writeCommand(uint8_t cmd); + virtual void writeByte(uint8_t data); + virtual uint8_t readByte(); }; #endif // TM1638_H__