├── .gitignore ├── LICENSE ├── README.md ├── docs └── pyboard-matrix-wiring.jpg ├── examples ├── game_of_life.py └── test_features.py └── matrix8x8.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | *.sw[op] 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jan Bednarik 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MicroPython Matrix8x8 Driver 2 | ============================ 3 | 4 | MicroPython driver for AdaFruit 8x8 LED Matrix display with HT16K33 backpack. 5 | 6 | Installation 7 | ------------ 8 | 9 | Copy `matrix8x8.py` file to your pyboard. 10 | 11 | Usage 12 | ----- 13 | 14 | ``` 15 | from matrix8x8 import Matrix8x8 16 | display = Matrix8x8() 17 | display.set_row(2, 0xFF) # turn on all LEDs in row 2 18 | display.set_column(3, 0xFF) # turn on all LEDs in column 3 19 | display.set_pixel(7, 6) # turn on LED at row 7, column 6 20 | ... 21 | ``` 22 | 23 | **Device controll methods:** 24 | 25 | ``` 26 | __init__(i2c_bus=1, addr=0x70, brightness=15, i2c=None) 27 | Params: 28 | * i2c_bus = I2C bus ID (1 or 2) or None (if param 'i2c' is provided) 29 | * addr = I2C address of connected display 30 | * brightness = display brightness (0 - 15) 31 | * i2c = initialised instance of pyb.I2C object 32 | 33 | on() 34 | Turn on display. 35 | 36 | off() 37 | Turn off display. You can controll display when it's off (change image, 38 | brightness, blinking, ...). 39 | 40 | set_brightness(value) 41 | Set display brightness. Value from 0 (min) to 15 (max). 42 | 43 | set_blinking(mode) 44 | Set blinking. Modes: 45 | 0 - blinking off 46 | 1 - blinking at 2Hz 47 | 2 - blinking at 1Hz 48 | 3 - blinking at 0.5Hz 49 | ``` 50 | 51 | **Image maipulation methods:** 52 | 53 | ``` 54 | set(bitmap) 55 | Show bitmap on display. Bitmap should be 8 bytes/bytearray object or any 56 | iterable object containing 8 bytes (one byte per row). 57 | 58 | set_row(row, byte) 59 | Set row by byte. 60 | 61 | set_column(column, byte) 62 | Set column by byte. 63 | 64 | set_pixel(row, column) 65 | Set (turn on) pixel. 66 | 67 | clear() 68 | Clear display. 69 | 70 | clear_row(row) 71 | Clear row. 72 | 73 | clear_column(column) 74 | Clear column. 75 | 76 | clear_pixel(row, column) 77 | Clear pixel. 78 | ``` 79 | 80 | Notes: 81 | * Rows a columns are numbered from 0 to 7. 82 | 83 | Examples 84 | -------- 85 | 86 | `examples/test_features.py` - Simple demo of display/driver fetures and usage. 87 | `examples/game_of_life.py` - Conway's Game of Life. In action video: 88 | http://youtu.be/XZgU1wqZMic 89 | 90 | You can copy one of the examples to your pyboard as `main.py` and if you connect 91 | display to I2C bus 1, they will instantly work. 92 | 93 | Wiring 94 | ------ 95 | 96 | LED Matrix display is using I2C bus. You can connect and controll more displays 97 | with one I2C bus if you change I2C address of display. 98 | 99 | Example of wiring to I2C bus 1 on breadboard: 100 | 101 | ![](https://github.com/JanBednarik/micropython-matrix8x8/blob/master/docs/pyboard-matrix-wiring.jpg) 102 | 103 | More info & Help 104 | ---------------- 105 | 106 | You can check more about the MicroPython project here: http://micropython.org 107 | 108 | Discussion about this driver: http://forum.micropython.org/viewtopic.php?f=5&t=405 109 | -------------------------------------------------------------------------------- /docs/pyboard-matrix-wiring.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanBednarik/micropython-matrix8x8/259d81a801d2cc7f73e8eda21b50f9a1316e1632/docs/pyboard-matrix-wiring.jpg -------------------------------------------------------------------------------- /examples/game_of_life.py: -------------------------------------------------------------------------------- 1 | import pyb 2 | 3 | from matrix8x8 import Matrix8x8 4 | 5 | 6 | def neighbors(cell): 7 | """ 8 | Yields neighbours of cell. 9 | """ 10 | x, y = cell 11 | yield x, y + 1 12 | yield x, y - 1 13 | yield x + 1, y 14 | yield x + 1, y + 1 15 | yield x + 1, y - 1 16 | yield x - 1, y 17 | yield x - 1, y + 1 18 | yield x - 1, y - 1 19 | 20 | 21 | def advance(board): 22 | """ 23 | Advance to next generation in Conway's Game of Life. 24 | """ 25 | new_board = set() 26 | for cell in ((x, y) for x in range(8) for y in range(8)): 27 | count = sum((neigh in board) for neigh in neighbors(cell)) 28 | if count == 3 or (count == 2 and cell in board): 29 | new_board.add(cell) 30 | return new_board, new_board == board 31 | 32 | 33 | def generate_board(): 34 | """ 35 | Returns random board. 36 | """ 37 | board = set() 38 | for x in range(8): 39 | for y in range(8): 40 | if pyb.rng() % 2 == 0: 41 | board.add((x, y)) 42 | return board 43 | 44 | 45 | def board_to_bitmap(board): 46 | """ 47 | Returns board converted to bitmap. 48 | """ 49 | bitmap = bytearray(8) 50 | for x, y in board: 51 | bitmap[x] |= 0x80 >> y 52 | return bitmap 53 | 54 | 55 | def restart_animation(display): 56 | """ 57 | Shows restart animation on display. 58 | """ 59 | for row in range(8): 60 | display.set_row(row, 0xFF) 61 | pyb.delay(100) 62 | for row in range(8): 63 | display.clear_row(7-row) 64 | pyb.delay(100) 65 | 66 | 67 | display = Matrix8x8(brightness=0) 68 | 69 | board, still_life = None, False 70 | 71 | while True: 72 | # init or restart of the game 73 | if still_life or not board: 74 | board = generate_board() 75 | restart_animation(display) 76 | pyb.delay(500) 77 | display.set(board_to_bitmap(board)) 78 | 79 | pyb.delay(500) 80 | # advance to next generation 81 | board, still_life = advance(board) 82 | display.set(board_to_bitmap(board)) 83 | 84 | # finish dead 85 | if not board: 86 | pyb.delay(1500) 87 | 88 | # finish still 89 | if still_life: 90 | pyb.delay(3000) 91 | -------------------------------------------------------------------------------- /examples/test_features.py: -------------------------------------------------------------------------------- 1 | import pyb 2 | 3 | from matrix8x8 import Matrix8x8 4 | 5 | 6 | display = Matrix8x8(brightness=0) 7 | 8 | while True: 9 | # test set() and clear() 10 | display.set(b'\xFF' * 8) 11 | pyb.delay(500) 12 | display.clear() 13 | pyb.delay(500) 14 | display.set(b'\x55\xAA' * 4) 15 | pyb.delay(500) 16 | display.set(b'\xAA\x55' * 4) 17 | pyb.delay(500) 18 | display.clear() 19 | 20 | # test set_row(), clear_row() 21 | for i in range(8): 22 | display.set_row(i, 0xFF) 23 | pyb.delay(100) 24 | for i in range(8): 25 | display.clear_row(i) 26 | pyb.delay(100) 27 | display.clear() 28 | 29 | # test set_column(), clear_column() 30 | for i in range(8): 31 | display.set_column(i, 0xFF) 32 | pyb.delay(100) 33 | for i in range(8): 34 | display.clear_column(i) 35 | pyb.delay(100) 36 | display.clear() 37 | 38 | # test set_pixel(), clear_pixel() 39 | for row in range(8): 40 | for column in range(8): 41 | display.set_pixel(row, column) 42 | pyb.delay(40) 43 | for row in range(8): 44 | for column in range(8): 45 | display.clear_pixel(row, column) 46 | pyb.delay(40) 47 | display.clear() 48 | 49 | # test set_brightness() 50 | display.set(b'\xFF' * 8) 51 | for i in range(16): 52 | display.set_brightness(i) 53 | pyb.delay(100) 54 | for i in range(16): 55 | display.set_brightness(16-i) 56 | pyb.delay(100) 57 | 58 | # test on(), off() 59 | display.set(b'\xAA' * 8) 60 | pyb.delay(500) 61 | display.off() 62 | pyb.delay(500) 63 | display.on() 64 | 65 | # test set_blinking() 66 | display.set_blinking(3) 67 | pyb.delay(5000) 68 | display.set_blinking(2) 69 | pyb.delay(5000) 70 | display.set_blinking(1) 71 | pyb.delay(5000) 72 | display.set_blinking(0) 73 | pyb.delay(2000) 74 | 75 | # test changes when display is in off state 76 | display.off() 77 | pyb.delay(500) 78 | display.set(b'\x33' * 8) 79 | display.set_blinking(1) 80 | display.on() 81 | pyb.delay(2000) 82 | display.set_blinking(0) 83 | -------------------------------------------------------------------------------- /matrix8x8.py: -------------------------------------------------------------------------------- 1 | import pyb 2 | 3 | 4 | class Matrix8x8: 5 | """ 6 | Driver for AdaFruit 8x8 LED Matrix display with HT16K33 backpack. 7 | Example of use: 8 | 9 | display = Matrix8x8() 10 | display.set(b'\xFF' * 8) # turn on all LEDs 11 | display.clear() # turn off all LEDs 12 | display.set_row(2, 0xFF) # turn on all LEDs in row 2 13 | display.set_column(3, 0xFF) # turn on all LEDs in column 3 14 | display.set_pixel(7, 6) # turn on LED at row 7, column 6 15 | """ 16 | row_addr = (0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E) 17 | 18 | def __init__(self, i2c_bus=1, addr=0x70, brightness=15, i2c=None): 19 | """ 20 | Params: 21 | * i2c_bus = I2C bus ID (1 or 2) or None (if param 'i2c' is provided) 22 | * addr = I2C address of connected display 23 | * brightness = display brightness (0 - 15) 24 | * i2c = initialised instance of pyb.I2C object 25 | """ 26 | self._blinking = 0 27 | self.addr = addr 28 | self.buf = bytearray(8) 29 | 30 | # I2C init 31 | if i2c: 32 | self.i2c = i2c 33 | else: 34 | self.i2c = pyb.I2C(i2c_bus, pyb.I2C.MASTER, baudrate=400000) 35 | 36 | # set HT16K33 oscillator on 37 | self._send(0x21) 38 | 39 | self.set_brightness(brightness) 40 | self.clear() 41 | self.on() 42 | 43 | def _send(self, data): 44 | """ 45 | Send data over I2C. 46 | """ 47 | self.i2c.send(data, self.addr) 48 | 49 | def _send_row(self, row): 50 | """ 51 | Send single row over I2C. 52 | """ 53 | data = bytes((self.row_addr[row], rotate_right(self.buf[row]))) 54 | self._send(data) 55 | 56 | def _send_buf(self): 57 | """ 58 | Send buffer over I2C. 59 | """ 60 | data = bytearray(16) 61 | i = 1 62 | for byte in self.buf: 63 | data[i] = rotate_right(byte) 64 | i += 2 65 | self._send(data) 66 | 67 | def _clear_column(self, column): 68 | """ 69 | Clear column in buffer (set it to 0). 70 | """ 71 | mask = 0x80 >> column 72 | for row in range(8): 73 | if self.buf[row] & mask: 74 | self.buf[row] ^= mask 75 | 76 | def _set_column(self, column, byte): 77 | """ 78 | Set column in buffer by byte. 79 | """ 80 | self._clear_column(column) 81 | if byte == 0: 82 | return 83 | mask = 0x80 84 | for row in range(8): 85 | shift = column - row 86 | if shift >= 0: 87 | self.buf[row] |= (byte & mask) >> shift 88 | else: 89 | self.buf[row] |= (byte & mask) << abs(shift) 90 | mask >>= 1 91 | 92 | def on(self): 93 | """ 94 | Turn on display. 95 | """ 96 | self.is_on = True 97 | self._send(0x81 | self._blinking << 1) 98 | 99 | def off(self): 100 | """ 101 | Turn off display. You can controll display when it's off (change image, 102 | brightness, blinking, ...). 103 | """ 104 | self.is_on = False 105 | self._send(0x80) 106 | 107 | def set_brightness(self, value): 108 | """ 109 | Set display brightness. Value from 0 (min) to 15 (max). 110 | """ 111 | self._send(0xE0 | value) 112 | 113 | def set_blinking(self, mode): 114 | """ 115 | Set blinking. Modes: 116 | 0 - blinking off 117 | 1 - blinking at 2Hz 118 | 2 - blinking at 1Hz 119 | 3 - blinking at 0.5Hz 120 | """ 121 | self._blinking = mode 122 | if self.is_on: 123 | self.on() 124 | 125 | def set(self, bitmap): 126 | """ 127 | Show bitmap on display. Bitmap should be 8 bytes/bytearray object or any 128 | iterable object containing 8 bytes (one byte per row). 129 | """ 130 | self.buf = bytearray(bitmap) 131 | self._send_buf() 132 | 133 | def clear(self): 134 | """ 135 | Clear display. 136 | """ 137 | for i in range(8): 138 | self.buf[i] = 0 139 | self._send_buf() 140 | 141 | def set_row(self, row, byte): 142 | """ 143 | Set row by byte. 144 | """ 145 | self.buf[row] = byte 146 | self._send_row(row) 147 | 148 | def clear_row(self, row): 149 | """ 150 | Clear row. 151 | """ 152 | self.set_row(row, 0) 153 | 154 | def set_column(self, column, byte): 155 | """ 156 | Set column by byte. 157 | """ 158 | self._set_column(column, byte) 159 | self._send_buf() 160 | 161 | def clear_column(self, column): 162 | """ 163 | Clear column. 164 | """ 165 | self._clear_column(column) 166 | self._send_buf() 167 | 168 | def set_pixel(self, row, column): 169 | """ 170 | Set (turn on) pixel. 171 | """ 172 | self.buf[row] |= (0x80 >> column) 173 | self._send_row(row) 174 | 175 | def clear_pixel(self, row, column): 176 | """ 177 | Clear pixel. 178 | """ 179 | self.buf[row] &= ~(0x80 >> column) 180 | self._send_row(row) 181 | 182 | 183 | def rotate_right(byte): 184 | """ 185 | Rotate bits right. 186 | """ 187 | byte &= 0xFF 188 | bit = byte & 0x01 189 | byte >>= 1 190 | if(bit): 191 | byte |= 0x80 192 | return byte 193 | --------------------------------------------------------------------------------