computers · maker

Adafruit 8×8 Backpack, HT16K33, RPi

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.