A TM1638 LED&KEY board on an Arduino
Intermediate
Or see every lesson.
- 455parts on the bench
- 43boards running now
- 1.00xreal time, on every board
The TM1638 "LED&KEY" board puts eight 7-segment digits, eight red LEDs and eight pushbuttons on one strip, all run by one TM1638 chip on three wires. It is the quick way to give a project a front panel: a counter, a menu, a timer with buttons to set it.
The circuit above is an Uno and an LED&KEY board running the TM1638plus library. Each button lights the LED above it while you hold it, and the display counts the presses and shows the last button’s number. Press Run, then press the buttons, two at once too.
What you need
- An Arduino Uno
- A TM1638 LED&KEY board
- Five jumper wires
Wiring
| Part and pin | Goes to | Note |
|---|---|---|
| TM1638 VCC | 5 V | |
| TM1638 GND | GND | |
| TM1638 STB | Pin 4 | Strobe: low for each command |
| TM1638 CLK | Pin 6 | Clock |
| TM1638 DIO | Pin 7 | Data, both ways |
How the TM1638 works
The TM1638 does the hard parts itself. It keeps the picture for the digits and LEDs in sixteen bytes of its own memory and lights them over and over, so the Uno does not have to keep refreshing them. It also scans the buttons on its own. The Uno only sends a command now and then, and asks for the buttons when it wants them.
The three wires are not standard SPI or I2C. STB goes low for the length of each command. Each byte goes out least significant bit first, taken on the rising edge of CLK. DIO carries data both ways: the Uno drives it to send, and the chip pulls it low to answer a button read.
On the LED&KEY board, the even bytes of that memory are the eight digits and bit 0 of each odd byte is the LED next to it. The library hides all that.
The sketch
TM1638plus tm(4, 6, 7) takes STB, CLK and DIO. In setup(), displayBegin() turns the display on, brightness(2) picks one of eight levels (0 to 7), and displayText("LED KEY ") writes eight characters, one per digit.
Every 20 ms the loop calls readButtons(), which returns one byte with bit 0 for S1 up to bit 7 for S8. When the byte changes, setLEDs(buttons << 8) lights the LED over each held button: the high byte of the number is which LEDs are red. Each new press adds one to the count and prints it. Press S3 and the serial monitor says "S3 pressed, 1 in all"; press S1 and S7 together and both are counted.
#include <TM1638plus.h>
const int STROBE_PIN = 4;
const int CLOCK_PIN = 6;
const int DATA_PIN = 7;
TM1638plus tm(STROBE_PIN, CLOCK_PIN, DATA_PIN);
uint8_t lastButtons = 0;
unsigned long presses = 0;
void show(int button) {
// "P" and the count on the left, a gap, the button's number on the right.
char text[9] = "P ";
unsigned long n = presses % 100000UL;
int at = 5;
do {
text[at--] = (char)('0' + n % 10);
n /= 10;
} while (n > 0 && at > 0);
text[7] = (char)('0' + button);
tm.displayText(text);
}
void setup() {
Serial.begin(9600);
tm.displayBegin();
tm.brightness(2);
tm.displayText("LED KEY ");
Serial.println("TM1638 ready");
}
void loop() {
uint8_t buttons = tm.readButtons();
if (buttons != lastButtons) {
tm.setLEDs((uint16_t)buttons << 8);
uint8_t pressed = buttons & ~lastButtons;
for (int i = 0; i < 8; i++) {
if (pressed & (1 << i)) {
presses++;
show(i + 1);
Serial.print("S");
Serial.print(i + 1);
Serial.print(" pressed, ");
Serial.print(presses);
Serial.println(" in all");
}
}
lastButtons = buttons;
}
delay(20);
}New presses, not held buttons
The line pressed = buttons & ~lastButtons is worth learning. ~lastButtons flips the old byte, so the AND keeps only the bits that are 1 now and were 0 before: the buttons that just went down. Without it, a button held for a second would count fifty times, once each loop.
The same trick works for any set of buttons read as bits: a shift register, a port read or an I/O expander.
TM1638 or TM1637?
The TM1637 is its smaller cousin: four digits, usually with a clock colon, on two wires, and no buttons on the common boards. Use it for a clock or a counter. Use the TM1638 when you want buttons and LEDs as well, or more digits. There is a TM1637 page below.
There are TM1638 boards with sixteen buttons and no LEDs, and boards with two-color LEDs. They use the same chip but wire the keys differently, so TM1638plus has a separate class for some of them.
Common mistakes
STB or CLK loose. The display stays dark, and worse, every button reads as pressed: with no strobe or clock, DIO just sits high, and the button byte comes back all ones. Pull STB in the circuit above and the serial monitor prints "S1 pressed" through "S8 pressed" at once.
DIO loose. The display stays dark and no button ever reads as pressed.
Sending bytes most significant bit first, as the SPI library does. The segments come out scrambled. The TM1638 wants least significant bit first; use a library, or shiftOut with LSBFIRST.
Counting held buttons instead of new presses. Compare with the last reading, as above.
Brightness out of range. It takes 0 to 7; there is no "off" level, so turn the display off with its own command instead.
Questions
What library do I use for the TM1638?
TM1638plus by Gavin Lyons: TM1638plus tm(STB, CLK, DIO), then tm.displayBegin(), tm.displayText(), tm.setLEDs() and tm.readButtons().
How do I read the buttons on a TM1638?
tm.readButtons() returns one byte, bit 0 for S1 up to bit 7 for S8. Several held at once set several bits.
Does the TM1638 work at 3.3 V?
The chip is rated for 5 V. Many boards work from 3.3 V with dimmer digits, but 5 V is what the datasheet asks for.
Is the TM1638 SPI?
Not quite. It has a strobe, a clock and one data line used both ways, and sends bits least significant first. A library or shiftOut is easier than the SPI library.
Keep going
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.