In one of my Arduino projects I needed to show some numbers. Luckily, a 4 digit display lain around on my desk. Because it had a built-in driver which would save me from all the hassle with shift registers it looked like a perfect fit. I checked the chip the display was using and it was labeled “TM1637”. I was already familiar with the TM1638 (for more details see my recent post about using a TM1638 based board with Arduino) chip so I thought making the TM1637 work should be easy. Unfortunately, while there are some similarities between the chips getting the TM1637 to work took me longer than I had expected. When I finally aligned all the moving pieces I decided to create a standalone sample which will hopefully be helpful to other people playing with TM1637 based displays. To make the sample a good demo I just took my Nokia LCD clock sample, replaced the Nokia LCD display with the TM1637 display and updated the code accordingly and ended up with a TM1637 clock:
Let’s get started from taking a closer look at the TM1637 display. The TM1637 display has four pins
VCC
, GND
, DIO
, CLK
. Similarly to the TM1638 the TM1637 uses a protocol to communicate with the display. While commands themselves are very similar to the ones used by the TM1638 board sending the data is a little different. The display does not have the STB
pin the TM1638 board had so separating commands and arguments works differently. A start signal needs to be sent before sending a command to the display. Once the command has been sent a stop signal needs to be used. If a command has arguments another start signal needs to be sent and then after all arguments have been sent the stop signal needs to be used. The start signal is just setting the CLK
and DIO
pins to HIGH
and then setting them to LOW
. The stop signal is setting the CLK
and DIO
pins to LOW
and then to HIGH
. The CLK
pin should not change the state more frequently than 250kHz so I added a little delay between state changes. Here is how my corresponding start and stop functions look like:
void start(void) { digitalWrite(clock,HIGH);//send start signal to TM1637 digitalWrite(data,HIGH); delayMicroseconds(5); digitalWrite(data,LOW); digitalWrite(clock,LOW); delayMicroseconds(5); } void stop(void) { digitalWrite(clock,LOW); digitalWrite(data,LOW); delayMicroseconds(5); digitalWrite(clock,HIGH); digitalWrite(data,HIGH); delayMicroseconds(5); }
The display has three commands:
- initialize the display and set brightness
- display value at a given position
- display values starting from a given position
The first command just initializes the display and sets the brightness. This is done by sending the %1000abbb
value where the a
bit activates (if set to 1
) or deactivates (if set to 0
) the display and the bbb
bits control the brightness (with 000
being the darkest and 111
being the brightest).
The second command allows controlling a single digit of the display. It requires sending 3 bytes to the display: 0x44 %110000aa X
where the aa
bits in the second byte are used to select the position of the digit to control while X
is the value to be displayed encoded in the standard 7 segment coding (as explained here).
Finally, the last command is used to control multiple digits and can consists of up to 6 bytes. Typically you want use all the digits in which case you send the following bytes to the display – 0x40 0xC0 W X Y Z
. The first byte is the command identifier the second byte tells the display to start at the first position and W X Y Z are the values to be displayed on subsequent positions (again in the standard 7 segment encoding).
Here are is a short summary of commands:
START (0x88|%00000aaa) STOP |
initialize and set the brightness |
START 0x44 STOP START (0xC0|%000000aa) X STOP |
display value X at position %000000aa |
START 0x40 STOP START 0xC0 W X Y Z STOP |
display values W X Y Z starting from position 0 |
When sending data to the display timing is the tricky part. Apparently (if I understood the machine translation of the data sheet which is in Chinese correctly), the CLK
pin should not change the state more frequently than 250kHz. This means that I could not use the Arduino shiftOut
function because it does not allow to provide the delay after setting the DIO
pin. Instead I had to come up with my own function (I called it writeValue
) that emulates the shiftOut
function but which uses a delay after setting the DIO
pin. (Yes, it did made me think that my other projects may have some kind of issue due to writing data too fast but, hey, they worked!). Another thing is that the chip is acknowledging it received data successfully by setting the DIO
pin to HIGH
after each byte of data has been written. I have never seen it fail and am not even sure what I would be supposed to do if it did. Probably in my little projects this does not matter too much – I am constantly writing to the display so I hope that a subsequent write will succeed and therefore even though the writeValue
function returns a result I am ignoring it. The writeValue function looks as follows:
bool writeValue(uint8_t value) { for(uint8_t i = 0; i < 8; i++) { digitalWrite(clock, LOW); delayMicroseconds(5); digitalWrite(data, (value & (1 << i)) >> i); delayMicroseconds(5); digitalWrite(clock, HIGH); delayMicroseconds(5); } // wait for ACK digitalWrite(clock,LOW); delayMicroseconds(5); pinMode(data,INPUT); digitalWrite(clock,HIGH); delayMicroseconds(5); bool ack = digitalRead(data) == 0; pinMode(data,OUTPUT); return ack; }
Based on this information I created a TM1637 clock as shown on the photo above. I connected a TM1637 display to Arduino as follows:
Arduino TM1637 display PIN #7 ------------------ CLK PIN #8 ------------------ DIO 3.3V ------------------ VCC GND ------------------ GND
The code is on github - clone it and try for yourself.