Write SPIN code for TM1637 and TM1638 (Part I)
big-banging seven-segment driver chips

Seven-segment digit display used to be ubiquitous. Nowadays they only appear on digital clocks, microwave oven or washing machine control panels, among others.
They can also be useful for your hobby electronics projects, to display voltage, current or temperate readings, or even to make your own Apollo DSKY simulator.
Both TM1637 and TM1638 driver chips are commonly used in low-cost multi-digit seven-segment display modules shown below.

You can find comprehensive Arduino libraries for both TM1637 (e.g. from Avishay) and TM1638 (e.g. from Ricardo Batista).
This article explores how to directly generate digital signals (also called bit-banging) that control TM1637, as well as TM1638 without the help of Arduino libraries. The goal is to dig deeper into the electrical signals, rather than staying at the “soft” (ware) level.
Propeller SPIN code may enhance the learning experience. But you are welcome to use any MCU platform and programming languages to follow along.
A logic analyzer or oscilloscope would be very valuable for examining the signals.
1. Parallax Propeller and SPIN
Parallax Propeller is an interesting MCU. It is helpful to think of it as a piece of FPGA hardware with 8 CPU cores. The CPU core is uniquely hand-crafted, called COG. (The current Propeller chip has 8 COGs and it is even open sourced here.)
Though not as popular as ARM or MIPS core MCU, Propeller has a rich ecosystem of development boards, coding languages with abundant libraries, as well as tutorials for beginners.
SPIN is one of Propeller’s native coding language, along with an assembly language called PASM. They provide a slim and self-contained tool chain for fast development.
Similar to the success of Arduino, SPIN code minimizes the distraction and layering of “modern” embedded hardware/software systems(RTOS, frameworks, compiler configurations etc.), but allows you to focus on generating digital signals directly.
If you need precise timing control of some digital signals, it is usually easier to code that in Propeller SPIN/PASM than in an FPGA, since you don’t have to deal with the incidental complexity of these giga-byte FPGA logic synthesis tool chains, but still get the job done.
To learn more about Propeller and SPIN/PASM language, please refer to the Propeller manual.
For example, here is the “Hello World!” of the MCU world: to light a LED, in SPIN language
CON
LED_PIN = 0 'LED connected to P0 pin
PUB main
dira[LED_PIN] := 1 'set pin as output
outa[LED_PIN] := 1 'set pin as high
repeat
Hopefully you can read that just like some pseudo-code, in case you are not familiar with SPIN code yet.
One peculiarity of that piece of code is this “repeat” statement at the end. It is to keep the current COG “alive”. If you remove “repeat”, P0 will get reset and the LED turns off quickly.
Yes, I hear you say, Arduino can omit “void loop()” and it still works.
Later you would realize it’s a good way to “release” inactive COGs for later reuse, well, since Propeller has 8 of them.
2. TM1637 I2C-like interface
TM1637 datasheet wasn’t well translated into English from its Chinese version (download from its manufacturer). For example, in the English datasheet, “LED high pressure drive” should really be “LED high voltage drive”, because 高压 was mistaken into “high water pressure”, rather than “high voltage”. There are many such “machine translation” issues in the English datasheet.
So let’s stick with the Chinese datasheet. Fortunately timing and hardware diagrams of the IO signals are self-explanatory enough.

TM1637 uses 2 wires (DIO and CLK) for bi-directional data communication. These two signals are I2C-like, because both are open-collector with pull-up resistors.

DIO is a bi-directional data line that is clocked by the rising edge of CLK. The START and STOP(END) conditions are very similar to the ones in I2C protocol. But the similarities stops there.

