Programming Adafruit 4-Digit Displa7 (HT16K33)

elec_mech

New Member
Hi All,

Well, I'm at my wits end, so I thought I'd ask the experts. I'm attempting to use an Adafruit 4-digit display that uses an I2C IC, specifically the Holtek HT16K33. I haven't located any PICAXE code for this, but I've poured through the datasheet and attempted to reverse engineer the Arduino code written for it. Unfortunately, that is a painful experience.

I've come up with what I think is close below, but no dice. I can't get the display to show anything.

Code:
#picaxe 20m2				' Define PICAXE 20M2
#no_data					' Ignore EEPROM data - loads program faster

Initialize:					' Initial pins and set up variables
	LET dirsB = %11111111		' Make all B pins outputs
	LET dirsC = %00000011		' Make pins C.0 & C.1 outputs; rest as inputs
	LET pinsB = %00000000 		' Turn off all outputs on port B
	LET pinsC = %00000000 		' Turn off all outputs on port C
PAUSE 2000
Main:						' Main Routine
	i2cslave %11100000, i2cfast, i2cbyte			' Set slave address to DS1307
	writei2c 0, (%00100001) ' Turn on clock
	writei2c 0, (%10100000)	' Set all rows as outputs
	writei2c 0, (%11101111)	' Set brightness to maximum
	writei2c 0, (%10000000)	' Disable blinking and turn off display
	writei2c 0,($FF,%10000001)	' Set all rows high for column 1 (digit 1) followed by turn display on command - expect to see 8 on first digit of display
	PAUSE 1000		' Wait one second

END
Looking at page 32 of the datasheet, I think I'm starting things off right, but I'm not sure. The big question is whether I'm sending the actual data correctly. According to page 32, the bottom flowchart shows sending an address setting (I assume this is taken care of by the 0 right after the writei2c command signifying the address should be 0) followed by RAM data (not sure if I need to send one or two bytes) and finished with a display on command. Of course, maybe I'm sending the initialization commands wrong as well.

Also attached is a reversed engineered schematic since I couldn't find one online.

Any suggestions greatly appreciated. Will post final code if ever I figure this bugger out.
 

Attachments

Hemi345

Senior Member
After a quick look at the datasheet, it looks like you should be specifying a location for your commands, like for starting the clock:
Code:
writei2c 0x20,(%00100001) ' Turn on clock
See page 30 of the datasheet for the locations and registers table.
 
Last edited:

elec_mech

New Member
I tried your suggestion and still no dice.

Code:
Main:						' Main Routine
	i2cslave %11100000, i2cfast, i2cbyte			' Set slave address to DS1307
	writei2c 0x21, (%00100001) ' Turn on clock
	writei2c 0xA0, (%10100000)	' Set all rows as outputs
	writei2c 0xEF, (%11101111)	' Set brightness to maximum
	writei2c 0x80, (%10000000)	' Disable blinking and turn off display
	writei2c 0,($FF,$FF, %10000001)	' Set all rows high for column 1 (digit 1) followed by turn display on command - expect to see 8 on first digit of display
	PAUSE 1000		' Wait one second

END
It seems a little counter-intuitive only because, if I'm understanding correctly, I'm sending the command byte twice.

As an aside, I've checked power to the board (5VDC from USB programmer) and that B.7 is connected to the clock line and B.5 is connected to the data line. A2-A0 in the schematic are unconnected, so according to the data sheet, the slave address should be %11100000 if I follow it correctly.

I think the key to this is on page 28 and 32, but I just don't understand how to carry out the write operation and how the PICAXE sends I2C commands.

According to page 28 of the data sheet, write operations must send the slave address followed by the command or address byte. I'm assuming that by defining the slave address in i2cslave as %11100000, the PICAXE will send this byte first every time a writei2c command is sent. Is this correct?

Example, if I write this:
Code:
	i2cslave %11100000, i2cfast, i2cbyte			' Set slave address to DS1307
	writei2c 0, (%00100001) ' Turn on clock
Then the PICAXE will send: %11100000, 0x00, %00100001, although that doesn't seem to follow the format on page 28.

Or, should I call out the slave address in the location variable of writei2c then send the command byte in the variable section?

Example:

Code:
writei2c %11100000, (%00100001)    ' Turn on clock
I just tried a few iterations without success. Ugh.

I'm also confused by the commands being listed as D15-D8. Does that mean I need to send a zero byte (0x00) after every command and/or that I need to use i2cword instead of i2cbyte? Looking at the read structure on page 29, I'm guessing not.

