changeset 1:88e1e6b73a24

Add autoplay and fading
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Wed, 29 Jun 2016 01:36:37 +0200
parents a7c5e71aa74b
children 5f42778239b8
files is31fl3731.py
diffstat 1 files changed, 106 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/is31fl3731.py	Tue Jun 28 18:51:57 2016 +0200
+++ b/is31fl3731.py	Wed Jun 29 01:36:37 2016 +0200
@@ -1,26 +1,39 @@
-import time
+import math
 
 
-_MODE_REGISTER = _const(0x00)
-_FRAME_REGISTER = _const(0x01)
-_SHUTDOWN_REGISTER = _const(0x0a)
-_AUDIOSYNC_REGISTER = _const(0x06)
+_MODE_REGISTER = const(0x00)
+_FRAME_REGISTER = const(0x01)
+_AUTOPLAY1_REGISTER = const(0x02)
+_AUTOPLAY2_REGISTER = const(0x03)
+_BREATH1_REGISTER = const(0x08)
+_BREATH2_REGISTER = const(0x09)
+_AUDIOSYNC_REGISTER = const(0x06)
+_SHUTDOWN_REGISTER = const(0x0a)
 
-_CONFIG_BANK = _const(0x0b)
-_BANK_ADDRESS = _const(0xfd)
+_CONFIG_BANK = const(0x0b)
+_BANK_ADDRESS = const(0xfd)
 
-_PICTURE_MODE = _const(0x00)
-_AUTOPLAY_MODE = _const(0x08)
-_AUDIOPLAY_MODE = _const(0x18)
+_PICTURE_MODE = const(0x00)
+_AUTOPLAY_MODE = const(0x08)
+_AUDIOPLAY_MODE = const(0x18)
 
 
-class IS31FL3731:
+class Matrix:
     def __init__(self, width, height, i2c, address=0x74):
+        """
+        Driver for the IS31FL3731 charlieplexed LED matrix.
+
+        >>> import is31fl3731
+        >>> from machine import I2C, Pin
+        >>> i2c = I2C(Pin(5), Pin(4))
+        >>> display = is31fl3731.Matrix(16, 9, i2c)
+        >>> display.fill(127)
+        >>> display.show()
+        """
         self.width = width
         self.height = height
         self.i2c = i2c
         self.address = address
-        self.reset()
         self.init()
 
     def _bank(self, bank=None):
@@ -35,6 +48,7 @@
         self.i2c.writeto_mem(self.address, register, bytearray([value]))
 
     def init(self):
+        """Initialize the display."""
         self._mode(_PICTURE_MODE)
         self.frame(0)
         self.fill(0)
@@ -46,12 +60,67 @@
     def _mode(self, mode=None):
         return self._register(_CONFIG_BANK, _MODE_REGISTER, mode)
 
-    def reset(self):
-        self._register(_CONFIG_BANK, _SHUTDOWN_REGISTER, 0x00)
-        time.delay_ms(10)
-        self._register(_CONFIG_BANK, _SHUTDOWN_REGISTER, 0x01)
+    def sleep(self, value):
+        """Enables, disables or gets the sleep mode."""
+        return self._register(_CONFIG_BANK, _SHUTDOWN_REGISTER, value)
+
+    def autoplay(self, delay=0, loops=0, frames=0):
+        """
+        Enables or disables autoplay.
+
+        If ``delay`` is 0, autoplay is disabled. Otherwise the display will
+        switch between ``frames`` frames every ``delay`` milliseconds, and
+        repeat the cycle ``loops`` times.  If ``loops`` is 0, it will repeat
+        indefinitely.
+        """
+        if delay == 0:
+            self._mode(_PICTURE_MODE)
+            return
+        delay /= 11
+        if not 0 <= loops <= 7:
+            raise ValueError("Loops out of range")
+        if not 0 <= frames <= 7:
+            raise ValueError("Frames out of range")
+        if not 1 <= delay <= 64:
+            raise ValueError("Delay out of range")
+        self._register(_CONFIG_BANK, _AUTOPLAY1_REGISTER, loops << 4 | frames)
+        self._register(_CONFIG_BANK, _AUTOPLAY2_REGISTER, delay % 64)
+        self._mode(_AUTOPLAY_MODE | self._frame)
+
+    def fade(self, fade_in=None, fade_out=None, pause=0):
+        """
+        Disables or enables and configures fading.
+
+        If called without parameters, disables fading. If ``fade_in`` and/or
+        ``fade_out`` are specified, it will take that many milliseconds to
+        change between frames, with ``pause`` milliseconds of dark between.
+        """
+        if fade_in is None and fade_out is None:
+            self._register(_CONFIG_BANK, _BREATH2_REGISTER, 0)
+        elif fade_in is None:
+            fade_in = fade_out
+        elif fade_out is None:
+            fade_out = fade_in
+        fade_in = int(math.log(fade_in / 26, 2))
+        fade_out = int(math.log(fade_out / 26, 2))
+        pause = int(math.log(pause / 26, 2))
+        if not 0 <= fade_in <= 7:
+            raise ValueError("Fade in out of range")
+        if not 0 <= fade_out <= 7:
+            raise ValueError("Fade out out of range")
+        if not 0 <= pause <= 7:
+            raise ValueError("Pause out of range")
+        self._register(_CONFIG_BANK, _BREATH1_REGISTER, fade_out << 4 | fade_in)
+        self._register(_CONFIG_BANK, _BREATH2_REGISTER, 1 << 4 | pause)
 
     def frame(self, frame=None, show=True):
+        """
+        Change or get active frame.
+
+        If ``frame`` is not specified, returns the active frame, otherwise sets
+        it to the value of ``frame``. If ``show`` is ``True``, also shows that
+        frame.
+        """
         if frame is None:
             return self._frame
         if not 0 <= frame <= 8:
@@ -60,7 +129,21 @@
         if show:
             self._register(_CONFIG_BANK, _FRAME_REGISTER, frame);
 
+    def audio_sync(self, value=None):
+        """Enable, disable or get sync of brightness with audio input."""
+        return self._register(_CONFIG_BANK, _AUDIOSYNC_REGISTER, value)
+
+    def audio_play(self, value=None):
+        """Enable, disable or get frame display according to the audio input."""
+        if value is None:
+            return self._mode() == _AUDIOPLAY_MODE
+        elif value:
+            self._mode(_AUDIOPLAY_MODE)
+        else:
+            self._mode(_PICTURE_MODE)
+
     def fill(self, color=0, frame=None):
+        """Fill the display with specified color."""
         if not 0 <= color <= 255:
             raise ValueError("Color out of range")
         if frame is None:
@@ -70,10 +153,14 @@
         for row in range(6):
             self.i2c.writeto_mem(self.address, 0x24 + row * 24, data)
 
-    def audio_sync(self, value=None):
-        return self._register(_CONFIG_BANK, _AUDIOSYNC_REGISTER, value)
+    def pixel(self, x, y, color=None, frame=None):
+        """
+        Read or write the specified pixel.
 
-    def pixel(self, x, y, color=None, frame=None):
+        If ``color`` is not specified, returns the current value of the pixel,
+        otherwise sets it to the value of ``color``. If ``frame`` is not
+        specified, affects the currently active frame.
+        """
         if not 0 <= x <= self.width:
             return
         if not 0 <= y <= self.height: