Pimoroni ScrollPhat using Adafruit IS31FL3731 Library

December 29, 2018 at 11:08 pm (maker) (, , , , , , )

I finally got myself a genuine Adafruit IS31FL3731 driver board and LED matrix (that I mentioned in a previous post) and glued an Arduino Nano on the back, thus creating a very neat little self-contained USB-driven 16×9 matrix of surface mount LEDs, which is great.

This uses the same driver chip as the Pimoroni ScrollPhat HD board for the Raspberry Pi, which is another great board I have, but I wanted to drive it from an Arduino too, so set about looking to see if the Adafruit libraries would work for the Pimoroni board too.  Four connections are required, as usual for I2C – VCC, GND, SDA, SCL as defined in the pinout here.

In summary, yes, but the Pimoroni has an odd LED configuration, as can be seen in the provided Python library:

class ScrollPhatHD(Matrix):
    width = 17
    height = 7

    def _pixel_addr(self, x, y):
        if x > 8:
            x = x - 8
            y = 6 - (y + 8)
            x = 8 - x
        return x * 16 + y

So the key feature is getting this translation over into the Adafruit IS31FL3731 library in the correct place.  So, taking a copy of the Adafruit_IS31FL3731.cpp and Adafruit_IS31FL3731.h files there were a number of changes to make.

First of all, the Adafruit boards provide a matrix of 16×9 (total 144) leds, compared to the ScrollPhat’s 17×7 (119) leds, so everywhere there is a reference to a width of 16 and height of 9, that needs changing to 17 and 7 respectively.  Unfortunately, there were a number of hard-coded “16”s and “9”s that needed updating.

Then, the translation function is required as follows (added to the .cpp and defined in the .h files):

// ScrollPhat LED Mapping taken from:
// https://github.com/pimoroni/scroll-phat-hd/blob/master/library/scrollphathd/is31fl3731.py
uint8_t ScrollPhat_IS31FL3731::xy2led (uint8_t x, uint8_t y) {
  if (x>8) {
    x = x-8;
    y = 6-(y+8);
  } else {
    x = 8-x;
  return x*16 + y;

This function is responsible for translating the x,y values into a serialised led offset number (0 to 144 in the case of a fully populated matrix like the Adafruit).  There is one place where this has to be called – in the function ScrollPhat_IS31FL3731::drawPixel the following line needs to be changed as indicated:

  setLEDPWM(x + y*16, color, _frame);

  setLEDPWM(xy2led(x,y), color, _frame);

Assuming all the 16×9 references have been updated correctly to 17×7, and no special rotations or anything were required (I’ve not tested all options in the Adafruit GFX libraries), then this largely seems to work, at least it works with the Adafruit Swirl and gfx demos (although there might be some sorting out still to do in terms of handling rotations).



Permalink 2 Comments

Adafruit 8×8 Backpack, HT16K33, RPi

July 20, 2013 at 11:20 pm (computers, maker) (, , , , )

I have recently acquired one of the great little 8×8 LED matrix boards from Adafruit, that uses the HT16K33 driver chip to give you access to the 8×8 matrix using the I2C bus.  With just four wires between your GPIO 5v, GND, SDA, SCL and the equivalent pins on the matrix board, you are ready to go. I wanted to use it with my Raspberry Pi and whilst their tutorials are excellent for getting you going quickly, I like to know a little more about what is actually going on when I call some of their higher-level functions. For this, I wanted to know what was actually going out on the I2C bus to make the magic happen.  However, apart from the data sheet for the chip itself, this information wasn’t actually that easy to come by! From looking through their sample C (for the Arduino) and Python (for Raspberry Pi) code, and messing around with i2cdump, and then comparing that with the datasheet, I seem to have got into the position of being able to drive the matrix using i2cset. My board appears on I2C bus number 1 (I have the second edition board that changed the GPIO I2C bus address from 0 to 1) at address 0x70, so all the following commands assume those values. The following commands are the main ‘initialisation’ commands:

i2cset -y 1 0x70 0x21

i2cset -y 1 0x70 0x81

i2cset -y 1 0x70 0xe0

The first turns on the oscillator – setting bit S in the “system setup” register – labelled D8 in the data sheet. The next enables the display with no blinking – setting bit D in the “display setup register” – D8 again in the data sheet.  To enable blinking then B1+B0 must be set to 01, 10 or 11 – i.e. replace 0x81 with 0x83, 0x85 or 0x87 respectively. The last command sets the brightness level – in this case, leaving the brightness to the dimmest setting, P0, P1, P2, P3 are all zero in the “digital dimming data input”.  Use a different value instead of 0 in the 0xe0 value – e.g. 0xef would be the brightest setting (all 1s). Now in order to actually turn on some LEDs, the rows are found at even addresses, starting from address 0x00, so the 8 rows of my matrix can be set using:

i2cset -y 1 0x70 0x00 0xaa

i2cset -y 1 0x70 0x02 0xaa

i2cset -y 1 0x70 0x04 0xaa

and so on up to

i2cset -y 1 0x70 0x0e 0xaa

In this case, I’m setting all rows to the value 0xaa (i.e. binary 10101010).  However, there is a complication – the bits are actually offset against the LEDs, so writing 0x01 actually illuminates the 2nd LED of each row.  So you actually need to do the following (taken from the source code of the library):

bitnum0to7 = (LED + 7) mod 8

So to set the first LED, write the hex pattern 0x80.  For the second LED, use 0x01; the third 0x02; fourth 0x04 and so on up to the last LED on the row being 0x40. I assume this is quirk of the wiring of the LED matrix to the driver pins of the HT16K33.

So thats it.  To turn off the display, turn off the oscillator or turn of the display (or both):

i2cset -y 1 0x70 0x20

i2cset -y 1 0x70 0x80

Everything else is built up from these basic commands.  And with this knowledge, the data sheet for the chip makes a lot more sense! One final note – the data sheet has an input mode too – the chip can be used to scan a simple keyboard.  Obviously in the case of the 8×8 LED matrix displays, this is not used. So, I’ll take another look at the C and Python code, but at least now I know what is going on underneath all that. Kevin.

Permalink 1 Comment