I think I can group my problems into groups:
1) How to send the proper write command.
2) How to send the proper command to change the row output values.

Latest attempt:
Code:
Main:						' Main Routine
	i2cslave %11100000, i2cfast, i2cbyte			' Set slave address to DS1307
	pause 100
	writei2c %11100000, (%00100001) ' Turn on clock
	pause 100
	writei2c %11100000, (%10100000)	' Set all rows as outputs
	pause 100
	writei2c %11100000, (%11101111)	' Set brightness to maximum
	pause 100
	writei2c %11100000, (%10000000)	' Disable blinking and turn off display
	pause 100
	writei2c %11100000,(0x00, $FF)	' Set all rows high for column 1 (digit 1) followed by turn display on command - expect to see 8 on first digit of display
	PAUSE 1000		' Wait one second

END
Sorry for all the questions, this data sheet just doesn't seem very clear.
 

Rick100

Senior Member
I translated the Bus Pirate commands to Picaxe code as best I could.

Code:
i2cslave %11100000, i2cfast, i2cbyte

writei2c (0x21) 	' Turn on clock
pause 100
writei2c (0xEF)	' Set brightness to maximum
pause 100
writei2c (0x81)	'turn blink off and display on
pause 100
writei2c (0x00, 0x06, 0xff, 0x5b, 0xff, 0x00, 0x00, 0x4f, 0xff, 0x66)	'Now, display "1 2 3 4"

end
I edited the program to remove the address from the i2c data in the program snippet.

Good luck,
Rick
 
Last edited:

westaust55

Moderator
See PICAXE Manual 2 page 75 and then try in the format :

Code:
Hi2cSETUP  %11100000, i2cfast, i2cbyte
PAUSE 1 ; [b]wait 1 ms for the display chip to initialise[/b]
Hi2cOUT ($21) ; start clock
Hi2cOUT ($A0) ; set all rows on
Hi2cOUT ($81) ; turn on the display
etc
etc
 

elec_mech

New Member
Wow, I was about to write I had no luck trying the pirate bus method but then I saw the other responses and tried Rick100's code and added an i2csetup. Success!

Here is some working code for anyone interested:

Code:
#picaxe 20m2				' Define PICAXE 20M2
#no_data					' Ignore EEPROM data - loads program faster

Initialize:					' Initial pins and set up variables
	LET dirsB = %11111111		' Make all B pins outputs
	LET dirsC = %00000011		' Make pins C.0 & C.1 outputs; rest as inputs
	LET pinsB = %00000000 		' Turn off all outputs on port B
	LET pinsC = %00000000 		' Turn off all outputs on port C
Main:
	i2cslave %11100000, i2cfast, i2cbyte
writei2c (0x21) 	' Turn on clock
pause 100
writei2c (0xEF)	' Set brightness to maximum
pause 100
writei2c (0x81)	'turn blink off and display on
pause 100
writei2c (0x00, 0x06, 0xff, 0x5b, 0xff, 0x00, 0x00, 0x4f, 0xff, 0x66)	'Now, display "1 2 3 4"

end
I never thought to not include the location variable in the writei2c command, but that appears to be what the problem was.

Before I forget, thank you everyone for your help and advice - I didn't think a solution would present itself so soon. Also thanks to everyone writing a bit of code to try - I help out on a beginner electronics forum and it's those details that really help.

westaust55, I got your code to work as well with a slight additional of i2cmaster to the first line. Here is that code:

Code:
#picaxe 20m2				' Define PICAXE 20M2
#no_data					' Ignore EEPROM data - loads program faster

Initialize:					' Initial pins and set up variables
	LET dirsB = %11111111		' Make all B pins outputs
	LET dirsC = %00000011		' Make pins C.0 & C.1 outputs; rest as inputs
	LET pinsB = %00000000 		' Turn off all outputs on port B
	LET pinsC = %00000000 		' Turn off all outputs on port C
Main:
Hi2cSETUP  i2cmaster,%11100000, i2cfast, i2cbyte
PAUSE 1 ; wait 1 ms for the display chip to initialise
Hi2cOUT ($21) ; start clock
Hi2cOUT ($A0) ; set all rows on
Hi2cOUT ($80) ; turn off the display
PAUSE 2000
Hi2cOUT ($81) ; turn on the display
Hi2cOUT (0x00, 0x06, 0xff, 0x5b, 0xff, 0x00, 0x00, 0x4f, 0xff, 0x66)
end
I turned the display on and off to verify operation after trying Rick100's code.

