Skip to content

Instantly share code, notes, and snippets.

@jasonmhite
Last active November 15, 2021 15:07
Show Gist options
  • Select an option

  • Save jasonmhite/ce098685228cc2dd42a9c5ec21e07388 to your computer and use it in GitHub Desktop.

Select an option

Save jasonmhite/ce098685228cc2dd42a9c5ec21e07388 to your computer and use it in GitHub Desktop.
TM1638 example for FT232H
# See https://blog.3d-logic.com/2015/01/10/using-a-tm1638-based-board-with-arduino/
import atexit
from pyftdi.spi import SpiController
from time import sleep
SPI_FREQ = 10000
SPI_MODE = 0
# Initialize the controller
spi = SpiController()
spi.configure("ftdi://ftdi:232h/1")
# This function reverses the bits in a byte. This is a quirk of this device,
# paired with the FT232H, you need to swap endianness.
# Note that this is a crappy way to do this and Python in theory has better
# ways to work with raw bytes, but it's not working right for me and I don't
# want to figure it out right now.
def rev(x):
x = ("{:08b}".format(x))[::-1]
return int(x, base=2)
# Register a callback to relinquish control of the FT232H when the program exits.
atexit.register(spi.terminate)
# Get a port to the slave with the appropriate chip select line.
slave = spi.get_port(cs=0, freq=SPI_FREQ, mode=SPI_MODE) # 0 = d3
###################################
### Addresses for the LEDs and segments, see the datasheet.
# Indexes for ADDR_DIGITS and ADDR_LEDS match the positions on the board,
# from left to right.
ADDR_ALL = [(0xc0 + i) for i in range(16)]
ADDR_DIGITS = ADDR_ALL[::2]
ADDR_LEDS = ADDR_ALL[1::2]
###################################
# Turn the device on and setting brightness to max
# 0x8f = 0b10001111 = 0b10000000 & 0b00001000 & 0b00000111
# address state = on bright = 7
slave.write([rev(0x8f)])
### Initialize all segments to off
# First, turn on auto-increment mode, in this mode you write a starting address
# followed by up to 16 bytes, corresponding to each segment/LED.
# The device automatically rolls through the segments and assigns their
# values, starting from the first address and wrapping around.
# You don't HAVE to write 16 bytes, you can do fewer (I think).
slave.write([rev(0x40)]) # Increment mode
slave.write([rev(0xc0)] + 16 * [rev(0x00)]) # Write all zeros = all off
### Now write a message
# Bitmasks correspond to individual segments... 0bABCDEFGP, where A-G
# are the standard names of the segments in a 7-segment display and P is
# the period.
# -A-
# | |
# F B
# | |
# -G-
# | |
# E C
# | |
# -D- P
# The items alternate between segments and LEDs. LEDs only use the
# least significant bit.
#
# NOTE: I am not reversing the bitmasks! I'm just doing them in the
# correct order already because it's easier to think about.
slave.write([rev(0x40)]) # Increment mode
slave.write([
rev(0xC0), # Start address
0b00101010, # D1
0b00000000, # L1
0b10011110, # D2
0b00000000, # L2
0b00111000, # D3
0b00000000, # L3
0b00011110, # D4
0b00000000, # L4
0b00001010, # D5
0b00000000, # L5
0b11111100, # D6
0b00000000, # L6
0b00101010, # D7
0b00000000, # L7
0b10110110, # D8
0b00000000, # L8
])
# Now I will make the LEDs blink back and forth to demonstrate address
# mode, where we write to one item at a time.
# state is just a list of bools to make it easier to toggle back and forth.
# Each item in the list will be used to set the state of the matching LED.
state = 4 * [False, True]
while True:
for i, x in enumerate(state):
addr = ADDR_LEDS[i] # Get the corresponding LED address
val = 0x01 if x else 0x00 # LED on if state is true, false otherwise
slave.write([rev(0x44)]) # Activate address mode
# Write (address, val) to set the state of the specific LED
slave.write([
rev(addr),
rev(val),
])
# Flip the variable in state so that LEDs toggle back and forth
state[i] = not x
sleep(1)
### Extra lesson: reading buttons!
# The TM1638 also has onboard buttons that you can read the state of.
# Unfortunately, the chip actually multiplexes MISO and MOSI on the same
# line. This doesn't work very well with most hardware SPI implementations.
# It's possible to make it work with the FT232H, but is more trouble than
# it's worth and I'm just gonna show you how for demonstration purposes
# as if it did work.
#
# This requires a transaction, with a write followed by a read. You write
# 0x42, then read back 4 bytes. These bytes represent scancodes for a key
# matrix... the details aren't that important. Tl;dr is you need to OR
# the 4 bytes together to get a status of the keys.
#
# from operator import or_ as bit_or
# from functools import reduce
#
## Write 0x42, then read back 4 bytes
# scan_codes = slave.exchange([rev(0x42)], readlen=4)
#
# key_mask = reduce(bit_or, scan_codes) # OR everything together
# print("0b{:08b}".format(key_mask))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment