I2C OLED display

edmunds

Senior Member
Dear all,

I have got one of those displays to play with: http://www.ebay.com/itm/371290271791?_trksid=p2057872.m2749.l2649&ssPageName=STRK:MEBIDX:IT

It took me maybe 20 minutes to get this code with lots of comments in French running to prove it is kind of alive :) : http://www.picaxeforum.co.uk/showthread.php?26180-adafruit-OLED-Display-do-not-want-showing-my-data&highlight=SSD1306

Now, it looks so nice I want to use it for something real.

If I understand everything correctly, the display IC (SSD1306) does not have a built in character set. Displaying any sort of graphics is also pixel by pixel. This would be quite a load for a picaxe chip while what I have (a timer of sorts counting backwards) is proving perfectly possible. If I understand the code correctly, it is drawing every number pixel by pixel and I cannot see any distortion or delays if compared to any screen around the house while picaxe is hardly sweating @32MHz. I'm pointing this out, since many times on this forum I have been told displays, where you would have to draw characters or graphics pixel by pixel are beyond the capabilities of picaxe or at least this is how I took the advice. Anyway, moving the heavy part to a separate X2 chip seems like a good idea and feeding it some ascii characters through HSerIn or external EEPROM locations of messages by I2C from the master later on.

Now, I like what I see - the large numbers, but I would now need smallish characters to take advantage of the tiny pixels of the display for my miniature applications :). Here are my challenges:

a) are there by any chance a way to tell the display display character "A" or an expression "Hello World!" directly that I just have missed somehow?
b) are there character sets with coordinates per character available that I could just load into internal or external EEPROM or someplace else and use for 128x64 OLEDs?
c) there is an arduino library for the same thing here and here, but I cannot figure out where does it get the actual character coordinates from ...



Thank you all for your time,

Edmunds
 

edmunds

Senior Member
Well, ok, I think I got stuck. For the night at least.

I have a "sample code" that works. I can shuffle bits and pieces of it around and get expected different results. However, I cannot figure out the actual contents of hi2cout commands byte by byte or rather their relationship to the data sheet.

For an example, I have a piece of code for initialisation.

Code:
hi2cout (OCM,0xAE,0x00,0x00,0x00,0x20,0x01,0x21,0,127,0x22,0,3,0xD5,0x80,0xA8,0x1F,0xD3,0x00,0x8D, _
0x14,0xA1,0x00,0xC8,0xDA,0x02,0x81,0x7F,0xD9,0xF1,0xDB,0x20,0xA4,0xA6,0xAF)

#rem
hi2cout 0x80,(0xAE)         ;turn off display(RESET=OFF) eteins l'écran
hi2cout 0x80,(0x00,0x00)    ;low column nibble(RESET=0),high column nibble(RESET=0)
hi2cout 0x80,(0x00)         ;start page address(RESET=0)
hi2cout 0x80,(0x20,0x01)    ;memory address mode(RESET=02 [page])			choix du mode de fonctionnement, pour moi vertical=01
hi2cout 0x80,(0x21,0,127)   ;Setup column start and end address			definie la colonne du début et de fin pour l'init. 0 à 127 tout l'écran
hi2cout 0x80,(0x22,0,3)     ;Setup page start and end address			definie les pages de début et de fin 4 pages sur cet ecran de 0~3
hi2cout 0x80,(0xD5,0x80)    ;oscillator frequency and divider(RESET=80)
hi2cout 0x80,(0xA8,0x1F)    ;mux ratio(RESET=3F [64 lines])
hi2cout 0x80,(0xD3,0x00)    ;display offset,COM vertical shift(RESET=0)
hi2cout 0x80,(0x8D,0x14)    ;enable charge pump(RESET=10 [OFF])
hi2cout 0x80,(0xA1,0x00)    ;segment remap(RESET=SEG0, COL0) 00 mirror image
hi2cout 0x80,(0xC8)         ;COM output scan(RESET=C0, C8 flips display) C8=Le premier pixel commence en haut a gauche / C0= la premier pixel commence a bas a gauche
hi2cout 0x80,(0xDA,0x02)    ;COM pins hardware config(RESET=12[alternate])
hi2cout 0x80,(0x81,0x7F)    ;contrast CF(RESET=7F)
hi2cout 0x80,(0xD9,0xF1)    ;pre-charge period F1(RESET=22)
hi2cout 0x80,(0xDB,0x20)    ;Vcom deselect(RESET=20)
hi2cout 0x80,(0xA4)         ;turn all on ignore RAM A5/RAM A4(RESET=A4)
hi2cout 0x80,(0xA6)         ;normal display A6/inverted A7(RESET=A6)
hi2cout 0x80,(0xAF)         ;turn on display
#endrem
It is well commented, but I cannot find a place in the data sheet where it requires this sort of sequence or even describes it. Fair enough, to complicate everything, the data sheet is in binary and this is hex and I'm not good at converting those in my head. Still, I have done the homework to note all the hex values on the corner of the paper, but they still do not match in my mind. Or am I too tired tonight?

When it comes to actually displaying something, I'm struggling with the basic procedure of things. I try to replicate what I have with my own characters, be it a dot or a line somewhere on the screen and I do get something, but randomly different from what I try to achieve. Can anyone try a few-liner, please, to explain what I should be after?


Thank you for your time,

Edmunds
 

BESQUEUT

Senior Member
I have a "sample code" that works.
Please, post that code, and what you see on the screen.
Forgot my code : it's only for 4bits/pixel, your is 1bit/pixel witch is easier.
Is page 34/59 of the datasheet clear for you ?
What is the size of the font you want to draw ?
 
Last edited:

edmunds

Senior Member
Please, post that code, and what you see on the screen.
Forgot my code : it's only for 4bits/pixel, your is 1bit/pixel witch is easier.
Is page 34/59 of the datasheet clear for you ?
What is the size of the font you want to draw ?
In the meantime I found this and it works like a charm. The code seems a tad easier for me to read, so I hope to work through it to be able to write my own. This is a better example to start from, my own is a mess by now. About the pages - I do understand each and every sentence, but to say it altogether makes sense would be an exaggeration :). Without all the details, what would be my algorithm to write a word 'hello'?

I assume it should be sending coordinates x and y from where I want to start drawing and then coordinates of each pixel and state of it - 1 means on, 0 means off. Or something like that. To make life easier, I would expect the screen gets split into some cell like arrangement. And as I understand from the manual, at least one of the coords would be possible to auto-increment.

For starters, the size and the font from the code above will do. Once I figure out how its done, I'm sure I can make my own - larger if necessary.


Thank you for your time,

Edmunds
 

edmunds

Senior Member
I got this thingy working, so I thought I'd post before I complicate the code with all the project specific logic. Thank you for your help, this has been great as usual. :)

Code:
#picaxe40X2

setfreq EM64

'Constants
Symbol I2CSpeed = i2cfast_64

'I/O pins, C.3&C.4 reserved for I2C
Symbol UpBtnPin = pinB.0
Symbol DnBtnPin = pinB.1
Symbol SelBtnPin = pinB.2

'I2C addresses
Symbol Memory = %10100000
Symbol Display = $3C << 1                      '[$78]

'Display command constants
Symbol TWI_BUFFER_LENGTH = 32 
Symbol LCDWIDTH = 128
Symbol LCDHEIGHT = 64
Symbol SETCONTRAST =0x81
Symbol DISPLAYALLON_RESUME =0xA4
Symbol DISPLAYALLON =0xA5
Symbol NORMALDISPLAY =0xA6
Symbol INVERTDISPLAY =0xA7
Symbol DISPLAYOFF =0xAE
Symbol DISPLAYON =0xAF
Symbol SETDISPLAYOFFSET =0xD3
Symbol SETCOMPINS =0xDA
Symbol SETVCOMDETECT =0xDB
Symbol SETDISPLAYCLOCKDIV =0xD5
Symbol SETPRECHARGE =0xD9
Symbol SETMULTIPLEX =0xA8
Symbol SETLOWCOLUMN =0x00
Symbol SETHIGHCOLUMN =0x10
Symbol SETSTARTLINE =0x40
Symbol MEMORYMODE =0x20
Symbol COLUMNADDR =0x21
Symbol PAGEADDR =0x22
Symbol COMSCANINC =0xC0
Symbol COMSCANDEC =0xC8
Symbol SEGREMAP =0xA0 | 1
Symbol CHARGEPUMP =0x8D
Symbol EXTERNALVCC =0x1
Symbol SWITCHCAPVCC =0x2

'Display scrolling commands
Symbol ACTIVATE_SCROLL =0x2F
Symbol DEACTIVATE_SCROLL =0x2E
Symbol SET_VERTICAL_SCROLL_AREA =0xA3
Symbol HORIZONTAL_SCROLL_RIGHT =0x26
Symbol HORIZONTAL_SCROLL_LEFT =0x27
Symbol VERT_AND_RIGHT_HORIZONTAL =0x29
Symbol VERT_AND_LEFT_HORIZONTAL =0x2A

'Byte variables
Symbol row = b6
Symbol col = b7

init:

b0 = 0
b1 = 0
b2 = 0

pause 500

hi2csetup i2cmaster, Display, I2CSpeed, I2cbyte

hi2cout (0, DISPLAYOFF)
hi2cout (0, SETDISPLAYCLOCKDIV)
hi2cout (0, 0x80)
hi2cout (0, SETMULTIPLEX)
hi2cout (0, 0x3F)
hi2cout (0, SETDISPLAYOFFSET)
hi2cout (0, 0x0)
hi2cout (0, SETSTARTLINE)
hi2cout (0, CHARGEPUMP)
hi2cout (0, 0x14)
hi2cout (0, MEMORYMODE)
hi2cout (0, 0x00)
hi2cout (0, SEGREMAP)
hi2cout (0, COMSCANDEC)
hi2cout (0, SETCOMPINS)
hi2cout (0, 0x12)
hi2cout (0, SETCONTRAST)
hi2cout (0, 0xCF)
hi2cout (0, SETPRECHARGE)
hi2cout (0, 0xF1)
hi2cout (0, SETVCOMDETECT)
hi2cout (0, 0x40)
hi2cout (0, DISPLAYALLON_RESUME)
hi2cout (0, DISPLAYON)

pause 500

main:
  'say something. anything.
  gosub ClearDisplay
  row = 4
  col = 30
  gosub SetPosition

  hi2cout (0x40, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00)   'H with extra pixel added for char spacing (8x6 font here)
  hi2cout (0x40, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00)   'E
  hi2cout (0x40, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00)   'L
  hi2cout (0x40, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00)   'L
  hi2cout (0x40, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00)   'O

  hi2cout (0x40, 0x00, 0x00, 0x00, 0x00, 0x00)             'space

  hi2cout (0x40, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00)   'W
  hi2cout (0x40, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00)   'O
  hi2cout (0x40, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00)   'R
  hi2cout (0x40, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00)   'L
  hi2cout (0x40, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00)   'D
  hi2cout (0x40, 0x00, 0x5F, 0x00, 0x00, 0x00)             '! with the first col removed, looks better
  
  pause 10000
 
goto main	

SetPosition:
  hi2cout (0, PAGEADDR)
  hi2cout (0, row)
  hi2cout (0, 7)

  hi2cout (0, COLUMNADDR)
;  col = col * 5 'columns per character   -   !!! NB not sure what this does or does not !!!
  hi2cout (0, col)
  hi2cout (0, 127)

return