Thank you again everyone!
 

Rick100

Senior Member
Glad to hear you got it working. Here is the discussion on the Adafruit forums referred to in the pevious link:
http://forums.adafruit.com/viewtopic.php?f=47&t=29897
The post by High Fidelity describes the layout of the display and how to address it. From reading his description, I think the first 0x00 in the line:
Code:
writei2c (0x00, 0x06, 0xff, 0x5b, 0xff, 0x00, 0x00, 0x4f, 0xff, 0x66)	'Now, display "1 2 3 4"
is the address for the display data. So this might be the proper way to write the line:
Code:
writei2c 0x00,(0x06, 0xff, 0x5b, 0xff, 0x00, 0x00, 0x4f, 0xff, 0x66)	'Now, display "1 2 3 4"
I would also follow westaust55 advice about using the newer hi2c commands. I thought about buying one of these displays a few months ago but was scared off when I saw the Arduino library.

Good luck,
Rick
 

westaust55

Moderator
Likewise good to see that you have the display now working.

Sorry about missing the i2cmaster parameter in my example :(

As the display controller datasheet emphasises a 1 ms delay form power on to allow the chip to initialise it would be good practive to just include such a short pause to be on the safe side.

The fact that the code you have now posted has a PICAXE initialisation with 4 LET assignment statements, at 4 MHz clock speed these equate to about 1 ms and may suffice but if you clearly include a
PAUSE 1 ; let display chip initialise before first access
then it is clear why the short delay is there.
 

Pongo

Senior Member
How do I modify this (westaust55 based) code to work with a 20X2? I've changed it as below but I'm not getting any display. I have 4K7 pullups on SCL and SDA and my 'scope shows good signals on both.

Code:
#picaxe 20X2				' Define PICAXE 20X2
#no_data                                           ' Ignore EEPROM data - loads program faster

Initialize:					        ' Initialize pins and set up variables - do I need to do this?
	LET dirsB = %11111111		' Make all B pins outputs
	LET dirsC = %00000011		' Make pins C.0 & C.1 outputs; rest as inputs
	LET pinsB = %00000000 		' Turn off all outputs on port B
	LET pinsC = %00000000 		' Turn off all outputs on port C
Main:
	i2cslave %11100000, i2cfast_8, i2cbyte
writei2c (0x21) 	' Turn on clock
pause 100
writei2c (0xEF)	' Set brightness to maximum
pause 100
writei2c (0x81)	'turn blink off and display on
pause 100
writei2c (0x00, 0x06, 0xff, 0x5b, 0xff, 0x00, 0x00, 0x4f, 0xff, 0x66)	'Now, display "1 2 3 4"

end
 

hippy

Technical Support
Staff member
How do I modify this (westaust55 based) code to work with a 20X2?
I2C commands are one of those things which, if working on one PICAXE, should also work on any other without any change. Some commands have been deprecated but those will still work -

I2cSlave -> HI2cSetup I2C_MASTER
WriteI2c -> HI2cOut

You could try changing the I2CFAST_8 to I2CSLOW in case the bus is running too fast for the chip to keep up. Otherwise it seems it may be a hardware issue. Double-check you have not got SDA and SCK crossed-over.
 

Pongo

Senior Member
Checked Vcc/Gnd/SDA/SCK at the pins of the chip, all OK. Tried I2CFAST_8, I2CSLOW_8, I2CFAST, I2CSLOW. The boards are supposed to be 100% tested by Adafruit. Code posted above is stated to work. Very frustrating...
 

Pongo

Senior Member
Got it working, my fault, set the wrong address on the board :eek:

I found some helpful picaxe code for the Adafruit/HT16K33 code on a German picaxe distributor site's forum here.
 

mikeyBoo

Senior Member
HT16K33 notes

Just found some of my old notes on the HT16K33. Not from a Picaxe project (looks like 8052-BASIC), but the notes do discuss some of the quirks of the gadget, (Note the comment on SETUP regs) so might be of some use for you.
I have used the HT16K33 with Picaxe chips before, but couldn't find the old code. With Picaxe, I most always use the OLED display (cause it's easy & I'm lazy).

For-what-it's worth dept:
I really wish the Picaxe folks would adopt the old 8052-BASIC chip for those times when we need floating point & arrays. The 8052 has been around for years (often found as a plug-in board on American PLCs). It's also easy to drop in & out of assembly language when microsecond timing resolution is needed. However, in most applications the Picaxe is a quicker solution because of the simple hardware design & nice editor.
 

Attachments

Pongo

Senior Member
Thanks for posting that listing. Now I have had some experience I find it's actually a pretty easy to use part.
 

stevesmythe

Senior Member
I bought a couple of 8x8 LED matrices that use the same i2c Adafruit backpack with the HT16K33 chip. It took me a while to figure out how to turn on the correct pixels in the matrix as there are a couple of "gotchas" due to the way the LED matrix is wired.
8x8 matrix LED.jpg

1. After initialising, the starting address ($00) is sent, then (in brackets in the example below), the string of data containing the value for each of the 8 columns, where the "value" is the row value in binary, MSB first. However, the row value is a 16 bit value and only the first 8 bits are physically connected. So, every other byte is ignored. I have put $AA into each of these in the example below, just so they are not confused with anything else.
2. For some reason, the physical row 0 (at the top if you look at the display with the Adafruit logo at the bottom) is wired as the MSB so all the other rows are shifted down.

I know that is not a very good explanation but maybe the code will be helpful if anyone else has some of these.
Code:
#picaxe 08m2	' Define PICAXE 08M2
#no_data		' Ignore EEPROM data - loads program faster

Initialise:				' Initialise pins and set up variables
LET dirsC = %00000110		' Make pins C.1 & C.2 outputs; rest as inputs
LET pinsC = %00000000 		' Turn off all outputs on port C
Hi2cSETUP  i2cmaster,%11100000, i2cfast, i2cbyte
PAUSE 1 ; wait 1 ms for the display chip to initialise
Hi2cOUT ($21) ; start clock/oscillator
' $00 turns off the whole row

Main:
Hi2cOUT ($80) ; clear the display


' turn on all LEDS in each column (%11111111=$FF)
' x = don't care (not connected) but I have used $AA
'    start !  col0 x  , col1 x  , col2 x  , col3 x  , col4 x , col5 x  'col6 x  , col 7 x
Hi2cOUT $00, ($FF, $AA, $FF, $AA, $FF, $AA, $FF, $AA, $FF,$AA, $FF, $AA, $FF,$AA, $FF, $AA) 
Hi2cOUT ($81) ; turn on the display
pause 1000

' now draw a smiley face
'  xxxx  
' x    x
'x x  x x
'x      x
'x x  x x
'x  xx  x
' x    x
'  xxxx



'    columns
'7 6 5 4 3 2 1 0
'0 0 1 1 1 1 0 0 row0
'0 1 0 0 0 0 1 0 row1
'1 0 1 0 0 1 0 1 row2
'1 0 0 0 0 0 0 1 row3
'1 0 1 0 0 1 0 1 row4
'1 0 0 1 1 0 0 1 row5
'0 1 0 0 0 0 1 0 row6
'0 0 1 1 1 1 0 0 row7

'Adafruit logo at bottom

'MOVE TOP ROW (ROW0) TO bottom (MSB) AND SHIFT EVERYTHING ELSE DOWN=>BIT0->BIT7, BIT7->BIT6, BIT6->BIT5

' becomes....

'    columns
'7 6 5 4 3 2 1 0
'0 1 0 0 0 0 1 0 row0
'1 0 1 0 0 1 0 1 row1
'1 0 0 0 0 0 0 1 row2
'1 0 1 0 0 1 0 1 row3
'1 0 0 1 1 0 0 1 row4
'0 1 0 0 0 0 1 0 row5
'0 0 1 1 1 1 0 0 row6
'0 0 1 1 1 1 0 0 row7

'Adafruit logo at bottom

'for example, after shifting row 0, column 0 becomes %00011110 or $1E
'    start !  col0 x  , col1 x  , col2 x  , col3 x  , col4 x , col5 x  'col6 x  , col 7 x
Hi2cOUT $00, ($1E, $AA,$21, $AA, $CA, $AA, $D0, $AA, $D0,$AA, $CA, $AA, $21,$AA, $1E, $AA)
Hi2cOUT ($81) ; turn on the display
pause 1000

Hi2cOUT ($E0) 'dim the display (range $E0 to $EF)
pause 1000
Hi2cOUT ($EF) ' full brightness again
pause 1000
Hi2cOUT ($83) 'blink the display ($83=2Hz blink, $85=1Hz, $87=0.5Hz)
pause 3000
Hi2cOUT ($80) ; turn off the display
pause 1000
goto main
 
Top