├── LICENSE.txt ├── README.md ├── docs ├── demo.jpg └── tm1640_datasheet_v1.0.pdf ├── package.json ├── setup.py ├── tm1640.py └── tm1640_test.py /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mike Causer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MicroPython TM1640 LED Matrix 2 | 3 | A MicroPython library for a LED matrix modules using the TM1740 LED driver. 4 | 5 | ![demo](docs/demo.jpg) 6 | 7 | ## Installation 8 | 9 | Using mip via mpremote: 10 | 11 | ```bash 12 | $ mpremote mip install github:mcauser/micropython-tm1640 13 | ``` 14 | 15 | Using mip directly on a WiFi capable board: 16 | 17 | ```python 18 | >>> import mip 19 | >>> mip.install("github:mcauser/micropython-tm1640") 20 | ``` 21 | 22 | Manual installation: 23 | 24 | Copy `tm1640.py` to the root directory of your device. 25 | 26 | ## Examples 27 | 28 | **Basic usage** 29 | 30 | ```python 31 | import tm1640 32 | from machine import Pin 33 | tm = tm1640.TM1640(clk=Pin(14), dio=Pin(13)) 34 | 35 | # line from bottom left to top right 36 | tm.write([1, 2, 4, 8, 16, 32, 64, 128]) 37 | 38 | # all on 39 | tm.write([255, 255, 255, 255, 255, 255, 255, 255]) 40 | 41 | # all off 42 | tm.write([0, 0, 0, 0, 0, 0, 0, 0]) 43 | 44 | # all LEDs dim 45 | tm.brightness(1) 46 | 47 | # all LEDs bright 48 | tm.brightness(7) 49 | 50 | # the number 3 51 | tm.write([0b00000000, 0b00011110, 0b00110011, 0b00110000, 0b00011100, 0b00110000, 0b00110011, 0b00011110]) 52 | 53 | # cross 54 | tm.write(b'\x81\x42\x24\x18\x18\x24\x42\x81') 55 | 56 | # squares 57 | tm.write([255, 129, 189, 165, 165, 189, 129, 255]) 58 | 59 | # 50% on 60 | tm.write_int(0x55aa55aa55aa55aa) 61 | ``` 62 | 63 | For more detailed examples, see ![tm1640_test.py](tm1640_test.py) 64 | 65 | ## Modules 66 | 67 | * [WeMos D1 Mini](https://www.aliexpress.com/item/32529101036.html) 68 | * [WeMos Matrix LED Shield](https://www.aliexpress.com/item/32812932291.html) 69 | * [DIY More MatrixLED Shield for D1 Mini](https://www.aliexpress.com/item/32821752799.html) 70 | * [Dual Base for WeMos D1 Mini](https://www.aliexpress.com/item/32642733925.html) 71 | 72 | ## Connections 73 | 74 | `CLK` and `DIO` are bit-banged. You can use any GPIO. 75 | 76 | TM1640 LED Matrix | WeMos D1 Mini 77 | ----------------- | ------------- 78 | CLK | D5 (GPIO14) 79 | DIO | D7 (GPIO13) 80 | VCC | 3V3/5V 81 | GND | G 82 | 83 | ## Links 84 | 85 | * [WeMos D1 Mini](https://www.wemos.cc/en/latest/d1/index.html) 86 | * [micropython.org](http://micropython.org) 87 | * [TM1640 datasheet](http://www.titanmic.com/pic/other/2014-11-20-15-36-028.pdf) 88 | * [Titan Micro TM1640 product page](http://www.titanmec.com/index.php/en/project/view/id/305.html) 89 | * [MicroPython framebuf](http://docs.micropython.org/en/latest/esp8266/library/framebuf.html) 90 | 91 | ## License 92 | 93 | Licensed under the [MIT License](http://opensource.org/licenses/MIT). 94 | -------------------------------------------------------------------------------- /docs/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcauser/micropython-tm1640/f82dfcc27df91fa3d9350fcc8fd788407f42d5f7/docs/demo.jpg -------------------------------------------------------------------------------- /docs/tm1640_datasheet_v1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcauser/micropython-tm1640/f82dfcc27df91fa3d9350fcc8fd788407f42d5f7/docs/tm1640_datasheet_v1.0.pdf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "urls": [ 3 | ["tm1640.py", "github:mcauser/micropython-tm1640/tm1640.py"] 4 | ], 5 | "version": "1.0.0" 6 | } 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | setup( 8 | name='micropython-tm1640', 9 | py_modules=['tm1640'], 10 | version='1.0.0', 11 | description='MicroPython library for TM1640 LED driver.', 12 | long_description='This library lets you operate a LED matrix display module based on a TM1640 LED driver.', 13 | keywords='tm1640 led matrix micropython', 14 | url='https://github.com/mcauser/micropython-tm1640', 15 | author='Mike Causer', 16 | author_email='mcauser@gmail.com', 17 | maintainer='Mike Causer', 18 | maintainer_email='mcauser@gmail.com', 19 | license='MIT', 20 | classifiers = [ 21 | 'Development Status :: 5 - Production/Stable', 22 | 'Programming Language :: Python :: Implementation :: MicroPython', 23 | 'License :: OSI Approved :: MIT License', 24 | ], 25 | ) -------------------------------------------------------------------------------- /tm1640.py: -------------------------------------------------------------------------------- 1 | """ 2 | MicroPython TM1640 LED matrix display driver 3 | https://github.com/mcauser/micropython-tm1640 4 | 5 | MIT License 6 | Copyright (c) 2017-2023 Mike Causer 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | from micropython import const 28 | from machine import Pin 29 | from time import sleep_us 30 | 31 | __version__ = '1.0.0' 32 | 33 | TM1640_CMD1 = const(64) # 0x40 data command 34 | TM1640_CMD2 = const(192) # 0xC0 address command 35 | TM1640_CMD3 = const(128) # 0x80 display control command 36 | TM1640_DSP_ON = const(8) # 0x08 display on 37 | TM1640_DELAY = const(10) # 10us delay between clk/dio pulses 38 | 39 | class TM1640(object): 40 | """Library for LED matrix display modules based on the TM1640 LED driver.""" 41 | def __init__(self, clk, dio, brightness=7): 42 | self.clk = clk 43 | self.dio = dio 44 | 45 | if not 0 <= brightness <= 7: 46 | raise ValueError("Brightness out of range") 47 | self._brightness = brightness 48 | 49 | self.clk.init(Pin.OUT, value=0) 50 | self.dio.init(Pin.OUT, value=0) 51 | sleep_us(TM1640_DELAY) 52 | 53 | self._write_data_cmd() 54 | self._write_dsp_ctrl() 55 | 56 | def _start(self): 57 | self.dio(0) 58 | sleep_us(TM1640_DELAY) 59 | self.clk(0) 60 | sleep_us(TM1640_DELAY) 61 | 62 | def _stop(self): 63 | self.dio(0) 64 | sleep_us(TM1640_DELAY) 65 | self.clk(1) 66 | sleep_us(TM1640_DELAY) 67 | self.dio(1) 68 | 69 | def _write_data_cmd(self): 70 | # automatic address increment, normal mode 71 | self._start() 72 | self._write_byte(TM1640_CMD1) 73 | self._stop() 74 | 75 | def _write_dsp_ctrl(self): 76 | # display on, set brightness 77 | self._start() 78 | self._write_byte(TM1640_CMD3 | TM1640_DSP_ON | self._brightness) 79 | self._stop() 80 | 81 | def _write_byte(self, b): 82 | for i in range(8): 83 | self.dio((b >> i) & 1) 84 | sleep_us(TM1640_DELAY) 85 | self.clk(1) 86 | sleep_us(TM1640_DELAY) 87 | self.clk(0) 88 | sleep_us(TM1640_DELAY) 89 | 90 | def brightness(self, val=None): 91 | """Set the display brightness 0-7.""" 92 | # brightness 0 = 1/16th pulse width 93 | # brightness 7 = 14/16th pulse width 94 | if val is None: 95 | return self._brightness 96 | if not 0 <= val <= 7: 97 | raise ValueError("Brightness out of range") 98 | 99 | self._brightness = val 100 | self._write_data_cmd() 101 | self._write_dsp_ctrl() 102 | 103 | def write(self, rows, pos=0): 104 | if not 0 <= pos <= 7: 105 | raise ValueError("Position out of range") 106 | 107 | self._write_data_cmd() 108 | self._start() 109 | 110 | self._write_byte(TM1640_CMD2 | pos) 111 | for row in rows: 112 | self._write_byte(row) 113 | 114 | self._stop() 115 | self._write_dsp_ctrl() 116 | 117 | def write_int(self, int, pos=0, len=8): 118 | self.write(int.to_bytes(len, 'big'), pos) 119 | 120 | def write_hmsb(self, buf, pos=0): 121 | self._write_data_cmd() 122 | self._start() 123 | 124 | self._write_byte(TM1640_CMD2 | pos) 125 | for i in range(7-pos, -1, -1): 126 | self._write_byte(buf[i]) 127 | 128 | self._stop() 129 | self._write_dsp_ctrl() 130 | -------------------------------------------------------------------------------- /tm1640_test.py: -------------------------------------------------------------------------------- 1 | # MicroPython TM1640 LED matrix display driver 2 | 3 | # WeMos D1 Mini -- LED Matrix 4 | # D5 (GPIO14) ---- CLK 5 | # D7 (GPIO13) ---- DIO 6 | # 3V3 ------------ VCC 7 | # G -------------- GND 8 | 9 | import tm1640 10 | from machine import Pin 11 | tm = tm1640.TM1640(clk=Pin(14), dio=Pin(13)) 12 | 13 | # all LEDS on 14 | tm.write([255, 255, 255, 255, 255, 255, 255, 255]) 15 | 16 | # all LEDs dim 17 | tm.brightness(0) 18 | 19 | # all LEDs bright 20 | tm.brightness(7) 21 | 22 | # all LEDS off 23 | tm.write([0, 0, 0, 0, 0, 0, 0, 0]) 24 | 25 | # bottom row all on 26 | tm.write([255, 0, 0, 0, 0, 0, 0, 0]) 27 | 28 | # top row all on 29 | tm.write([0, 0, 0, 0, 0, 0, 0, 255]) 30 | 31 | # left column all on 32 | tm.write([1, 1, 1, 1, 1, 1, 1, 1]) 33 | 34 | # right column all on 35 | tm.write([128, 128, 128, 128, 128, 128, 128, 128]) 36 | 37 | # 50% on 38 | tm.write([85, 170, 85, 170, 85, 170, 85, 170]) 39 | tm.write([170, 85, 170, 85, 170, 85, 170, 85]) 40 | 41 | # line from bottom left to top right 42 | tm.write([1, 2, 4, 8, 16, 32, 64, 128]) 43 | 44 | # line from top left to bottom right 45 | tm.write([128, 64, 32, 16, 8, 4, 2, 1]) 46 | 47 | # cross 48 | tm.write([0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81]) 49 | tm.write(b'\x81\x42\x24\x18\x18\x24\x42\x81') 50 | 51 | # partial update - bottom row all on 52 | tm.write([255], 0) 53 | 54 | # partial update - 3nd row from bottom all on 55 | tm.write([255], 2) 56 | 57 | # partial update - 4nd row from bottom all off 58 | tm.write([0], 3) 59 | 60 | # partial update - 7th and 8th from bottom all on 61 | tm.write([255,255], 6) 62 | 63 | # partial update - bottom four rows all on 64 | tm.write([255,255,255,255]) 65 | 66 | # square 67 | tm.write([255, 129, 129, 129, 129, 129, 129, 255]) 68 | 69 | # squares 70 | tm.write([255, 129, 189, 165, 165, 189, 129, 255]) 71 | 72 | # happy smiley 73 | tm.write([60, 66, 153, 165, 129, 165, 66, 60]) 74 | 75 | # sad smiley 76 | tm.write([0x3C, 0x42, 0xA5, 0x99, 0x81, 0xA5, 0x42, 0x3C]) 77 | 78 | # try to draw the number 3 - you'll see it's displayed rotated 180 degrees. 79 | # .####... = 0b01111000 80 | # ##..##.. = 0b11001100 81 | # ....##.. = 0b00001100 82 | # ..###... = 0b00111000 83 | # ....##.. = 0b00001100 84 | # ##..##.. = 0b11001100 85 | # .####... = 0b01111000 86 | # ........ = 0b00000000 87 | tm.write([ 88 | 0b01111000, 89 | 0b11001100, 90 | 0b00001100, 91 | 0b00111000, 92 | 0b00001100, 93 | 0b11001100, 94 | 0b01111000, 95 | 0b00000000 96 | ]) 97 | 98 | # you need to rotate the bitmap 180 deg, to make bottom row first byte and MSB left most pixel 99 | # ...####. = 0b00000000 100 | # ..##..## = 0b00011110 101 | # ..##.... = 0b00110011 102 | # ...###.. = 0b00110000 103 | # ..##.... = 0b00011100 104 | # ..##..## = 0b00110000 105 | # ...####. = 0b00110011 106 | # ........ = 0b00011110 107 | tm.write([ 108 | 0b00000000, 109 | 0b00011110, 110 | 0b00110011, 111 | 0b00110000, 112 | 0b00011100, 113 | 0b00110000, 114 | 0b00110011, 115 | 0b00011110, 116 | ]) 117 | 118 | # the number 9 (rotated 180 deg) 119 | # ........ = 0b00000000 120 | # ....###. = 0b00001110 121 | # ...##... = 0b00011000 122 | # ..##.... = 0b00110000 123 | # ..#####. = 0b00111110 124 | # ..##..## = 0b00110011 125 | # ..##..## = 0b00110011 126 | # ...####. = 0b00011110 127 | tm.write([ 128 | 0b00000000, 129 | 0b00001110, 130 | 0b00011000, 131 | 0b00110000, 132 | 0b00111110, 133 | 0b00110011, 134 | 0b00110011, 135 | 0b00011110 136 | ]) 137 | 138 | # heart (rotated 180 deg) 139 | # ........ = 0b00000000 140 | # ...##... = 0b00011000 141 | # ..####.. = 0b00111100 142 | # .######. = 0b01111110 143 | # ######## = 0b11111111 144 | # ######## = 0b11111111 145 | # ######## = 0b11111111 146 | # .##..##. = 0b01100110 147 | tm.write([0b00000000, 0b00011000, 0b00111100, 0b01111110, 0b11111111, 0b11111111, 0b11111111, 0b01100110]) 148 | 149 | # added write_int() for compatibility with 150 | # https://xantorohara.github.io/led-matrix-editor 151 | # 64-bit long integer 152 | # 1 byte per row 153 | # MSB = bottom row, right most pixel 154 | # LSB = top row, left most pixel 155 | # line from bottom left to top right 156 | tm.write_int(0x0102040810204080) 157 | 158 | # partial update - line from bottom left to top right, skip bottom row 159 | tm.write([0, 0, 0, 0, 0, 0, 0, 0]) # all off, so you can see what changed 160 | tm.write_int(0x02040810204080, 1) 161 | 162 | # partial update - line from bottom left to top right, skip bottom 2 rows 163 | tm.write([255, 255, 255, 255, 255, 255, 255, 255]) # all on, so you can see what changed 164 | tm.write_int(0x040810204080, 2) 165 | 166 | # xantorohara's Set 1 digits 167 | digits = [ 168 | 0x3c66666e76663c00, # 0 169 | 0x7e1818181c181800, # 1 170 | 0x7e060c3060663c00, # 2 171 | 0x3c66603860663c00, # 3 172 | 0x30307e3234383000, # 4 173 | 0x3c6660603e067e00, # 5 174 | 0x3c66663e06663c00, # 6 175 | 0x1818183030667e00, # 7 176 | 0x3c66663c66663c00, # 8 177 | 0x3c66607c66663c00 # 9 178 | ] 179 | tm.write_int(digits[0]) 180 | tm.write_int(digits[5]) 181 | 182 | # count to ten with xantorohara's digits 183 | from time import sleep 184 | def count(): 185 | for i in range(10): 186 | tm.write_int(digits[i]) 187 | sleep(1) 188 | 189 | # write_int() is just wrapping int.to_bytes using big-endian 190 | int = 0x0102030405060708 191 | tm.write(int.to_bytes(8, 'big'), 0) 192 | 193 | int = 0x01020304 194 | tm.write(int.to_bytes(4, 'big'), 0) 195 | 196 | # use a framebuf 197 | import framebuf 198 | buf = bytearray(8) 199 | fb = framebuf.FrameBuffer(buf, 8, 8, framebuf.MONO_HMSB) 200 | # corner pixels 201 | fb.pixel(0, 0, 1) 202 | fb.pixel(0, 7, 1) 203 | fb.pixel(7, 0, 1) 204 | fb.pixel(7, 7, 1) 205 | tm.write_hmsb(buf) 206 | # the letter F - the first non-symmetrical letter, to show the orientation 207 | fb.fill(0) 208 | fb.text('F', 0, 0, 1) 209 | tm.write_hmsb(buf) 210 | 211 | # say hello, one letter at a time, one second apart 212 | from time import sleep_ms 213 | import framebuf 214 | buf = bytearray(8) 215 | fb = framebuf.FrameBuffer(buf, 8, 8, framebuf.MONO_HMSB) 216 | 217 | def say(str, ms): 218 | for s in str: 219 | fb.fill(0) 220 | fb.text(s, 0, 0, 1) 221 | tm.write_hmsb(buf) 222 | sleep_ms(ms) 223 | fb.fill(0) 224 | tm.write_hmsb(buf) 225 | sleep_ms(50) 226 | say('Hello', 500) 227 | 228 | # framebuf lines and shapes 229 | import framebuf 230 | buf = bytearray(8) 231 | fb = framebuf.FrameBuffer(buf, 8, 8, framebuf.MONO_HMSB) 232 | fb.fill(0) 233 | fb.pixel(0,0,1) 234 | fb.hline(0, 2, 2, 1) 235 | fb.vline(2, 0, 2, 1) 236 | fb.line(5, 5, 7, 7, 1) 237 | fb.rect(0, 4, 4, 4, 1) 238 | fb.fill_rect(4, 0, 4, 4, 1) 239 | tm.write_hmsb(buf) 240 | 241 | # random pixels 242 | from urandom import getrandbits 243 | def rand(n): 244 | for i in range(n): 245 | tm.write([getrandbits(8), getrandbits(8), getrandbits(8), getrandbits(8), getrandbits(8), getrandbits(8), getrandbits(8), getrandbits(8)]) 246 | rand(100) 247 | --------------------------------------------------------------------------------