Last active
November 15, 2021 15:07
-
-
Save jasonmhite/ce098685228cc2dd42a9c5ec21e07388 to your computer and use it in GitHub Desktop.
TM1638 example for FT232H
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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