I had trouble accessing the file linked, so thought I would add my bits here, for those that don't want to or aren't able to unravel the arduino library or datasheet.
I'm interfacing with a bunch of these on an STM32F407.
I've got a total of 8, 4 red and 4 green. Have set them up as unique addresses (0x70-77 - 8 is the max per i2c bus).
The interface is pretty simple when you realise how Adafruit have laid it out. I did this without looking at the schematic, just by reading the HT16K33 datasheet and the Arduino library.
To initialise:
send 0x21 - start the oscillator
send 0xEF - set brightness to max (not really required, but a bit of a sanity check).
send 0x81 - turn blink off and display on
To unravel this
0x21 = 0x20 + 0x01 // 0x20 = System setup command. 0x01 = S bit (1 = oscillator on, 0 = oscillator off)
0xEF = 0xE0 + 0x0F // 0xE0 = brightness command. 0x0F = 16/16 duty cycle. (valid values are 0x00 - 0x0F for 1/16 to 16/16 duty cycle)
0x81 = 0x80 + 0x01 // 0x80 = blink command. 0x01 = display on (add 0x06 for .5hz blink, 0x04 for 1hz blink, 0x02 for 1hz blink)
The adafruit backpack has each individual digit (and the colon) configured as a column output.
Left most digit = column 0
second digit = column 1
colon = column 2
third digit = column 3
right most digit = column 4
For each column, up to 16 rows are connected. A 16 bit value is written to configure the output of each column. If a 1 is set in the corresponding row position, then that will light up that segment.
For each digit the rows are:
PGFEDCBAxxxxxxxx
x = don't care
P = decimal point
A-G are the individual segments.
- Code: Select all
A
-----
F | | B
--G--
E | | C
-----
D
As per previous reply... Some standard codes:
0 = 0x3F
1 = 0x06
2 = 0x5B
3 = 0x4F
4 = 0x66
5 = 0x6D
6 = 0x7D
7 = 0x07
8 = 0x7F
9 = 0x6F
A = 0x77
b = 0x7C
C = 0x39
d = 0x5E
E = 0x79
F = 0x71
to turn on the decimal point, add 0x80 to any digit code above.
The colon = 0xFF to turn on all segments, not sure which rows the colon segments are actually connected to.
The driver chip has 16 rows x 8 columns. This is represented as a 16x8bit RAM. Effectively, this means that you have 16 transfers on the i2c bus to fill the RAM that represents the displays.
To actually update the display, do the following:
send 0x00 // Starting RAM address, this auto increments after each successive write and wraps after 16 writes.
send 0xXX // column 0 row 0-7 drivers - this is digit 1.
send 0xXX // column 0 row 8-15 drivers - not connected
send 0xXX // column 1 row 0-7 drivers - this is digit 2.
send 0xXX // column 1 row 8-15 drivers - not connected
send 0xXX // column 2 row 0-7 drivers - this is the colon.
send 0xXX // column 2 row 8-15 drivers - not connected
send 0xXX // column 3 row 0-7 drivers - this is digit 3.
send 0xXX // column 3 row 8-15 drivers - not connected
send 0xXX // column 4 row 0-7 drivers - this is digit 4.
send 0xXX // column 4 row 8-15 drivers - not connected
My application is in a track day special car, so I'm interested in automotive related things. My examples will be geared around that.
I've got a warning for low oil pressure, so I display OIL blinking on the display to indicate that.
send 0x83 // display on, blink = 2hz
send 0x00 // starting RAM address for driver outputs
send 0x3F // = O
send 0x30 // = I
send 0x00 // = colon off
send 0x38 // = L
this will blink OIL on the first three digits at 2 Hz as a warning.
How about the same for a fuel warning.
send 0x85 // display on, blink = 1hz (not as critical as low oil pressure, so slower blink rate)
send 0x00 // starting RAM address
send 0x71 // = F
send 0x3E // = U
send 0x00 // = colon off
send 0x79 // = E
send 0x38 // = L
When a generic error condition is noted, I display four dashes, blinking
send 0x85 // display on, blink = 1hz
send 0x00 // starting RAM address
send 0x40 // = -
send 0x40 // = -
send 0x00 // = colon off
send 0x40 // = -
send 0x40 // = -
The bit I've left out of all of this is the generic i2c stuff. That is, you need to preface each sequence with a START and then the address of the device you're targeting. Don't forget that the devices use 7 bit addresses, and the 8th bit in the address byte must be set to whether you're sending or receiving (from the master's perspective). That means that if your i2c display is 0x70, you actually need to send 0xE0 (= 0x70 << 1 + 0x0). You'll then get an ack back from the display, which indicates that it's ready to receive your data. Once you're done transferring, don't forget to generate a STOP condition also.
I've been coding in C++, and if anyone is interested in my code, you're welcome to have a look, just respond here or send me a message.
Hope that someone else finds this useful. It was something I did in an hour or so on a Sunday night.