├── LICENSE.txt ├── README.md ├── docs ├── TM1638.pdf └── demo.jpg ├── examples ├── address-mode │ └── README.md ├── brightness │ └── README.md ├── buttons │ └── README.md ├── dht │ └── README.md ├── font │ └── README.md ├── leds │ └── README.md ├── power │ └── README.md ├── segments │ └── README.md └── uptime │ └── README.md └── tm1638.py /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 TM1638 LED Driver 2 | 3 | A MicroPython library for LED&KEY modules with 8x 7-segment decimal LED modules, 8x individual LEDs and 8x switches using the TM1738 LED driver. 4 | 5 | ![demo](docs/demo.jpg) 6 | 7 | ## Examples 8 | 9 | Copy the file to your device, using [ampy](https://github.com/adafruit/ampy), [rshell](https://github.com/dhylands/rshell), [webrepl](http://micropython.org/webrepl/) or compiling and deploying. eg. 10 | 11 | ```bash 12 | $ ampy put tm1638.py 13 | ``` 14 | 15 | **Basic usage** 16 | 17 | ```python 18 | # TinyPICO / ESP32 19 | import tm1638 20 | from machine import Pin 21 | tm = tm1638.TM1638(stb=Pin(5), clk=Pin(18), dio=Pin(23)) 22 | 23 | # Wemos D1 Mini / ESP8266 24 | import tm1638 25 | from machine import Pin 26 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 27 | 28 | # STM32F407VET6 29 | import tm1638 30 | from machine import Pin 31 | tm = tm1638.TM1638(stb=Pin('B4'), clk=Pin('B5'), dio=Pin('B6')) 32 | 33 | # every 2nd LED on 34 | tm.leds(0b01010101) 35 | 36 | # all LEDs off 37 | tm.leds(0) 38 | 39 | # segments 40 | tm.show('cool') 41 | tm.show('abcdefgh') 42 | tm.number(-1234567) 43 | tm.number(1234) 44 | tm.number(5678, 4) 45 | tm.hex(0xdeadbeef) 46 | 47 | # dim both LEDs and segments 48 | tm.brightness(0) 49 | 50 | # all LEDs and segments off 51 | tm.clear() 52 | 53 | # get which buttons are pressed on LED&KEY module 54 | tm.keys() 55 | 56 | # get which buttons are pressed on QYF-TM1638 module 57 | tm.qyf_keys() 58 | ``` 59 | 60 | For more detailed examples, see [examples](/examples). 61 | 62 | # Seven Segment Font 63 | 64 | They are called 7-segment displays as there are 7 LEDs for each digit (segment). 65 | One byte (7 lower bits) for each segment. The 8th bit (MSB) is for the decimal point on each segment. 66 | 67 | ``` 68 | A 69 | --- 70 | F | | B 71 | -G- 72 | E | | C 73 | --- * H 74 | D 75 | 76 | HGFEDCBA 77 | 0b01101101 = 0x6D = 109 = show "5" 78 | ``` 79 | 80 | Display | Bin | Hex | Dec 81 | ------- | ---------- | ---- | --- 82 | 0 | 0b00111111 | 0x3F | 63 83 | 1 | 0b00000110 | 0x06 | 6 84 | 2 | 0b01011011 | 0x5B | 91 85 | 3 | 0b01001111 | 0x4F | 79 86 | 4 | 0b01100110 | 0x66 | 102 87 | 5 | 0b01101101 | 0x6D | 109 88 | 6 | 0b01111101 | 0x7D | 125 89 | 7 | 0b00000111 | 0x07 | 7 90 | 8 | 0b01111111 | 0x7F | 127 91 | 9 | 0b01101111 | 0x6F | 111 92 | A | 0b01110111 | 0x77 | 119 93 | b | 0b01111100 | 0x7C | 124 94 | C | 0b00111001 | 0x39 | 57 95 | d | 0b01011110 | 0x5E | 94 96 | E | 0b01111001 | 0x79 | 121 97 | F | 0b01110001 | 0x71 | 113 98 | G | 0b00111101 | 0x3D | 61 99 | H | 0b01110110 | 0x76 | 118 100 | I | 0b00000110 | 0x06 | 6 101 | J | 0b00011110 | 0x1E | 30 102 | K | 0b01110110 | 0x76 | 118 103 | L | 0b00111000 | 0x38 | 56 104 | M | 0b01010101 | 0x55 | 85 105 | n | 0b01010100 | 0x54 | 84 106 | O | 0b00111111 | 0x3F | 63 107 | P | 0b01110011 | 0x73 | 115 108 | q | 0b01100111 | 0x67 | 103 109 | r | 0b01010000 | 0x50 | 80 110 | S | 0b01101101 | 0x6D | 109 111 | t | 0b01111000 | 0x78 | 120 112 | U | 0b00111110 | 0x3E | 62 113 | v | 0b00011100 | 0x1C | 28 114 | W | 0b00101010 | 0x2A | 42 115 | X | 0b01110110 | 0x76 | 118 116 | y | 0b01101110 | 0x6E | 110 117 | Z | 0b01011011 | 0x5B | 91 118 | blank | 0b00000000 | 0x00 | 0 119 | \- | 0b01000000 | 0x40 | 64 120 | \* | 0b01100011 | 0x63 | 99 121 | 122 | # Methods 123 | 124 | Power up, power down or check status. 125 | ``` 126 | power(val=None) 127 | ``` 128 | 129 | Get or set brightness. 130 | ``` 131 | brightness(val=None) 132 | ``` 133 | 134 | Write zeros to each address. 135 | ``` 136 | clear() 137 | ``` 138 | 139 | Write to all 16 addresses from a given position. 140 | Order is left to right, 1st segment, 1st LED, 2nd segment, 2nd LED etc. 141 | ``` 142 | write(data, pos=0) 143 | ``` 144 | 145 | Set the value of a single LED. 146 | ``` 147 | led(pos, val) 148 | ``` 149 | 150 | Set all LEDs at once. LSB is left most LED. 151 | Only writes to the LED positions (every 2nd starting from 1). 152 | ``` 153 | leds(val) 154 | ``` 155 | 156 | Set one or more segments at a relative position. 157 | Only writes to the segment positions (every 2nd starting from 0). 158 | ``` 159 | segments(segments, pos=0) 160 | ``` 161 | 162 | Return a byte representing which keys are pressed. LSB is SW1. 163 | ``` 164 | keys() 165 | ``` 166 | 167 | Return a 16-bit value representing which keys are pressed. LSB is SW1. 168 | ``` 169 | qyf_keys() 170 | ``` 171 | 172 | Convert a single hex digit (0x00-0x0f) to a segment. 173 | ``` 174 | encode_digit(digit) 175 | ``` 176 | 177 | Convert a string to a list of segments. Dots are merged with the previous character. 178 | ``` 179 | encode_string(string) 180 | ``` 181 | 182 | Convert a single character to a segment. 183 | ``` 184 | encode_char(char) 185 | ``` 186 | 187 | Display a number in hexadecimal format 00000000 through FFFFFFFF, right aligned, leading zeros. 188 | ``` 189 | hex(val) 190 | ``` 191 | 192 | Display a numeric value -9999999 through 99999999, right aligned. 193 | ``` 194 | number(num) 195 | ``` 196 | 197 | Display a temperature -9 through 99 followed by degrees C. 198 | ``` 199 | temperature(num, pos=0) 200 | ``` 201 | 202 | Displays a relative humidity -9 through 99 followed by RH. 203 | ``` 204 | humidity(num, pos=4) 205 | ``` 206 | 207 | Displays as much of a string as will fit. 208 | ``` 209 | show(string, pos=0) 210 | ``` 211 | 212 | Display a string, scrolling from the right to left, speed adjustable. 213 | String starts off-screen right and scrolls until off-screen left at 4 FPS by default. 214 | ``` 215 | scroll(string, delay=250) 216 | ``` 217 | 218 | ## Parts 219 | 220 | * [WeMos D1 Mini](https://www.aliexpress.com/store/product/D1-mini-Mini-NodeMcu-4M-bytes-Lua-WIFI-Internet-of-Things-development-board-based-ESP8266/1331105_32529101036.html) $6.36 AUD 221 | * [LED&KEY TM1638 Module](https://www.aliexpress.com/item/TM1638-Module-Key-Display-For-AVR-Arduino-New-8-Bit-Digital-LED-Tube-8-Bit-TM1638/32805933184.html) $2.30 AUD 222 | * [Female-Female Dupont wires](https://www.aliexpress.com/item/10pcs-10cm-2-54mm-1p-1p-Pin-Male-to-Male-Color-Breadboard-Cable-Jump-Wire-Jumper/32636873838.html) $0.62 AUD 223 | 224 | ## Connections 225 | 226 | WeMos D1 Mini | LED&KEY TM1638 Module 227 | ------------- | ----------------- 228 | 3V3 (or 5V) | VCC 229 | G | GND 230 | D7 (GPIO13) | STB 231 | D5 (GPIO14) | CLK 232 | D6 (GPIO12) | DIO 233 | 234 | STM32F407VET6 | LED&KEY TM1638 Module 235 | ------------- | ----------------- 236 | 3V3 | VCC 237 | G | GND 238 | B4 | STB 239 | B5 | CLK 240 | B6 | DIO 241 | 242 | ## Links 243 | 244 | * [WeMos D1 Mini](https://wiki.wemos.cc/products:d1:d1_mini) 245 | * [micropython.org](http://micropython.org) 246 | * [TM1638 datasheet](http://titanmec.com/index.php/en/project/download/id/303.html) 247 | * [Titan Micro TM1638 product page](http://titanmec.com/index.php/en/project/view/id/303.html) 248 | * [Adafruit Ampy](https://learn.adafruit.com/micropython-basics-load-files-and-run-code/install-ampy) 249 | 250 | ## License 251 | 252 | Licensed under the [MIT License](http://opensource.org/licenses/MIT). 253 | -------------------------------------------------------------------------------- /docs/TM1638.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcauser/micropython-tm1638/aad2d9e8f72e51490e011df29fb673634c08eff6/docs/TM1638.pdf -------------------------------------------------------------------------------- /docs/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcauser/micropython-tm1638/aad2d9e8f72e51490e011df29fb673634c08eff6/docs/demo.jpg -------------------------------------------------------------------------------- /examples/address-mode/README.md: -------------------------------------------------------------------------------- 1 | # Address mode 2 | 3 | In fixed address mode, each display data byte is written to the same address. 4 | In address increasing mode, after each display data byte is written the address increments. 5 | 6 | ``` 7 | # Wemos D1 Mini 8 | import tm1638 9 | from machine import Pin 10 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 11 | 12 | from time import sleep_ms 13 | 14 | # set fixed mode 15 | tm._command(tm1638.TM1638_CMD1 | tm1638.TM1638_FIXED) 16 | # or simply 17 | tm._command(68) 18 | 19 | # write 255 bytes to the first address (1st segment) 20 | tm.stb(0) 21 | tm._set_address(0) 22 | for i in range(256): 23 | tm._byte(i) 24 | sleep_ms(50) 25 | tm.stb(1) 26 | 27 | # set normal address increasing mode 28 | tm._command(tm1638.TM1638_CMD1) 29 | # or simply 30 | tm._command(64) 31 | # or as other methods call it 32 | tm._write_data_cmd() 33 | 34 | # repeat the write 255 bytes and you'll see each byte moves to the next address 35 | tm.stb(0) 36 | tm._set_address(0) 37 | for i in range(256): 38 | tm._byte(i) 39 | sleep_ms(50) 40 | tm.stb(1) 41 | 42 | # these methods reset to normal address increasing mode: 43 | # .clear, .write(), .leds(), .segments() 44 | ``` 45 | -------------------------------------------------------------------------------- /examples/brightness/README.md: -------------------------------------------------------------------------------- 1 | # Brightness 2 | 3 | Demonstrates setting the display brightness. 4 | 5 | ``` 6 | # Wemos D1 Mini 7 | import tm1638 8 | from machine import Pin 9 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 10 | 11 | # light up some LEDs 12 | tm.leds(0b10101010) 13 | 14 | # write some characters 15 | tm.show('abcdefgh') 16 | 17 | # dim both the individual LEDs and the segments 18 | tm.brightness(0) 19 | 20 | # a little less than half brightness 21 | tm.brightness(3) 22 | 23 | # what is the current brightness? returns 3 24 | tm.brightness() 25 | 26 | # back to full brightness 27 | tm.brightness(7) 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/buttons/README.md: -------------------------------------------------------------------------------- 1 | # Buttons 2 | 3 | Demonstrates the key scanning ability. 4 | 5 | ``` 6 | # Wemos D1 Mini 7 | import tm1638 8 | from machine import Pin 9 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 10 | 11 | from time import sleep_ms 12 | 13 | # press a button to illuminate the matching individual LED 14 | # you can press multiple buttons simultaneously 15 | while True: 16 | pressed = tm.keys() 17 | for i in range(8): 18 | tm.led(i, (pressed >> i) & 1) 19 | sleep_ms(10) 20 | ``` 21 | -------------------------------------------------------------------------------- /examples/dht/README.md: -------------------------------------------------------------------------------- 1 | # DHT temperature and humidity 2 | 3 | Show the current temperature and humidity. 4 | 5 | ``` 6 | # Wemos D1 Mini 7 | import tm1638 8 | from machine import Pin 9 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 10 | 11 | # display 23*C 75rh 12 | tm.temperature(23) 13 | tm.humidity(75) 14 | 15 | # display 45rh 26*C 16 | tm.temperature(26,4) 17 | tm.humidity(45,0) 18 | 19 | # display temp and humidity from a DHT11 sensor 20 | import dht 21 | d = dht.DHT11(Pin(4)) 22 | 23 | d.measure() 24 | tm.temperature(d.temperature()) 25 | tm.humidity(d.humidity()) 26 | ``` 27 | -------------------------------------------------------------------------------- /examples/font/README.md: -------------------------------------------------------------------------------- 1 | # 7-segment font 2 | 3 | Show all of the supported characters. 0-9, a-z, space, dash, degrees. 4 | 5 | ``` 6 | # Wemos D1 Mini 7 | import tm1638 8 | from machine import Pin 9 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 10 | 11 | # scroll through all supported characters 12 | tm.scroll(list(tm1638._SEGMENTS)) 13 | 14 | # 01234567 15 | tm.clear() 16 | tm.segments(tm1638._SEGMENTS[0:8]) 17 | 18 | # 89abcdef 19 | tm.clear() 20 | tm.segments(tm1638._SEGMENTS[8:16]) 21 | 22 | # ghijklmn 23 | tm.clear() 24 | tm.segments(tm1638._SEGMENTS[16:24]) 25 | 26 | # opqrstuv 27 | tm.clear() 28 | tm.segments(tm1638._SEGMENTS[24:32]) 29 | 30 | # wxyz (space) (dash) (degrees) 31 | tm.clear() 32 | tm.segments(tm1638._SEGMENTS[32:39]) 33 | ``` 34 | -------------------------------------------------------------------------------- /examples/leds/README.md: -------------------------------------------------------------------------------- 1 | # Individual LEDs 2 | 3 | Demonstrates toggling the Individual LEDs at the top. 4 | 5 | ``` 6 | # Wemos D1 Mini 7 | import tm1638 8 | from machine import Pin 9 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 10 | 11 | from time import sleep_ms 12 | 13 | # turn them on, one by one 14 | for i in range(8): 15 | tm.led(i, 1) 16 | sleep_ms(200) 17 | 18 | # turn them off, one by one 19 | for i in range(8): 20 | tm.led(i, 0) 21 | sleep_ms(200) 22 | 23 | # toggle 1st LED 24 | tm.led(0, 1) 25 | tm.led(0, 0) 26 | 27 | # toggle the 2nd LED 28 | tm.led(1, True) 29 | tm.led(1, False) 30 | 31 | # turn on only the 4th LED 32 | tm.leds(8) 33 | 34 | # turn on only the first 3 LEDs 35 | tm.leds(7) 36 | 37 | # every 2nd led on 38 | for i in range(10): 39 | tm.leds(0b01010101) 40 | sleep_ms(100) 41 | tm.leds(0b10101010) 42 | sleep_ms(100) 43 | 44 | # leds off 45 | tm.leds(0) 46 | ``` 47 | -------------------------------------------------------------------------------- /examples/power/README.md: -------------------------------------------------------------------------------- 1 | # Power control 2 | 3 | Demonstrates toggling the power control register. 4 | 5 | ``` 6 | # Wemos D1 Mini 7 | import tm1638 8 | from machine import Pin 9 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 10 | 11 | # get the current status, returns True since you just initialised the driver 12 | tm.power() 13 | 14 | # switch on all of the LEDs 15 | tm.leds(255) 16 | 17 | # switch on all of the segments 18 | tm.show('8.8.8.8.8.8.8.8.') 19 | 20 | # switch off the display, both individual LEDs and 7-segments 21 | tm.power(False) 22 | 23 | # get the current status, returns False 24 | tm.power() 25 | 26 | # you can still write to the display registers 27 | tm.leds(0b10101010) 28 | tm.show('abcdefgh') 29 | 30 | # the changes wont be visible until you power on the display 31 | tm.power(True) 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/segments/README.md: -------------------------------------------------------------------------------- 1 | # Segments 2 | 3 | Demonstrates writing characters to the 7-segment LED modules. 4 | 5 | ``` 6 | # Wemos D1 Mini 7 | import tm1638 8 | from machine import Pin 9 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 10 | 11 | from time import sleep_ms 12 | 13 | # cycle through all possible LED combinations on the first segment 14 | for i in range(256): 15 | tm.write([i], 0) 16 | sleep_ms(50) 17 | 18 | # display "01234567" using bytes from the tm1638._SEGMENTS font 19 | tm.segments([0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07]) 20 | 21 | # encode string will give you a byte array which you can pass to .segments() 22 | tm.encode_string('abcdefgh') 23 | 24 | # display "abcdefgh" 25 | tm.segments(tm.encode_string('abcdefgh')) 26 | 27 | # there is a .show() method for simplifying this 28 | tm.show('abcdefgh') 29 | 30 | # show supports a position offset, so you can insert characters anywhere in the 8x segments 31 | tm.show('abcdefg', 1) 32 | tm.show('abcd') 33 | tm.show('efgh', 4) 34 | 35 | # you can write blank segments by using spaces 36 | tm.show(' 123.45') 37 | 38 | # a dot trailing a supported character will get merged with the character as the decimal place 39 | tm.show('a.b.cdefgh') 40 | tm.show('a.b.c.d.e.f.g.h.') 41 | tm.show('0.0000000') 42 | tm.show('0.0.0.0.0000') 43 | 44 | # the scroll method accepts any length string and displays it by scrolling in from the right until it is completely offscreen on the left 45 | tm.scroll('cool') 46 | tm.scroll('4 FPS', 250) 47 | tm.scroll('faster', 125) 48 | tm.scroll('slower', 500) 49 | 50 | # displays a number in hexidecimal format (0-9a-f), with leading zeros. 51 | # displays "00000001" (0x1) 52 | tm.hex(0x01) 53 | 54 | # displays "00001234" (0x1234) 55 | tm.hex(4660) 56 | 57 | # displays "12345678" (0x12345678) 58 | tm.hex(305419896) 59 | 60 | # displays "0000cafe" 61 | tm.hex(0xcafe) 62 | 63 | # displays "deadbeef" 64 | tm.hex(0xdeadbeef) 65 | 66 | # displays numbers ranging from -9999999 to 99999999, right aligned, with no leading zeros 67 | # negative numbers have a dash for a minus symbol 68 | tm.number(-9999999) 69 | tm.number(-123) 70 | tm.number(123) 71 | tm.number(99999999) 72 | ``` 73 | -------------------------------------------------------------------------------- /examples/uptime/README.md: -------------------------------------------------------------------------------- 1 | # Uptime 2 | 3 | Display a millisecond counter. 4 | 5 | ``` 6 | # Wemos D1 Mini 7 | import tm1638 8 | from machine import Pin 9 | tm = tm1638.TM1638(stb=Pin(13), clk=Pin(14), dio=Pin(12)) 10 | 11 | import utime 12 | 13 | while True: 14 | tm.number(utime.ticks_ms()) 15 | ``` 16 | -------------------------------------------------------------------------------- /tm1638.py: -------------------------------------------------------------------------------- 1 | """ 2 | MicroPython TM1638 7-segment LED display driver with keyscan 3 | https://github.com/mcauser/micropython-tm1638 4 | 5 | MIT License 6 | Copyright (c) 2018 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 | # LED&KEY module features: 28 | # 8x 7-segment decimal LED modules 29 | # 8x individual LEDs 30 | # 8x push buttons 31 | 32 | # QYF-TM1638 module features: 33 | # 8x 7-segment decimal LED modules 34 | # 16x push buttons 35 | 36 | from micropython import const 37 | from machine import Pin 38 | from time import sleep_us, sleep_ms 39 | 40 | TM1638_CMD1 = const(64) # 0x40 data command 41 | TM1638_CMD2 = const(192) # 0xC0 address command 42 | TM1638_CMD3 = const(128) # 0x80 display control command 43 | TM1638_DSP_ON = const(8) # 0x08 display on 44 | TM1638_READ = const(2) # 0x02 read key scan data 45 | TM1638_FIXED = const(4) # 0x04 fixed address mode 46 | 47 | # 0-9, a-z, blank, dash, star 48 | _SEGMENTS = bytearray(b'\x3F\x06\x5B\x4F\x66\x6D\x7D\x07\x7F\x6F\x77\x7C\x39\x5E\x79\x71\x3D\x76\x06\x1E\x76\x38\x55\x54\x3F\x73\x67\x50\x6D\x78\x3E\x1C\x2A\x76\x6E\x5B\x00\x40\x63') 49 | 50 | class TM1638(object): 51 | """Library for the TM1638 LED display driver.""" 52 | def __init__(self, stb, clk, dio, brightness=7): 53 | self.stb = stb 54 | self.clk = clk 55 | self.dio = dio 56 | 57 | if not 0 <= brightness <= 7: 58 | raise ValueError("Brightness out of range") 59 | self._brightness = brightness 60 | 61 | self._on = TM1638_DSP_ON 62 | 63 | self.clk.init(Pin.OUT, value=1) 64 | self.dio.init(Pin.OUT, value=0) 65 | self.stb.init(Pin.OUT, value=1) 66 | 67 | self.clear() 68 | self._write_dsp_ctrl() 69 | 70 | def _write_data_cmd(self): 71 | # data command: automatic address increment, normal mode 72 | self._command(TM1638_CMD1) 73 | 74 | def _set_address(self, addr=0): 75 | # address command: move to address 76 | self._byte(TM1638_CMD2 | addr) 77 | 78 | def _write_dsp_ctrl(self): 79 | # display command: display on, set brightness 80 | self._command(TM1638_CMD3 | self._on | self._brightness) 81 | 82 | def _command(self, cmd): 83 | self.stb(0) 84 | self._byte(cmd) 85 | self.stb(1) 86 | 87 | def _byte(self, b): 88 | for i in range(8): 89 | self.clk(0) 90 | self.dio((b >> i) & 1) 91 | self.clk(1) 92 | 93 | def _scan_keys(self): 94 | """Reads one of the four bytes representing which keys are pressed.""" 95 | pressed = 0 96 | self.dio.init(Pin.IN, Pin.PULL_UP) 97 | for i in range(8): 98 | self.clk(0) 99 | if self.dio.value(): 100 | pressed |= 1 << i 101 | self.clk(1) 102 | self.dio.init(Pin.OUT) 103 | return pressed 104 | 105 | def power(self, val=None): 106 | """Power up, power down or check status""" 107 | if val is None: 108 | return self._on == TM1638_DSP_ON 109 | self._on = TM1638_DSP_ON if val else 0 110 | self._write_dsp_ctrl() 111 | 112 | def brightness(self, val=None): 113 | """Set the display brightness 0-7.""" 114 | # brightness 0 = 1/16th pulse width 115 | # brightness 7 = 14/16th pulse width 116 | if val is None: 117 | return self._brightness 118 | if not 0 <= val <= 7: 119 | raise ValueError("Brightness out of range") 120 | self._brightness = val 121 | self._write_dsp_ctrl() 122 | 123 | def clear(self): 124 | """Write zeros to each address""" 125 | self._write_data_cmd() 126 | self.stb(0) 127 | self._set_address(0) 128 | for i in range(16): 129 | self._byte(0x00) 130 | self.stb(1) 131 | 132 | def write(self, data, pos=0): 133 | """Write to all 16 addresses from a given position. 134 | Order is left to right, 1st segment, 1st LED, 2nd segment, 2nd LED etc.""" 135 | if not 0 <= pos <= 15: 136 | raise ValueError("Position out of range") 137 | self._write_data_cmd() 138 | self.stb(0) 139 | self._set_address(pos) 140 | for b in data: 141 | self._byte(b) 142 | self.stb(1) 143 | 144 | def led(self, pos, val): 145 | """Set the value of a single LED""" 146 | self.write([val], (pos << 1) + 1) 147 | 148 | def leds(self, val): 149 | """Set all LEDs at once. LSB is left most LED. 150 | Only writes to the LED positions (every 2nd starting from 1)""" 151 | self._write_data_cmd() 152 | pos = 1 153 | for i in range(8): 154 | self.stb(0) 155 | self._set_address(pos) 156 | self._byte((val >> i) & 1) 157 | pos += 2 158 | self.stb(1) 159 | 160 | def segments(self, segments, pos=0): 161 | """Set one or more segments at a relative position. 162 | Only writes to the segment positions (every 2nd starting from 0)""" 163 | if not 0 <= pos <= 7: 164 | raise ValueError("Position out of range") 165 | self._write_data_cmd() 166 | for seg in segments: 167 | self.stb(0) 168 | self._set_address(pos << 1) 169 | self._byte(seg) 170 | pos += 1 171 | self.stb(1) 172 | 173 | def keys(self): 174 | """Return a byte representing which keys are pressed. LSB is SW1""" 175 | keys = 0 176 | self.stb(0) 177 | self._byte(TM1638_CMD1 | TM1638_READ) 178 | for i in range(4): 179 | keys |= self._scan_keys() << i 180 | self.stb(1) 181 | return keys 182 | 183 | def qyf_keys(self): 184 | """Return a 16-bit value representing which keys are pressed. LSB is SW1""" 185 | keys = 0 186 | self.stb(0) 187 | self._byte(TM1638_CMD1 | TM1638_READ) 188 | for i in range(4): 189 | i_keys = self._scan_keys() 190 | for k in range(2): 191 | for j in range(2): 192 | x = (0x04 >> k) << j*4 193 | if i_keys & x == x: 194 | keys |= (1 << (j + k*8 + 2*i)) 195 | self.stb(1) 196 | return keys 197 | 198 | def encode_digit(self, digit): 199 | """Convert a character 0-9, a-f to a segment.""" 200 | return _SEGMENTS[digit & 0x0f] 201 | 202 | def encode_string(self, string): 203 | """Convert an up to 8 character length string containing 0-9, a-z, 204 | space, dash, star to an array of segments, matching the length of the 205 | source string excluding dots, which are merged with previous char.""" 206 | segments = bytearray(len(string.replace('.',''))) 207 | j = 0 208 | for i in range(len(string)): 209 | if string[i] == '.' and j > 0: 210 | segments[j-1] |= (1 << 7) 211 | continue 212 | segments[j] = self.encode_char(string[i]) 213 | j += 1 214 | return segments 215 | 216 | def encode_char(self, char): 217 | """Convert a character 0-9, a-z, space, dash or star to a segment.""" 218 | o = ord(char) 219 | if o == 32: 220 | return _SEGMENTS[36] # space 221 | if o == 42: 222 | return _SEGMENTS[38] # star/degrees 223 | if o == 45: 224 | return _SEGMENTS[37] # dash 225 | if o >= 65 and o <= 90: 226 | return _SEGMENTS[o-55] # uppercase A-Z 227 | if o >= 97 and o <= 122: 228 | return _SEGMENTS[o-87] # lowercase a-z 229 | if o >= 48 and o <= 57: 230 | return _SEGMENTS[o-48] # 0-9 231 | raise ValueError("Character out of range: {:d} '{:s}'".format(o, chr(o))) 232 | 233 | def hex(self, val): 234 | """Display a hex value 0x00000000 through 0xffffffff, right aligned, leading zeros.""" 235 | string = '{:08x}'.format(val & 0xffffffff) 236 | self.segments(self.encode_string(string)) 237 | 238 | def number(self, num): 239 | """Display a numeric value -9999999 through 99999999, right aligned.""" 240 | # limit to range -9999999 to 99999999 241 | num = max(-9999999, min(num, 99999999)) 242 | string = '{0: >8d}'.format(num) 243 | self.segments(self.encode_string(string)) 244 | 245 | #def float(self, num): 246 | # # needs more work 247 | # string = '{0:>9f}'.format(num) 248 | # self.segments(self.encode_string(string[0:9])) 249 | 250 | def temperature(self, num, pos=0): 251 | """Displays 2 digit temperature followed by degrees C""" 252 | if num < -9: 253 | self.show('lo', pos) # low 254 | elif num > 99: 255 | self.show('hi', pos) # high 256 | else: 257 | string = '{0: >2d}'.format(num) 258 | self.segments(self.encode_string(string), pos) 259 | self.show('*C', pos + 2) # degrees C 260 | 261 | def humidity(self, num, pos=4): 262 | """Displays 2 digit humidity followed by RH""" 263 | if num < -9: 264 | self.show('lo', pos) # low 265 | elif num > 99: 266 | self.show('hi', pos) # high 267 | else: 268 | string = '{0: >2d}'.format(num) 269 | self.segments(self.encode_string(string), pos) 270 | self.show('rh', pos + 2) # relative humidity 271 | 272 | def show(self, string, pos=0): 273 | """Displays a string""" 274 | segments = self.encode_string(string) 275 | self.segments(segments[:8], pos) 276 | 277 | def scroll(self, string, delay=250): 278 | """Display a string, scrolling from the right to left, speed adjustable. 279 | String starts off-screen right and scrolls until off-screen left.""" 280 | segments = string if isinstance(string, list) else self.encode_string(string) 281 | data = [0] * 16 282 | data[8:0] = list(segments) 283 | for i in range(len(segments) + 9): 284 | self.segments(data[0+i:8+i]) 285 | sleep_ms(delay) 286 | --------------------------------------------------------------------------------