ClearDisplay:

  for b2 = 0 to LCDWIDTH
    hi2cout (0x40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
  next b2

  hi2cout (0, COLUMNADDR)
  hi2cout (0, 0)
  hi2cout (0, 127)
  hi2cout (0, PAGEADDR)
  hi2cout (0, 0)
  hi2cout (0, 7)
  
return



' STANDARD FONT

'table (0x00, 0x00, 0x00, 0x00, 0x00)	' Space
'table (0x00, 0x00, 0x5F, 0x00, 0x00)	' !
'table (0x00, 0x07, 0x00, 0x07, 0x00) ' "
'table (0x14, 0x7F, 0x14, 0x7F, 0x14) ' #
'table (0x24, 0x2A, 0x7F, 0x2A, 0x12) ' $
'table (0x23, 0x13, 0x08, 0x64, 0x62) ' %
'table (0x36, 0x49, 0x56, 0x20, 0x50) ' &
'table (0x00, 0x08, 0x07, 0x03, 0x00) ' '

table (0x00, 0x1C, 0x22, 0x41, 0x00) ' (
table (0x00, 0x41, 0x22, 0x1C, 0x00) ' )
table (0x2A, 0x1C, 0x7F, 0x1C, 0x2A) ' *
table (0x08, 0x08, 0x3E, 0x08, 0x08) ' +
table (0x00, 0x80, 0x70, 0x30, 0x00) ' ,
table (0x08, 0x08, 0x08, 0x08, 0x08)	' -
table (0x00, 0x00, 0x5F, 0x00, 0x0)	' .
table (0x20, 0x10, 0x08, 0x04, 0x02)	' /

table (0x3E, 0x51, 0x49, 0x45, 0x3E)   ;9 : pos40
table (0x00, 0x42, 0x7F, 0x40, 0x00)	 ; 1
table (0x72, 0x49, 0x49, 0x49, 0x46)
table (0x21, 0x41, 0x49, 0x4D, 0x33)
table (0x18, 0x14, 0x12, 0x7F, 0x10)
table (0x27, 0x45, 0x45, 0x45, 0x39)
table (0x3C, 0x4A, 0x49, 0x49, 0x31)
table (0x41, 0x21, 0x11, 0x09, 0x07)
table (0x36, 0x49, 0x49, 0x49, 0x36)
table (0x46, 0x49, 0x49, 0x29, 0x1E)
table (0x00, 0x00, 0x14, 0x00, 0x00)
table (0x00, 0x40, 0x34, 0x00, 0x00)
table (0x00, 0x08, 0x14, 0x22, 0x41)
table (0x14, 0x14, 0x14, 0x14, 0x14)
table (0x00, 0x41, 0x22, 0x14, 0x08)
table (0x02, 0x01, 0x59, 0x09, 0x06)
table (0x3E, 0x41, 0x5D, 0x59, 0x4E)
table (0x7C, 0x12, 0x11, 0x12, 0x7C) ;A most likely and extra blank pixel would need to be added
table (0x7F, 0x49, 0x49, 0x49, 0x36)  ;B
table (0x3E, 0x41, 0x41, 0x41, 0x22)  ;C
table (0x7F, 0x41, 0x41, 0x41, 0x3E)  ;D
table (0x7F, 0x49, 0x49, 0x49, 0x41)  ;E
table (0x7F, 0x09, 0x09, 0x09, 0x01)  ;F
table (0x3E, 0x41, 0x41, 0x51, 0x73)  ;G
table (0x7F, 0x08, 0x08, 0x08, 0x7F)  ;H
table (0x00, 0x41, 0x7F, 0x41, 0x00)  ;I
table (0x20, 0x40, 0x41, 0x3F, 0x01)  ;J
table (0x7F, 0x08, 0x14, 0x22, 0x41)  ;K
table (0x7F, 0x40, 0x40, 0x40, 0x40)  ;L
table (0x7F, 0x02, 0x1C, 0x02, 0x7F)  ;M
table (0x7F, 0x04, 0x08, 0x10, 0x7F)  ;N
table (0x3E, 0x41, 0x41, 0x41, 0x3E)  ;O
table (0x7F, 0x09, 0x09, 0x09, 0x06)  ;P
table (0x3E, 0x41, 0x51, 0x21, 0x5E)  ;Q
table (0x7F, 0x09, 0x19, 0x29, 0x46)  ;R
table (0x26, 0x49, 0x49, 0x49, 0x32)  ;S
table (0x03, 0x01, 0x7F, 0x01, 0x03)  ;T
table (0x3F, 0x40, 0x40, 0x40, 0x3F)   ;U
table (0x1F, 0x20, 0x40, 0x20, 0x1F)   ;V
table (0x3F, 0x40, 0x38, 0x40, 0x3F)   ;W 
table (0x63, 0x14, 0x08, 0x14, 0x63)  ;X
table (0x03, 0x04, 0x78, 0x04, 0x03)  ;Y
table (0x61, 0x59, 0x49, 0x4D, 0x43)	;Z

Edmunds
 

edmunds

Senior Member
Strategic question :).


I now have a significant number of menu items to display. For the sake of simplicity, let's stay at the discussion about alphanumeric items. Every character is 6 bytes if you include the space or one byte (8 rows, 1 col) after each character. Punctuation and other symbols might be smaller, but 6 bytes for the calculations it is. Menu items are like 'Lane 1 Setup ...', 'Save and Exit ...' and so on.

There is no way I have space for all of this in the chip, so EEPROM it is. I2C at least for now, because I have some and know how to operate them.

The question is, how do I organise the storage and what are ways people normally approach this kind of thing?

I can, for an example, have characters stored in a table (enough space for uppercase or lowercase only) and then store addresses for the start of the characters to combine a menu item stored in the eeprom.

Or I can organise the eeprom to have the actual characters in the beginning and then menu items in later locations.

Or I can organise characters into words and have menu items organised in a way to call the required words.

Or maybe the menu items consisting of word addresses would be sitting in the table and eeprom would have characters and words.

A bit puzzled at this point. What other limitations besides the obvious memory size limitations do I have to keep in mind? What is the most efficient way to do it for speed or storage? Or both?


Edmunds
 

BESQUEUT

Senior Member
There is no way I have space for all of this in the chip,
Not so sure...
If you have a look at the link in #2, you can see the code :
Code:
	select case b1
	case >="a"
		b1=b1-"a"
		on b1 gosub Ecr_a,Ecr_b,Ecr_c,Ecr_d,Ecr_e,Ecr_f,Ecr_g,Ecr_h,Ecr_i,Ecr_j,Ecr_k,Ecr_l,Ecr_m,Ecr_n,Ecr_o,Ecr_p,Ecr_q,Ecr_r,Ecr_s,Ecr_t,Ecr_u,Ecr_v,Ecr_w,Ecr_x,Ecr_y,Ecr_z
	
	case >="A"
		b1=b1-"A"
		on b1 gosub EcrA,EcrB,EcrC,EcrD,EcrE,EcrF,EcrG,EcrH,EcrI,EcrJ,EcrK,EcrL,EcrM,EcrN,EcrO,EcrP,EcrQ,EcrR,EcrS,EcrT,EcrU,EcrV,EcrW,EcrX,EcrY,EcrZ
...

Ecr_H : hi2cout (0x40, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00) : RETURN
So character set is in program memory, and drawing is very fast.
With a 40X2 you have 4 slots, 4096 bytes each.

If you have menu items like 'Lane 1 Setup ...' 'Lane 2 Setup ...' 'Lane 3 Setup ...'
you can have one subroutine to do all that,
or one to write "Lane", one for any number and one for "Setup"...
 
Last edited:

hippy

Technical Support
Staff member
A bit puzzled at this point. What other limitations besides the obvious memory size limitations do I have to keep in mind? What is the most efficient way to do it for speed or storage? Or both?
Not sure, not familiar with your hardware, and not entirely sure what you mean with all your proposed solutions.

The traditional way to do pixel graphics is to have a font table holding the printable characters as bitmaps, and a list of strings of ASCII characters. Select the appropriate string, extract the characters one at a time, draw the appropriate pixels from the font table for each character. Using EEPROM and TABLE will give enough space to store 73 characters using a 7 pixel high font; space, A-Z, a-z, 0-9 plus 10 others which should be enough. The actual strings can be held in I2C EEPROM. The font table could be put there too but that may mean jumping between the two which may slow things down.

I would start with that, then consider optimising if necessary.

The other trick is to store bitmap images for whole lines or screens allowing fast get and put operations. The problem there is the size may grow quite considerably. Full screen bitmaps create problems if you have lines wider than the screen which then need to scroll. You may need to add the character drawing capability if text needs to change.

