CircuitPython : Interfacing TM1637 with Circuit playground express

TM1637 is an LED driver chip that can also scan a keypad. It can drive 48 LEDs (8*6) and can read 8*2 keypad. In this post we will see how to control 4 digit 7-segment display with adafruit circuit playground express board using CircuitPython.

What you need

  • Circuit playground express board
  • TM1637 based 4 digit 7-segment display
  • CircuitPython setup and few jumpers to connect 7-segment display with controller board

if your express board is not already loaded with circuitpython, see this post.

About TM1637 LED Driver

TM1637 can drive 48 LEDs or 6 7-segment displays independently. It can also scan a keypad, but in this post we are only discussing about LED driving.

typical TM1637 circuit (source : TM1637 datasheet)

TM1637 communicates with the host using 2 wire protocol, it is very similar to I2C protocol (It matches the wire protocol, but doesn’t have the I2C address) but not exactly same. If you have full control on the I2C hardware of the host controller, it might be possible to use the I2C hardware instead of bitbanging the data. Most of the libraries available uses bitbang instead of I2C hardware. It supports maximum clock frequency of 500 KHz.

Wire protocol

TM1637 wire protocol

Pulling the Data line (DIO) down while the clock is high indicates start bit and pulling it up while the clock is high indicates stop bit. while sending data, the data line need to change its state when the clock is low, the chip will read the data line when the clock moves from low to high. When the communication is initiated the first byte will act as mode/data command which will tell the driver on how to interpret the incoming bytes.

mode/data command table

The driver has 6 8-bit display registers ( 0xC0- 0xC5 ) for holding state of each LED. Data can be set in each register individually(Fixed address mode) or can be written to the consecutive set of registers (Automatic address mode).

Fixed address mode

In fixed address mode, we need to send 3 bytes to write data to any one display register.

<mode/data command> <address> <display register data>

mode command tells the driver how interpret the incoming data. For example if we want to write 0x3F (represents decimal 0 on 7-segment display) to 0xC0 register (first display register), we need to send following byte stream on the wire.

0x44 0xC0 0x3F

Automatic address mode

In automatic address mode, we just send the first display register address, then send as many bytes as there are register from the given register offset. For example to write 1234 decimals to a 4 digit 7 segment display we need to send the following byte stream.

0x40 0xC0 0x06 0x5B 0x4F 0x66

Display control

The driver also lets us control the brightness of the display, it supports 8 levels of brightness. To set brightness we need to two bytes.

<mode/data command> <display command>
TM1637 brightness control table

For example to set the display at ~62% brightness, we need to send following byte stream. The second byte values ranges from (0x88 to 0x8F).

0x40 0x8B

We can also switch off the display by sending following byte stream.

0x40 0x80

Connections

express board – display board

3.3V – VCC
GND – GND
A4 – CLK
A5 – DIO

display board has onboard pullups on clock and data lines, so no external pull ups are required.

Code

Open mu editor and create/open code.py file and copy the following code into it and save the file. (Code is adopted from TM1636 library). You can see that the 7 segment display will start counting numbers.

import time
import board
import digitalio

_CMD1   = 0x40      # data command
_CMD2   = 0xC0      # address command
_CMD3   = 0x80      # display control command
_DSP_ON = 0x08      # display on
_DELAY  = 0.000010  # 10us delay between clk/dio pulses
_MSB    = 0x80      # decimal point or colon depending on your display
clk = board.A4
dio = board.A5
# 0-9
_SEGMENTS = bytearray(b'\x3F\x06\x5B\x4F\x66\x6D\x7D\x07\x7F\x6F')
_clk = digitalio.DigitalInOut(clk)
_clk.direction = digitalio.Direction.OUTPUT
_clk.value     = 0
_dio = digitalio.DigitalInOut(dio)
_dio.direction = digitalio.Direction.OUTPUT
_dio.value     = 0
_brightness = 7
time.sleep(_DELAY)

def _start():
    _dio.value = 0
    time.sleep(_DELAY)
    _clk.value = 0
    time.sleep(_DELAY)

def _stop():
    _dio.value = 0
    time.sleep(_DELAY)
    _clk.value = 1
    time.sleep(_DELAY)
    _dio.value = 1

def _write_byte(b):
    for i in range(8):
      _dio.value = (b >> i) & 1
      time.sleep(_DELAY)
      _clk.value = 1
      time.sleep(_DELAY)
      _clk.value = 0
      time.sleep(_DELAY)
    _clk.value = 0
    time.sleep(_DELAY)
    _clk.value = 1
    time.sleep(_DELAY)
    _clk.value = 0
    time.sleep(_DELAY)

def _write_data_cmd():
    # automatic address increment, normal mode
    _start()
    _write_byte(_CMD1)
    _stop()

def _write_dsp_ctrl():
    # display on, set brightness
    _start()
    _write_byte(_CMD3 | _DSP_ON | _brightness)
    _stop()

def brightness(val=7):
    _brightness = val
    _write_data_cmd()
    _write_dsp_ctrl()

def write( segments, pos=0):
    _write_data_cmd()
    _start()

    _write_byte(_CMD2 | pos)
    for seg in segments:
      _write_byte(seg)
    _stop()
    _write_dsp_ctrl()

def encode_char(char):
    o = ord(char)
    if o >= 48 and o <= 57:
      return _SEGMENTS[o-48] # 0-9
    raise ValueError("Character out of range: {:d} '{:s}'".format(o, chr(o)))

def encode_string(string):
    segments = bytearray(len(string))
    for i in range(len(string)):
      segments[i] = encode_char(string[i])
    return segments

def showNumber(num):
    string = '{:04d}'.format(num)
    print(string)
    write(encode_string(string))

if __name__ == "__main__":
  print("TM1637 interface")
  _write_data_cmd()
  _write_dsp_ctrl()
  count = 0  
  while True:
    count = count + 1
    print(count)
    showNumber(count)
    t = time.localtime()
    time.sleep(10)

Add a Comment

Your email address will not be published. Required fields are marked *