Raspberry Pi Pico
TM1637 4 Digit Display

The TM1637 is an LED driver. There are lots of cheap 4 and 6 digit displays that use it. You can pick these up for a a few pounds if you look around. I have had this one for a while now and finally decided to find out how to use it whilst some of my students were doing a long assessment.

This is how the board looks. It came with some male headers pre-soldered meaning it would stand vertically in a breadboard if you didn't use the male to female jumper cables.

Pico Circuit

Here is a Fritzing diagram showing how to connect the circuit.

Pico Circuit

from time import sleep_us
from micropython import const

_CMD1 = const(64)
_CMD2 = const(192)
_CMD3 = const(128)

_BIT_DELAY = 10


class TM1637:
    
    def __init__(self, clk, dio):
        self.clk = clk
        self.dio = dio
        self.data = [0, 0, 0, 0]
        self.set_brightness(7)
        
    def set_brightness(self, b, on=1):
        if b<0 or b>7: return
        self.brightness = b + (on * 8)
        self.start()
        self.write_byte(_CMD3 + self.brightness)
        self.stop()
                
    def start(self):
        self.dio.value(0)
        sleep_us(_BIT_DELAY)
        self.clk.value(0)
        sleep_us(_BIT_DELAY)
    
    def stop(self):
        self.dio.value(0)
        sleep_us(_BIT_DELAY)
        self.clk.value(1)
        sleep_us(_BIT_DELAY)
        self.dio.value(1)
        
    def write_byte(self, b):
        bits = [b >> i & 1 for i in range(8)]
        for b in bits:
            self.dio.value(b)
            sleep_us(_BIT_DELAY)
            self.clk.value(1)
            sleep_us(_BIT_DELAY)
            self.clk.value(0)
            sleep_us(_BIT_DELAY)
        self.clk.value(0)
        sleep_us(_BIT_DELAY)
        self.clk.value(1)
        sleep_us(_BIT_DELAY)
        self.clk.value(0)
        sleep_us(_BIT_DELAY)
        
    def write_digits(self, colon=True):
        seg_data = [0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D,
                    0x07, 0x7F, 0x6F]
        
        bitdata = [seg_data[d] for d in self.data]
        if colon:
            bitdata[1] |= 128

        self.start() 
        self.write_byte(_CMD1)
        self.stop()
         
        self.start()
        self.write_byte(_CMD2)
        
        for digit in bitdata:
            self.write_byte(digit)         
         
        self.stop()
        
        self.start()
        self.write_byte(_CMD3 + self.brightness)
        self.stop()
    
    # write a list of 4 binary values
    def write_raw(self, digits):
        self.data = digits
        self.start() 
        self.write_byte(_CMD1)
        self.stop()
         
        self.start()
        self.write_byte(_CMD2)
        
        for digit in digits:
            self.write_byte(digit)         
         
        self.stop()
        
        self.start()
        self.write_byte(_CMD3 + self.brightness)
        self.stop()
        
    def display_number(self, num, colon = True):
        if num < 0 or num > 9999:
            return
        sdig =  '{:04d}'.format(num)
        self.data = [int(x) for x in sdig]
        self.write_digits(colon)

Here is the test code I used,

from machine import Pin
from time import sleep
from tm1637 import TM1637

clk = Pin(17, Pin.OUT)
dio = Pin(16, Pin.OUT)

t = TM1637(clk, dio)
t.write_raw([127, 255, 127, 127])
for i in range(8):
    t.set_brightness(7 - i)
    sleep(1)
t.set_brightness(7)
sleep(1)
# off and on again
t.set_brightness(7, False)
sleep(1)
t.set_brightness(7, True)
sleep(1)
for i in range(10000):
    t.display_number(i, False)
    sleep(0.1)

When I finally got this to work, I was reasonably impressed with it. It can use any pins you like because it has a custom protocol and these displays are really cheap. There is no decimal point on my display and I find the colon to be a little cramped. It's still good enough to get across some numbers or the time when you need it to. I suspect I will find myself using this a lot in the future.