You likely need to come up with an estimate of how many messages you want to display, what the menu screens look like, and how they are used and interact, before knowing how much data that might require and deciding which is the best solution. There's no easy 'do this' answer because few do it and how the menus work and interact depends on application and may differ to how others might do it.

My approach would be to write a program to simulate it all on a PC and have the program generate the data which will then be used with the PICAXE program.

If the menus could be mostly implemented with static bitmap screens I might be tempted to use SD Card holding screen bitmaps.
 

edmunds

Senior Member
Not so sure...
If you have a look at the link in #2, you can see the code :
Code:
	select case b1
	case >="a"
		b1=b1-"a"
		on b1 gosub Ecr_a,Ecr_b,Ecr_c,Ecr_d,Ecr_e,Ecr_f,Ecr_g,Ecr_h,Ecr_i,Ecr_j,Ecr_k,Ecr_l,Ecr_m,Ecr_n,Ecr_o,Ecr_p,Ecr_q,Ecr_r,Ecr_s,Ecr_t,Ecr_u,Ecr_v,Ecr_w,Ecr_x,Ecr_y,Ecr_z
	
	case >="A"
		b1=b1-"A"
		on b1 gosub EcrA,EcrB,EcrC,EcrD,EcrE,EcrF,EcrG,EcrH,EcrI,EcrJ,EcrK,EcrL,EcrM,EcrN,EcrO,EcrP,EcrQ,EcrR,EcrS,EcrT,EcrU,EcrV,EcrW,EcrX,EcrY,EcrZ
...

Ecr_H : hi2cout (0x40, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00) : RETURN
So character set is in program memory, and drawing is very fast.
With a 40X2 you have 4 slots, 4096 bytes each.

If you have menu items like 'Lane 1 Setup ...' 'Lane 2 Setup ...' 'Lane 3 Setup ...'
you can have one subroutine to do all that,
or one to write "Lane", one for any number and one for "Setup"...
Thanks, this is getting me forward. Might be able to put everything in.

Edmunds
 

edmunds

Senior Member
Not sure, not familiar with your hardware, and not entirely sure what you mean with all your proposed solutions.

The traditional way to do pixel graphics is to have a font table holding the printable characters as bitmaps, and a list of strings of ASCII characters. Select the appropriate string, extract the characters one at a time, draw the appropriate pixels from the font table for each character. Using EEPROM and TABLE will give enough space to store 73 characters using a 7 pixel high font; space, A-Z, a-z, 0-9 plus 10 others which should be enough. The actual strings can be held in I2C EEPROM. The font table could be put there too but that may mean jumping between the two which may slow things down.

I would start with that, then consider optimising if necessary.

The other trick is to store bitmap images for whole lines or screens allowing fast get and put operations. The problem there is the size may grow quite considerably. Full screen bitmaps create problems if you have lines wider than the screen which then need to scroll. You may need to add the character drawing capability if text needs to change.

You likely need to come up with an estimate of how many messages you want to display, what the menu screens look like, and how they are used and interact, before knowing how much data that might require and deciding which is the best solution. There's no easy 'do this' answer because few do it and how the menus work and interact depends on application and may differ to how others might do it.

My approach would be to write a program to simulate it all on a PC and have the program generate the data which will then be used with the PICAXE program.

If the menus could be mostly implemented with static bitmap screens I might be tempted to use SD Card holding screen bitmaps.
Oj. That was complicated. I hope I don't have to go as far as bitmaps on SD cards. I'm two menu levels deep by now and it seems to work. Lets see where I get stuck :).

Edmunds
 

edmunds

Senior Member
Well, as usual, @hippy turned out to be right :).

It is a just-do-it sort of thing. Still have some minor bugs, but the short video shows how far I am: http://youtu.be/JXZg_IP1vSI. The video is a bit blurry to keep the size decent, but in real life the display is super crisp and looks really good.

To list functions implemented so far:

1. Full character set, stored in program memory, both small and capital letters;
2. Some simple bitmaps - triangles to represent cursor and continuing lists up and down, checkbox-set and -clear;
3. Multi-page menu lists with indication there are more items in the next/previous page;
4. Multi-level menu;
5. Checkbox functionality setting or clearing bits of variables written to chip's EEPROM memory afterwards.

ToDo:
1. User defined menu items - a user can add menu items, name them and set some parameters for them;
2. User defined servo right-centre-left servo positions;
3. Improving and sorting out the ClearScreen and ClearDisplay functions (about the slowest thing there is at the moment);
4. Better debouncing of buttons, try a rotary-encoder;
5. Continue optimising the code to remain in 2-3 program slot limit even with more functions added;
6. Clean everything up and make beautiful with comments and stuff :)


View attachment ICCU_v_8.0_Slot2_Forum.bas
View attachment ICCU_v_8.0_Slot1_Forum.bas

Thank you all for your input so far and feedback is extremely welcome :),

Edmunds
 

BESQUEUT

Senior Member
Well, as usual, @hippy turned out to be right :).
?
To list functions implemented so far:

1. Full character set, stored in program memory, both small and capital letters;
2. Some simple bitmaps - triangles to represent cursor and continuing lists up and down, checkbox-set and -clear;
3. Multi-page menu lists with indication there are more items in the next/previous page;
4. Multi-level menu;
5. Checkbox functionality setting or clearing bits of variables written to chip's EEPROM memory afterwards.
I have only one word of feedback :
BRAVO !
- no need for external components,
- clear and easy to maintain,
- EEPROM fully usable for others things, like storing user preferences,
- fast as we can see by video (with a Picaxe, that's not so evident...)
- well designed : easy to use without notice.

Little optimisation :
Code:
WrdLayout:
	gosub Chr_L
	gosub Chr_a
	gosub Chr_y
	gosub Chr_o
	gosub Chr_u
	gosub Chr_t
return
- as readable,
- 20 bytes less,
- even faster...
 
Last edited:

edmunds

Senior Member
- as readable,
- 20 bytes less,
- even faster...
Thanks. The hippy's recommendation a couple of posts back was there is no standard way to do it, and the way to do it, is just to do it. To make it short :). This was what I was referring to.

Very good optimisation solution. Gives me a lot more memory :). Must have been kind of home-blind not to see that myself. However, in one of the slots, I ran into 255 GOSUBS limit. Will have to roll back to 'on Counter gosub'.


Edmunds
 

lbenson

Senior Member
Really nice display. Congratulations. Maybe Rev-ed can make a 3-wire serial version with all of the hard work hidden. That would be a sweet thing to put on many picaxe projects.
 

lbenson

Senior Member
I've ordered two of these displays.

