Source code for display_ht16k33.ht16k33

# SPDX-FileCopyrightText: Copyright (c) 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT
"""
`display_ht16k33.ht16k33`
================================================================================

On Display Simulation for an HT16K33 driver. Works with 16x8 and 8x8 matrices.

Based on some code from https://github.com/adafruit/Adafruit_CircuitPython_HT16K33.git
Authors: Radomir Dopieralski and Tony DiCola License: MIT

* Author(s): Jose D. Montoya


"""
from vectorio import Circle
import displayio
import ulab.numpy as np


__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/jposada202020/CircuitPython_DISPLAY_HT16K33.git"


# pylint: disable=too-many-arguments, too-many-instance-attributes
[docs]class HT16K33: """ Main class :param int x: x coordinates in pixels for the matrix to start. This is the top left corner of the first digit :param int y: y coordinates in pixels for the matrix to start. This is the top left corner of the first digit :param int radius: led radius in pixels. Defaults to :const:`10` pixels :param bool text: define if the matrix will be used to display text. For reasons that are beyond my understanding :). The text and pixels examples work differently. displaying text will use framebuffer, and it will show in a different direction. will be good to review a PR if you found this situation a little too much for your OCD ;) :param int num_led_x: Led quantity in the x direction. Although you could select a different value than 8 or 16, library logic is tested with these value, so unexpected behaviour is expected, if these values are not used :param int num_led_y: Led quantity in the y direction. Although you could select a different value than 8, library logic is tested with this value, so unexpected behaviour is expected, if this value is not used :param register_width int:register width to be used. Defaults to :const:`2` """ def __init__( self, x, y, radius=10, text=False, num_led_x: int = 16, num_led_y: int = 8, register_width: int = 2, ) -> None: self.x = x self.y = y self.cols = num_led_x self.rows = num_led_y self.radius = radius self.bit_mask = ((1 << self.cols) - 1) << 0 self.buffer_rows = [] for _ in range(self.rows): self.buffer_rows.append(bytearray(register_width)) self.buffer = bytearray(register_width) self.array = np.ndarray(np.empty((self.rows, self.cols)), dtype=np.uint8) self.length = register_width self.group = displayio.Group() palette = displayio.Palette(3) palette[0] = 0x2263A4 palette[1] = 0x101010 palette[2] = 0xFF5500 self.matrix = [] if text: y_range = range(1, self.rows + 1) else: y_range = range(self.rows + 1, 1, -1) for j in y_range: row_buff = [] for coord_x in range(1, self.cols + 1): value = Circle( pixel_shader=palette, radius=self.radius, x=self.x + coord_x * (self.radius * 2 + self.radius // 2), y=self.y + j * (self.radius * 2 + self.radius // 2), color_index=1, ) row_buff.append(value) self.group.append(value) self.matrix.append(row_buff) @property def value(self) -> bytearray: """ Value of the buffer """ return self.buffer
[docs] def set(self, y: int, new_value: int) -> None: """ Set a particular value in a specific row defined by y :param int y: row number :param int new_value: value to be set """ reg = 0 order = range(0, self.length) for ind in order: reg = (reg << 8) | self.buffer_rows[y][ind] reg &= ~self.bit_mask reg |= new_value for ind2 in order: self.buffer_rows[y][ind2] = reg & 0xFF reg >>= 8 self.convert_to_leds(y)
[docs] def pixel(self, x: int, y: int, color=True) -> None: """ Set a specific pixel in the matrix :param int x: pixel's x coordinate in the matrix :param int y: pixel's y coordinate in the matrix :param bool color: if the pixel is to be shown or not. Defaults to shown `True` """ reg = 0 order = range(0, self.length) for i in order: reg = (reg << 8) | self.buffer_rows[y][i] mask = 1 << x if color: buff = reg | mask else: buff = reg & ~mask for i in reversed(order): self.buffer_rows[y][i] = buff & 0xFF buff >>= 8 self.convert_to_leds(y) self.update(y)
[docs] def convert_to_leds(self, y) -> None: """ Internal function to convert values to a led on and off matrix :param int y: y row number :return: None """ index = 0 for i in range(0, self.length): val = self.buffer_rows[y][i] for j in range(7, -1, -1): buffval = val >> j & 1 self.array[y][index] = buffval index = index + 1
[docs] def update(self, y: int) -> None: """ Update a particular Row :param int y: row y number """ for i, _ in enumerate(self.matrix[y]): if self.array[y][i]: self.matrix[y][i].color_index = 2 else: self.matrix[y][i].color_index = 1
[docs] def update_all(self) -> None: """ Update all the matrix """ for row in range(self.rows): self.update(row)
[docs] def shift(self, x: int, y: int, rotate: bool = True) -> None: """ Shift Matrix by x and y :param int x: pixel x coordinate :param int y: pixel x coordinate """ if rotate: pass self.array = np.roll(self.array, y, axis=0) self.array = np.roll(self.array, x, axis=1) self.update_all()
[docs] def shift_right(self, rotate: bool = False) -> None: """ Shift all pixels right :param rotate: (Optional) Rotate the shifted pixels to the left side (default=False) """ self.shift(1, 0, rotate)
[docs] def shift_left(self, rotate: bool = False) -> None: """ Shift all pixels left :param rotate: (Optional) Rotate the shifted pixels to the right side (default=False) """ self.shift(-1, 0, rotate)
[docs] def shift_up(self, rotate: bool = False) -> None: """ Shift all pixels up :param rotate: (Optional) Rotate the shifted pixels to bottom (default=False) """ self.shift(0, 1, rotate)
[docs] def shift_down(self, rotate: bool = False) -> None: """ Shift all pixels down :param rotate: (Optional) Rotate the shifted pixels to top (default=False) """ self.shift(0, -1, rotate)
[docs] def fill(self, color: bool) -> None: """ fill the entire matrix """ if color: new_value = 0xFF else: new_value = 0x00 for ele in range(self.rows): self.set(ele, new_value)