TM1637 digital commands are not like I2C commands at all. It has three commonly used commands: display-on-off(0x8F/0x80), data-write (0x40/0x44) and write-to-register-address (0xC0, 0xC1,…). Here is the SPIN code to turn on display with full brightness.
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000CON
DIO_PIN = 24 'P24
CLK_PIN = 25 'P25
CON
DISPLAY_ON_CMD = $8F
DISPLAY_OFF_CMD = $80
DATA_CMD = $40
ADDR_CMD = $C0PUB main
'init: external pull up high both DIO and CLK
dira[DIO_PIN] := 0
dira[CLK_PIN] := 0 start 'start condition
send_byte(DISPLAY_ON_CMD) 'turn-on display; full brightness
end 'end condition repeatPRI start
'get ready to drive low
outa[DIO_PIN] := 0
outa[CLK_PIN] := 0
'start condition
dira[DIO_PIN]:= 1 'drive DIO low
delay
dira[CLK_PIN] := 1 'drive CLK lowPRI send_byte(data_byte)
'8-bit LSB first
repeat 8
outa[CLK_PIN] := 0
delay
outa[DIO_PIN] := data_byte & $01
data_byte >>= 1
outa[CLK_PIN] := 1
delay
'9th CLK for ACK bit
outa[CLK_PIN] := 0
dira[DIO_PIN] := 0 'release DIO to TM1637
delay
outa[CLK_PIN] := 1
delay
outa[CLK_PIN] := 0 'finish 9th CLK; TM1637 release DIO outa[DIO_PIN] := 0
dira[DIO_PIN] := 1 'reclaim DIO
delay
PRI end
'end condition
outa[CLK_PIN] := 1
delay
outa[DIO_PIN] := 1
delay
'release bus
dira[CLK_PIN] := 0
dira[DIO_PIN] := 0PRI delay
'~10us
waitcnt(clkfreq/100_000 + cnt)
Important things to notice:
- one 8-bit command is enclosed by
start
andend
condition - after sending 8-bit
dira[DIO_PIN]:= 0
sets DIO pin as input, thus releases the line from MCU side. Because of the pull up resistor externally, it is either pulled high or driven low by TM1637 signaling ACK bit. data_byte >>= 1
right shift data byte to send it out LSB first- START condition: drive DIO low while CLK is high
- END condition: drive CLK high first, followed by DIO going high
To “flash” the display, you can send DISPLAY_ON_CMD
and DISPLAY_OFF_CMD
alternately.
If you are equipped with an oscilloscope, you may dig deeper by examining the digital signals, to find something interesting that isn’t mentioned in the datasheet.

You see two little glitches on DIO (blue trace) at the falling edge of 8th and 9th CLK signal.
The first glitch is because when MCU releases DIO, TM1637 drives DIO low immediately, signaling one bit ACK.
The second glitch is because when TM1637 release DIO after one clock cycle, MCU immediately grabs it to pull it low.
If you have followed along to this point, nothing is shown on the 4-digit display yet. That’s because we haven’t shifted in data bytes to control segments of the four digits.
Now let’s add more SPIN code into “main” function
DAT
digits BYTE %00111111, %00000110, %01011011, %01001111, %01100110, %01101101, %01111101, %00000111, %01111111, %01101111PUB main
'init: external pull up to set both DIO and CLK high
dira[DIO_PIN] := 0
dira[CLK_PIN] := 0
start 'start condition
send_byte(DISPLAY_ON) 'turn-on display; full brightness
end start
send_byte(DATA_CMD)
end start
send_byte(ADDR_CMD)
send_byte(digits[3] | $80)
send_byte(digits[1])
send_byte(digits[4])
send_byte(digits[1])
end repeat
- Because we use auto address increment
DATA_CMD = $40
only the starting address0xC0
is sent, then followed by 4 bytes going to address0xC0, 0xC1, 0xC2, 0xC3
. These corresponds to 4 digits of the display. - Usually each command is enclosed in between
start
andend
. TheADDR_CMD
can take up to 6 bytes to control 6 digits in sequence. - Each bit turns on/off one segment of the digit, according to datasheet

For example data byte 0b01011011 lights up segments “g, e, d, b, a”, which makes the digit “2”.
Now are you hungry? Do you see the “pi”?
3. TM1638 SPI-like interface
Because both chips come from the same manufacturer (Chinese datasheet), TM1638 has similar commands with TM1637.
But TM1638 adopts a SPI-like signal interface. Again it’s SPI-like, because the standard SPI MISO and MOSI lines are combined into one bi-directional DIO line. Since there is no START, END signaling, the digital signals become a lot simpler.
Here is the hardware setup diagram

(TO BE CONTINUED: PART II)