I wanted to look at another approach. I noticed that the characters are drawn with 7 bytes, with the first and last always the same. For capital letters, I extracted the unique 5 in the middle and put them to the scratchpad. Then I established strings, (terminated with 0) in table memory, and have tried to print the strings--for each character figuring out the location of the unique middle five bytes with "ptr = char - "A" * 5" and then writing that character with hi2cout (0x40, the 5 unique bytes, 0).

I'm not sure I got all the initialization right. If you have time, could you see if this works (perhaps with juggling to place the cursor)?
Code:
' 28LCD_128x64 writes strings to lcd
#picaxe 28X2

'Display command constants
Symbol TWI_BUFFER_LENGTH = 32 
Symbol LCDWIDTH = 128
Symbol LCDHEIGHT = 64
Symbol SETCONTRAST =0x81
Symbol DISPLAYALLON_RESUME =0xA4
Symbol DISPLAYALLON =0xA5
Symbol NORMALDISPLAY =0xA6
Symbol INVERTDISPLAY =0xA7
Symbol DISPLAYOFF =0xAE
Symbol DISPLAYON =0xAF
Symbol SETDISPLAYOFFSET =0xD3
Symbol SETCOMPINS =0xDA
Symbol SETVCOMDETECT =0xDB
Symbol SETDISPLAYCLOCKDIV =0xD5
Symbol SETPRECHARGE =0xD9
Symbol SETMULTIPLEX =0xA8
Symbol SETLOWColUMN =0x00
Symbol SETHIGHColUMN =0x10
Symbol SETSTARTLINE =0x40
Symbol MEMORYMODE =0x20
Symbol COLUMNADDR =0x21
Symbol PAGEADDR =0x22
Symbol COMSCANINC =0xC0
Symbol COMSCANDEC =0xC8
Symbol SEGREMAP =0xA0 | 1
Symbol CHARGEPUMP =0x8D
Symbol EXTERNALVCC =0x1
Symbol SWITCHCAPVCC =0x2

symbol A_addr = 0 ' beginning of eeprom
put A_addr,0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, _
               0x3E, 0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, _
               0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, _
               0x3E, 0x41, 0x41, 0x51, 0x73, 0x7F, 0x08, 0x08, 0x08, 0x7F, _
               0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, _
               0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, 0x40, _
               0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, _
               0x3E, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, _
               0x3E, 0x41, 0x51, 0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, _
               0x26, 0x49, 0x49, 0x49, 0x32, 0x03, 0x01, 0x7F, 0x01, 0x03, _
               0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20, 0x40, 0x20, 0x1F, _
               0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, 0x63, _
               0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4D, 0x43

Symbol I2CSpeed = i2cfast_64
Symbol Display = $3C << 1                       '[$78]

symbol line1addr = 0
symbol line2addr = 10
table line1addr,("MESSAGE 1",0)
table line2addr,("ANOTHER MESSAGE",0)

  hi2csetup i2cmaster, Display, I2CSpeed, I2cbyte   'Need to initialise I2C 
  gosub InitDisplay
  b1 = line1addr
  gosub putString
  b1 = line2addr
  gosub putString
  end

putString:
  readtable b1,b0
  do while b0 <> 0
    sertxd(b0)
    if b0 >= "A" and b0 <= "Z" then
      ptr = b0 - "A" * 5 ' point to 5 unique char bytes
      hi2cout (0x40, @ptrinc, @ptrinc, @ptrinc, @ptrinc, @ptrinc, 0)
    endif
    inc b1
    readtable b1,b0
  loop
  return
  
  InitDisplay:
  hi2cout (0, DISPLAYOFF)
  hi2cout (0, SETDISPLAYCLOCKDIV)
  hi2cout (0, 0x80)
  hi2cout (0, SETMULTIPLEX)
  hi2cout (0, 0x3F)
  hi2cout (0, SETDISPLAYOFFSET)
  hi2cout (0, 0x0)
  hi2cout (0, SETSTARTLINE)
  hi2cout (0, CHARGEPUMP)
  hi2cout (0, 0x14)
  hi2cout (0, MEMORYMODE)
  hi2cout (0, 0x00)
  hi2cout (0, SEGREMAP)
  hi2cout (0, COMSCANDEC)
  hi2cout (0, SETCOMPINS)
  hi2cout (0, 0x12)
  hi2cout (0, SETCONTRAST)
  hi2cout (0, 0xCF)
  hi2cout (0, SETPRECHARGE)
  hi2cout (0, 0xF1)
  hi2cout (0, SETVCOMDETECT)
  hi2cout (0, 0x40)
  hi2cout (0, DISPLAYALLON_RESUME)
  hi2cout (0, DISPLAYON)
'  gosub LoadTopMenuPage
return
This runs in the simulator, putting out the characters with sertxd.

(By the way, I wish we had 'put loc,"character string"' and 'poke loc,"character string"' the way we have 'eeprom loc,("character string")' and table loc,("character string")'.)
 
Last edited:

edmunds

Senior Member
I wanted to look at another approach.
The idea sounds interesting, but would it be possible to combine it with background serial receive?

Anyway, I wanted to try it so I have been at it since morning here. Your code does not work directly - blank screen. Something with initialisation, I guess. I tried to post the part where you generate the message and the required scratchpad and table commands into my code.

This initialises, but the messages do not work, despite I get the correct SerTxd. I have been trying to figure out how you "calculate" the character to display, but no luck. If you could try to explain that, I could try some more to get it working :)

Edmunds
 

lbenson

Senior Member
Edmunds,

Sorry it didn't work out of the box. That may have been asking a lot.

The code could work with background serial receive, depending on how much you're receiving and whether you reset hserptr or just let it wrap through the whole 1024-byte scratchpad buffer.

I placed the codes for writing the characters at position 0 in the scratchpad, but it could be further on in the scratchpad (allowing some space for background serial receive), or in upper ram. It could also be in eeprom.

The heart of the code is getting a single character from the table to display ("readtable b1,b0"). The table memory contains strings terminated with a 0.

The OLED drawing codes are in a 26-element array of 5 bytes each, representing the letters "A"-"Z". "A" is the zeroth element of the array, so you can point to its location with "ptr = "A" - "A" * 5, which is 0. The second array, representing "B", is "B" - "A" (which is 1) * 5, or 5, so with ptr equal to 5, the five @ptrinc commands should retrieve and send out the internal 5 characters of the OLED drawing command.

Maybe start by drawing an "A" with your code to make sure that the cursor position is correct for the rest of the code.

Your menu system is really clean-looking and quick. It would be nice to be able to provide the strings to be written more easily.
 
Last edited:

edmunds

Senior Member
The OLED drawing codes are in a 26-element array of 5 bytes each, representing the letters "A"-"Z". "A" is the zeroth element of the array, so you can poing to its location with "ptr = "A" - "A" * 5, which is 0. The second array, representing "B", is "B" - "A" (which is 1) * 5, or 5, so with ptr equal to 5, the five @ptrinc commands should retrieve and send out the internal 5 characters of the OLED drawing command.
It is clear, how to show all the characters in a sequence this way. But how do you show a character M followed by character E? I mean M is some location 677 and E is 602. I don't understand how A - A * 5 will take you to 667 or whatever it is.

What I'm working on now, is I'm filling scratchpad locations 512 to 987 with ASCII table from 32 to 127. I'm thinking, I can then have messages as rows of my pseudo-ascii scratchpad locations in the table array, terminated with a stop character (could be 0, but could be something more extraordinary). The only problem with this is that the location is now becoming a word size variable which limits the number of characters one can store in a table to 127, which is not much. I could push the ASCII table down to byte size locations, but there would not be enough space for the whole table anyway and that interferes with HSERIN. There would not be enough space in the built in EEPROM for another lookup table containing ASCII code - scratchpad location matches, as this would take 285 bytes (3 * 95) for a full table. I could throw out a couple of strange characters from the table and that might be fine for many projects or if one would want to build a standalone display driver like this. For my current project, though, I could give up maybe half of the EEPROM and even that reluctantly, since this would limit my future options.

Of course, I can store as much as I can in the table and the rest in the program memory as I have done before. This solution is better, because the files above make no use of table or scratchpad memory. But not perfect yet :).

Thanks for the pointer to HSERPTR, this should work then.

EDIT: EEPROM space calculation is actually not correct. It is somewhere in between. Just ignore it.

Edmunds
 

lbenson

Senior Member
I was offsetting "A" - "Z" to 0, so the "B" element in the 5-byte array would be "B" - "A", or 1, times 5, so the offset into my table starting at scratchpad 0 is 5. Likewise "M" - "A" is 12, so the offset would be 12*5 or 60--the 5 bytes for "M" would be at offset 60 in the scratchpad.

If you are using the entire ascii table, then "M" = 77, so the offset would be 77*5 or 385. If you're using the ascii table, you don't need the first 32 unprintable characters, or you can use them for your special characters, like the cleared and checked checkboxes. Then you would need 128*5 or 640 bytes. If you don't want to use 640 bytes in the scratchpad, you could put them in external eeprom. For instance, if you are using a real-time-clock module like this one -- http://www.ebay.com/itm/DS3231-AT24C32-IIC-module-precision-Real-time-clock-quare-memory-for-Arduino-/221549752451?pt=LH_DefaultDomain_0&hash=item339564f083 -- then you could store the data in the associated i2c eeprom.
 

Rick100

Senior Member
Ibenson,

I have one of these displays and tried your code on a 20X2. It worked after I added a gosub InitDisplay after the hi2csetup command. It doesn't print the first character of each string though.
 

edmunds

Senior Member
Bingo!

I've been barking up the wrong tree here :). The bit I could not get my head around was that every "character" already is an ASCII code i.e. number, so the compiler already "knows" the ASCII value (number) of the character. This should solve the problem that I could not come up with an elegant solution for. Will be back. Thank you so much!

Edmunds
 

lbenson

Senior Member
lbenson,

I have one of these displays and tried your code on a 20X2. It worked after I added a gosub InitDisplay after the hi2csetup command. It doesn't print the first character of each string though.
Thanks for that. Rookie error on my part. Within putString, "inc b1" and "readtable b1,b0" should follow the "if" statement, not preceed it.

Also, I included the InitDisplay subroutine, but neglected to call it.

I've edited the code in the posting to correct these errors.
 
Last edited:

edmunds

Senior Member
So.
I understood the pointing thing.
I typed almost all of the ASCII table (32 to 127, 5 bytes each) into the PUT commands to save it to scratchpad.
I typed my strings into TABLE commands to feed them to the clever pointing routine.
I re-wrote my menu items generation routines for the new string generation engine.

Only to learn that this takes as much or more program space than the previous, less clever solution and is also noticeably slower - probably useable, but not so cool even at 64MHz.

It has been a great day of learning, however and the code is below before I scrap most of it to try another idea :).

Thank you all for your input,

Edmunds

View attachment ICCU_v_9.0_Slot1_Forum.bas
 

lbenson

Senior Member
A lot of good work there. Note that sertxd takes a lot of time. How fast is it if you remove the sertxd statements from DisplayString?

Speed of execution was an uncertainty, and those puts do take a lot of programming space. Putting the drawing codes into external eeprom, if available, would fix that.

This is good proof of concept though. Now I hope Rev-ed makes a little module with a raw pic to store the codes and quickly pass to the OLED the strings sent to it via a serial connection.

One advantage to this code--it would be easier to add new strings.
 

edmunds

Senior Member
A lot of good work there. Note that sertxd takes a lot of time. How fast is it if you remove the sertxd statements from DisplayString?

Speed of execution was an uncertainty, and those puts do take a lot of programming space. Putting the drawing codes into external eeprom, if available, would fix that.

This is good proof of concept though. Now I hope Rev-ed makes a little module with a raw pic to store the codes and quickly pass to the OLED the strings sent to it via a serial connection.

One advantage to this code--it would be easier to add new strings.
I tried without the sertxd as well. I could not see the difference with a naked eye, but I will try some more later.

Not only this was easier to add new strings, it would also be very simple to make this into a serially driven "display driver" if one would like that.

I am now thinking if I can name the procedures their ascii codes, rather than Chr_x and use a version of your pointing routine to call them. The part that I'm struggling to solve efficiently is that I cannot call a label with a variable. The only solution then is a 127 long select case statement and I just have to try it to see how efficient that is. Or is there some other way?

Edmunds
 

hippy

Technical Support
Staff member
I am now thinking if I can name the procedures their ascii codes, rather than Chr_x and use a version of your pointing routine to call them. The part that I'm struggling to solve efficiently is that I cannot call a label with a variable. The only solution then is a 127 long select case statement and I just have to try it to see how efficient that is. Or is there some other way?
Normally one would not jump to individual routines for each letter but would pass the ASCII value of the letter to be shown, take the appropriate data to put out from the font table rather than have individual routines to generate that.

So, if you had a routine to show "END" ...

Code:
Show_END:
  Gosub Chr_E
  Gosub Chr_N
  Gosub Chr_D
  Return
That would be -

Code:
Show_END:
  b0 = "E" : Gosub ShowChar
  b0 = "N" : Gosub ShowChar
  b0 = "D" : Gosub ShowChar
  Return
Or, optimised to save GOSUB calls and minimise program code ...

Code:
Show_END:
  For b1 = 0 To 2
    LookUp b1, ( "END" ), b0 : Gosub ShowChar
  Next
  Return
 

westaust55

Moderator
@Edmunds,

If you want some reading have a look at this thread from 2008 with my first work with graphic LCD displays taken from mobile phones (later threads covered other displays including colour).
http://www.picaxeforum.co.uk/showthread.php?10014-Siemens-A55-C55-LCD-graphics-display
This not only has drivers fro the display (of little interest to your task) but routines for displaying characters (5 pixels wide) and even a for of proportional font in later posts where spaces and narrow characters such as "I" took less that the total of (5+1) pixels width. I went on to have double width/height characters and some primitive graphics.
So while it was for LCD rather than OLED graphic displays there might be information of use.
 

edmunds

Senior Member
@Edmunds,

If you want some reading have a look at this thread from 2008 with my first work with graphic LCD displays taken from mobile phones (later threads covered other displays including colour).
http://www.picaxeforum.co.uk/showthread.php?10014-Siemens-A55-C55-LCD-graphics-display
This not only has drivers fro the display (of little interest to your task) but routines for displaying characters (5 pixels wide) and even a for of proportional font in later posts where spaces and narrow characters such as "I" took less that the total of (5+1) pixels width. I went on to have double width/height characters and some primitive graphics.
So while it was for LCD rather than OLED graphic displays there might be information of use.
Great stuff, thank you, will have a look :).

Edmunds
 

edmunds

Senior Member
A lot of good work there. Note that sertxd takes a lot of time. How fast is it if you remove the sertxd statements from DisplayString?

Speed of execution was an uncertainty, and those puts do take a lot of programming space. Putting the drawing codes into external eeprom, if available, would fix that.
I did some real speed tests after some optimisations. Might not be that bad. At the moment I'm loosing only two hundreds of a second to the code directly from program memory and there is still room for optimisation. Especially with the tons of meticulously described previous work by @westaust55 on basically the same thing.

So I'm now working on both files in parallel literally. Lets see what comes out best :)

Edmunds
 

edmunds

Senior Member
Just a progress report to cool off my brain and re-group the cells while summarising what is done and what remains to be done :).


I will split the code in the following way:

slot0 - allocated for main program, run 1 executed to switch into SETUP mode
slot1 (slot0 while coding and testing) - allocated to part of the functions of the setup menus and display [2939 bytes]
slot2 - allocated to part of the functions of the setup menus and display [3671 bytes]
slot3 - allocated to data loading into various memory areas, space for some program code for the future [1790 bytes]

Scratchpad - locations from 512 upwards filled with character definitions, ASCII characters 32 to 127 supported
Table - default settings storage, so I can go back to default settings at any time
EEPROM - settings storage, overwritten by values from Table in case Load Default Settings is requested

Functions implemented and reasonably thoroughly tested that could be of interested:

0. Three button interface - Up, Down and Select;
1. Full character set, stored in scratchpad, both small and capital letters;
2. Some simple bitmaps - triangles to represent cursor and continuing lists up and down, checkbox-set and -clear;
3. Multi-page menu lists with indication there are more items in the next/previous page;
4. Multi-level menu;
5. Checkbox functionality setting or clearing bits of variables written to chip's EEPROM memory afterwards;
6. Radio-button-like functionality, where only one option of several can be selected (selecting a new option clears the previous one), written to chip's EEPROM memory afterwards;
7. Engine to set values in the range of 0 to 255 for a menu parameter - in this case three servo positions, left, centre and right for each traffic lanes of 4; removing leading zeros from 3 or 2 digit number; stored in chip's EEPROM memory afterwards;
8. YES/NO message window for confirming/cancelling the selected action.

Now doing preparations for the most challenging part - letting a user to add, delete and rename certain menu items. And the first challenge is interface design, not putting the code together.

Link to a video and program files posted below.

https://youtu.be/PLjKaargY40

View attachment ICCU_v_9.0_Slot1_Forum.bas
View attachment ICCU_v_9.0_Slot2_Forum.bas
[The third file just would not upload, so see next post]

Suggestion on further improvements of all kinds are, of course, very much welcome!

Cheers,

Edmunds
 
Last edited:

edmunds

Senior Member
Code:
;settimer 64286                                      '65536 - (1 000 000us [1s] / 8us [1 minor tick @32MHz] / 100 [to get hundreds of a second])

#slot 3
#picaxe40x2

setfreq EM64

'Flag variables
Symbol DataLoadedFlag = b21

'Load values into table
'Lane setup bytes:
'First byte - Main Road (bit0), Cars, Trucks, Long Trucks, Bus 1, Bus 2, Emergency, Emergency on Duty (bit7)
'Second byte - Batt-Low Cars (bit8), Batt-Low Trucks, Batt-Low Buses, Layout OFF, Reserved, Reserved, Reserved, Reserved (bit15)
'Third byte - User Defined Group 1 (bit0), User Defined Group 2, User Defined Group 3, User Defined Group 4, User Defined Group 5, Reserved, Reserved, Reserved
'Fourth byte - Reserved (bit0), Reserved, Reserved, Reserved, Reserved, Reserved, Reserved, Reserved

table 0, (%11111110,%00001111,%00000000,%00000000)      'Lane 1 Default Setup
table 4, (%11111110,%00001111,%00000000,%00000000)      'Lane 2 Default Setup
table 8, (%11111110,%00001111,%00000000,%00000000)      'Lane 3 Default Setup
table 12, (%11111110,%00001111,%00000000,%00000000)    'Lane 4 Default Setup

'Traffic Lights setup bits - Normal Mode (bit0), Yellow Only Mode, Switch OFF (bit2)
table 16, (%00000001)                    'Traffic Lights Default Setup

table 17, (75, 150, 225)                  'Left, Centre, Right servo position for Lane 1
table 20, (75, 150, 225)                  'Left, Centre, Right servo position for Lane 1
table 23, (75, 150, 225)                  'Left, Centre, Right servo position for Lane 1
table 26, (75, 150, 225)                  'Left, Centre, Right servo position for Lane 1

'Load the required ASCII table values 32 to 127 into scratchpad and go to slot0
  put 512, 0x00, 0x00, 0x00, 0x00, 0x00    '032    Space
  put 517, 0x00, 0x00, 0x5F, 0x00, 0x00    '033    !
  put 522, 0x00, 0x07, 0x00, 0x07, 0x00    '034    "
  put 527, 0x14, 0x7F, 0x14, 0x7F, 0x14     '035   #
  put 532, 0x24, 0x2A, 0x7F, 0x2A, 0x12    '036    $
  put 537, 0x23, 0x13, 0x08, 0x64, 0x62    '037    %
  put 542, 0x36, 0x49, 0x56, 0x20, 0x50    '038    &
  put 547, 0x00, 0x08, 0x07, 0x03, 0x00    '039    '
  put 552, 0x00, 0x1C, 0x22, 0x41, 0x00    '040    (
  put 557, 0x00, 0x41, 0x22, 0x1C, 0x00    '041    )
  put 562, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A    '042    *
  put 567, 0x08, 0x08, 0x3E, 0x08, 0x08    '043    +
  put 572, 0x00, 0x80, 0x70, 0x30, 0x00    '044    ,
  put 577, 0x08, 0x08, 0x08, 0x08, 0x08    '045    -
  put 582, 0x00, 0x00, 0x60, 0x60, 0x00    '046    .
  put 587, 0x20, 0x10, 0x08, 0x04, 0x02    '047    /
  put 592, 0x3E, 0x51, 0x49, 0x45, 0x3E    '048    0
  put 597, 0x00, 0x42, 0x7F, 0x40, 0x00    '049    1
  put 602, 0x72, 0x49, 0x49, 0x49, 0x46    '050    2
  put 607, 0x21, 0x41, 0x49, 0x4D, 0x33    '051    3
  put 612, 0x18, 0x14, 0x12, 0x7F, 0x10    '052    4
  put 617, 0x27, 0x45, 0x45, 0x45, 0x39    '053    5
  put 622, 0x3C, 0x4A, 0x49, 0x49, 0x31    '054    6
  put 627, 0x41, 0x21, 0x11, 0x09, 0x07    '055    7
  put 632, 0x36, 0x49, 0x49, 0x49, 0x36    '056    8
  put 637, 0x46, 0x49, 0x49, 0x29, 0x1E    '057    9
  ;put 642
  ;put 647
  ;put 652
  ;put 657
  ;put 662
  put 667, 0x02, 0x01, 0x59, 0x09, 0x06    '063    ?
  ;put 672
  put 677, 0x7C, 0x12, 0x11, 0x12, 0x7C    '065    A
  put 682, 0x7F, 0x49, 0x49, 0x49, 0x36    '066    B
  put 687, 0x3E, 0x41, 0x41, 0x41, 0x22    '067    C
  put 692, 0x7F, 0x41, 0x41, 0x41, 0x3E    '068    D
  put 697, 0x7F, 0x49, 0x49, 0x49, 0x41    '069    E
  put 702, 0x7F, 0x09, 0x09, 0x09, 0x01    '070    F
  put 707, 0x3E, 0x41, 0x41, 0x51, 0x73    '071    G
  put 712, 0x7F, 0x08, 0x08, 0x08, 0x7F    '072    H
  put 717, 0x00, 0x41, 0x7F, 0x41, 0x00    '073    I
  put 722, 0x20, 0x40, 0x41, 0x3F, 0x01    '074    J
  put 727, 0x7F, 0x08, 0x14, 0x22, 0x41    '075    K
  put 732, 0x7F, 0x40, 0x40, 0x40, 0x40    '076    L
  put 737, 0x7F, 0x02, 0x1C, 0x02, 0x7F    '077    M
  put 742, 0x7F, 0x04, 0x08, 0x10, 0x7F    '078    N
  put 747, 0x3E, 0x41, 0x41, 0x41, 0x3E    '079    O
  put 752, 0x7F, 0x09, 0x09, 0x09, 0x06    '080    P
  put 757, 0x3E, 0x41, 0x51, 0x21, 0x5E    '081    Q
  put 762, 0x7F, 0x09, 0x19, 0x29, 0x46    '082    R
  put 767, 0x26, 0x49, 0x49, 0x49, 0x32    '083    S
  put 772, 0x03, 0x01, 0x7F, 0x01, 0x03    '084    T
  put 777, 0x3F, 0x40, 0x40, 0x40, 0x3F    '085    U
  put 782, 0x1F, 0x20, 0x40, 0x20, 0x1F    '086    V
  put 787, 0x3F, 0x40, 0x38, 0x40, 0x3F    '087    W
  put 792, 0x63, 0x14, 0x08, 0x14, 0x63    '088    X
  put 797, 0x03, 0x04, 0x78, 0x04, 0x03    '089    Y
  put 802, 0x61, 0x59, 0x49, 0x4D, 0x43    '090    Z
  ;put 805
  ;put 812
  ;put 817
  ;put 822
  ;put 827
  ;put 832
  put 837, 0x20, 0x54, 0x54, 0x78, 0x40    '097    a
  put 842, 0x7F, 0x28, 0x44, 0x44, 0x38    '098    b
  put 847, 0x38, 0x44, 0x44, 0x44, 0x28    '099    c
  put 852, 0x38, 0x44, 0x44, 0x28, 0x7F    '100    d
  put 857, 0x38, 0x54, 0x54, 0x54, 0x18    '101    e
  put 862, 0x00, 0x08, 0x7E, 0x09, 0x02    '102    f
  put 867, 0x18, 0xA4, 0xA4, 0x9C, 0x78    '103    g
  put 872, 0x7F, 0x08, 0x04, 0x04, 0x78    '104    h
  put 877, 0x00, 0x44, 0x7D, 0x40, 0x00    '105    i
  put 882, 0x20, 0x40, 0x40, 0x3D, 0x00    '106    j
  put 887, 0x7F, 0x10, 0x28, 0x44, 0x00    '107    k
  put 892, 0x00, 0x41, 0x7F, 0x40, 0x00    '108    l
  put 897, 0x7C, 0x04, 0x78, 0x04, 0x78    '109    m
  put 902, 0x7C, 0x08, 0x04, 0x04, 0x78    '110    n
  put 907, 0x38, 0x44, 0x44, 0x44, 0x38    '111    o
  put 912, 0xFC, 0x18, 0x24, 0x24, 0x18    '112    p
  put 917, 0x18, 0x24, 0x24, 0x18, 0xFC    '113    q
  put 922, 0x7C, 0x08, 0x04, 0x04, 0x08    '114    r
  put 927, 0x48, 0x54, 0x54, 0x54, 0x24    '115    s
  put 932, 0x04, 0x04, 0x3F, 0x44, 0x24    '116    t
  put 937, 0x3C, 0x40, 0x40, 0x20, 0x7C    '117    u
  put 942, 0x1C, 0x20, 0x40, 0x20, 0x1C    '118    v
  put 947, 0x3C, 0x40, 0x30, 0x40, 0x3C    '119    w
  put 952, 0x44, 0x28, 0x10, 0x28, 0x44    '120    x
  put 957, 0x4C, 0x90, 0x90, 0x90, 0x7C    '121    y
  put 962, 0x44, 0x64, 0x54, 0x4C, 0x44    '122    z
  ;put 967
  ;put 972
  ;put 977
  ;put 982
  ;put 987
  DataLoadedFlag = 1     'set a flag for the main program to know not to loop back here
  Run 0                           'return to the main program after loading the registers
 
Top