Large characters on 128 x 64 OLED

I'm fairly new to Picaxe and would welcome a steer as to whether I'm overlooking something obvious.

23264

I bought one of these small 128 x 64 OLEDs (in this case from Amazon but they also have them on eBay). It worked more or less out of the box with the code for small 5x7 characters from post #7 off this thread: https://picaxeforum.co.uk/threads/i2c-oled-display.27651/. It's a very sharp and responsive display.

I've succeeded in creating double size characters, based on the method in this thread: https://picaxeforum.co.uk/threads/ssd1306-oled-128x32-test-code.31467/

I used a different font, based on the design here: https://fontstruct.com/fontstructions/show/243719/command_prompt_12x16 . I hope that I haven't overlooked a simple way of converting this to the SSD1306 format. I found that the easiest way was to map out each character in a blank table in a Word document (like below) before identifying the correct binary/hex code (this conversion table was useful: https://ascii.cl/conversion.htm). It's very time-consuming.

23265

The font needs a few tweaks - for example "g" goes below the line and will need some adjustment, but as you can see from the photo of the display, the font is very clear on the small display.

So here's my code. At the moment it's just intended to load a starting figure onto the display, then subtract one for each press of a button.
(Omitting the OLED set-up instructions...)

Code:
gosub ClearDisplay
    
WriteLoad:     'writes "Load:" on the OLED, double height.  Each character fits within 4 6-column by 8-row blocks, resulting in a character     12 wide and 16 high.  Of these the 2 right hand rows and 2 top columns are blank to provide character and row spacing.

' "L" as 12 x 16 
'top row
row = 0 : col = 0 ' rows are 0 to 7 top to bottom (each row 8 pixels high); columns are 0 to 127. SetPosition at multiples of 12 - 0, 12, 24, etc  
gosub SetPosition
hi2cout (0x40, 0xFC, 0xFC, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) '0x40 is the command to receive data, then follow 12 hex characters which equate to 12 bytes, with each binary byte representing a column of 8 bits, 1s and/or 0s, with the 1s representing lit pixels.  The bytes run from the bottom (MSB) to the top (LSB), so 0xFC represents 11111100 to give 6 lit pixels in the top half of the vertical leg of the L, with two spaces at the top.  The bottom half of the vertical leg of the L is coded in the second row below, in which 0xFF represents a full column of 11111111
 
'2nd row
row = 1 : col = 0
gosub SetPosition
hi2cout (0x40, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0,0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00)

'"o" as 12 x 16

row = 0 : col = 12
gosub SetPosition
hi2cout (0x40, 0x00, 0x00, 0x80, 0x80,  0x80, 0x80, 0x80, 0x80,0x00, 0x00, 0x00, 0x00)
 
row = 1 : col = 12
gosub SetPosition
hi2cout (0x40, 0x3E, 0x7F, 0xE3, 0xC1, 0xC1, 0xC1, 0xC1, 0xE3, 0x7F, 0x3E, 0x00, 0x00)
and then the same principle for "a", "d" and ":"

Now the counter:

Code:
Let b8 = 18 'sets the counter to 18, which is the starting number

let b9 = b8 ' allows us to check whether there has been any change in b8

goto CountAndDisplay

CheckCount:
If pinC.1 = 1 then 'C.1 in this instance is a push button
    pause 75 ' debounce
    let b8 = b8 - 1 'decrease the value of b8
else goto CheckCount 'keep looping until there is a button press
endif

if b8 <> b9 then goto CountAndDisplay ' not sure that this is necessary - if b8 = b9 then b8 won't have decreased and CheckCount will still be looping

CountAndDisplay:
    
      
     select case b8
        case 0 'if b8 = zero, then go to the subroutines to display "0".  There must be a simpler way than this.
            row = 0 : col = 60
            gosub SetPosition
            gosub DisplayTop0
            row = 1 : col = 60
            gosub SetPosition
            gosub DisplayBottom0
            row = 0 : col = 72 'need to add a space to single digit figures because otherwise as the figures count down below 10, the second digit (0) from the 10 remains undeleted.  So load a blank space in order to suppress the second digit from earlier numbers.  Could otherwise selectively clear that character if I could work out how to do it
            gosub SetPosition
            gosub DisplayTopSpace
            row = 1 : col = 72
            gosub SetPosition
            gosub DisplayBottomSpace
            
        case 1 'if b8 = 1, then go to the subroutines to display "1"
            row = 0 : col = 60
            gosub SetPosition
            gosub DisplayTop1
            row = 1 : col = 60
            gosub SetPosition
            gosub DisplayBottom1
            row = 0 : col = 72
            gosub SetPosition
            gosub DisplayTopSpace
            row = 1 : col = 72
            gosub SetPosition
            gosub DisplayBottomSpace
and so on for cases "2" to "18"

Then

Code:
        case 10 'if b8 = 10, then go to the subroutines to display "1"followed by "0"
            row = 0 : col = 60
            gosub SetPosition
            gosub DisplayTop1
            row = 1 : col = 60
            gosub SetPosition
            gosub DisplayBottom1
            row = 0 : col = 72
            gosub SetPosition
            gosub DisplayTop0
            row = 1 : col = 72
            gosub SetPosition
            gosub DisplayBottom0
and so on for the remaining digits

Then

Code:
EndSelect
dec b9 'decrease b9 by 1 so that when the code loops back to the start of CheckCount and compares the counter b8 with b9, the counter displays a lower figure only if b8 changes  
goto CheckCount

end

 
    'Characters are set out here as subroutines - 2 subroutines (top and bottom) per character.  There need to be two separate subroutines because each half of the character is posted to a different row address.  Unless it is possible to address/create a double height column in the OLED setup menu
    
    '"space" as 12 x 16

DisplayTopSpace:
hi2cout (0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
return
 
DisplayBottomSpace:
hi2cout (0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
return

'"0" as 12 x 16

DisplayTop0:
hi2cout (0x40, 0xE0, 0xF8, 0x18, 0x0C, 0x0C, 0x8C, 0xCC, 0x7C, 0xF8, 0xE0, 0x00, 0x00)
return
 
DisplayBottom0:
hi2cout (0x40, 0x1F, 0x7F, 0x7C, 0xCE, 0xC3, 0xC1, 0xC0, 0x60, 0x7F, 0x1F, 0x00, 0x00)
return

'"1" as 12 x 16 - top and bottom halves

DisplayTop1: 'displays the upper 12 columns of the digit "1"
hi2cout (0x40, 0x30, 0x30, 0x38, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
return

DisplayBottom1: 'displays the lower 12 columns of the digit "1"
hi2cout (0x40, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00)
return
This is a very thirsty approach, and 0 to 9 characters and the select... code take up just under 2000 bytes. There are a number of threads that show how to put the characters into memory and then send them to the display, eg. post #10 of this one: https://picaxeforum.co.uk/threads/ssd1306-oled-screen-help.30783/ . I have to confess that I don't really understand how this bit works:

Code:
b1 = 0 : do : LookUp b1, ( "HELLO WORLD",0 ), b0 
    if b0 <> 0 then : Gosub ShowChar : endif : inc b1
  loop until b0 = 0

  do : loop
  end

ShowChar:
' sertxd(b0)
  if b0 >= "A" and b0 <= "Z" then
      bptr = b0 - "A" * 5 + A_addr ' point to 5 unique char bytes in upper ram
      hi2cout (0x40, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, 0)
    endif
  return
If I understand correctly, it puts a long string of hex codes for all characters sequentially into the memory, and then retrieves the 5 that code for a particular character, and sends them to the display, but I don't understand how it identifies which 5 relate to a particular character. Presumably if I created or found a similar string for all of the double height characters, I could do something similar - with two times 0x00 added in each case to provide the spacing. But I would somehow need to do that twice for each character - one for the top row and one for the bottom. Would I need to have two separate strings in the memory, one top half and one bottom half? And how would I send them to the right place in the display? The threads I mentioned seem to rely on the OLED advancing one character space automatically for each new character, but with the double size characters I would need to set the address manually each time. Or could I somehow set the column and row by reference to the previous, so the next character is [column +12] and the bottom row of the character is [top row + 8]?

All thoughts very gratefully received.

I've posted the entire code here in case the work I've done on the characters is useful to anyone.
 

Attachments

lbenson

Senior Member
I don't understand how it identifies which 5 relate to a particular character
For that example, in which upper ram is used to store the character pixel map (as opposed to the later versions which use eeprom), this is the line which does the work:

bptr = b0 - "A" * 5 + A_addr ' point to 5 unique char bytes in upper ram

"bptr" will point to the pixel map for the character which is contained in b0. The pixel map for all letters begins at the address in "A_addr". Since only upper case letters A-Z are included in this example, b0 - "A" * 5 will give the offset into pixel map. For instance, if the character is "C", then "C" minus "A" will give 2 (look at http://www.asciitable.com--67-65=2). Multiply that by the number of bytes per character, 5, and you get an offset of 10. 10 bytes into the pixel map beginning at A_addr will give you the first byte (of 5) of the pixel map for the character "C". @bptr will point the the first character of the "C" pixel map.

Hope this helps explain it.
 
Last edited:

lbenson

Senior Member
In your particular case, if you have double-height letters which take 10 characters, it might be easiest to write 2 lines like this:

Code:
  symbol offset=b2

  offset=0
  b1 = 0 : do : LookUp b1, ( "HELLO WORLD",0 ), b0 
    if b0 <> 0 then : Gosub ShowChar : endif : inc b1
  loop until b0 = 0
' do what is needed to move to the next line
  ...
  offset=5
  b1 = 0 : do : LookUp b1, ( "HELLO WORLD",0 ), b0 
    if b0 <> 0 then : Gosub ShowChar : endif : inc b1
  loop until b0 = 0

  do : loop
  end

ShowChar:
' sertxd(b0)
  if b0 >= "A" and b0 <= "Z" then
      bptr = b0 - "A" * 5 + A_addr + offset ' point to 5 unique char bytes in upper ram
      hi2cout (0x40, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, 0)
    endif
  return
The change is adding the variable, "offset". This will be either 0 or 5 to point to the upper 5 pixel rows or the lower 5. In the code "..." must be replaced by code which moves the cursor to the next line.

There may well be better ways to do this.
 

Hemi345

Senior Member
I created the attached Excel file to help build my double size fonts. You're right, it's very time consuming. I did all mine by hand, I should have taken your approach and found a preexisting font and copied it. I got 3/4 of the way through building mine then didn't get back to it a couple weeks later and had lost my rhythm so the last bunch don't match well. :LOL: Anyway, with the Excel file, just change the 0 to 1 and vice-versa to design the font and then the excel sheet builds the hex value. Down at the bottom, it concatenates '0x' to the hex values for building a nice string for programming the EEPROM.

Here's some code that I initially programmed the PICAXE to write my characters to the external EEPROM (being careful to not write more than the EEPROM's 128byte page boundry/buffer):
Code:
#picaxe 20x2
#no_data
#no_Table
#terminal 38400
setfreq m32
pause 3000
;constants for i2c comms
symbol eeprom_addr = %10100000
symbol loc = w0
loc = 0
hi2csetup i2cmaster, eeprom_addr, i2cfast_32, i2cword ;EEPROM
sertxd ("Writing to eeprom...",13,10)
'*0 @ 0 ('0' - 48 * 20) if '' > 47 and '' < 58
hi2cout loc,(0xF8,0xFC,0xFE,0xE7,0xC7,0x87,0x07,0xFE,0xFC,0xF8,0x1F,0x3F,0x7F,0xE0,0xE1,0xE3,0xE7,0x7F,0x3F,0x1F, _
0x00,0x04,0x06,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00, _
0x0C,0x0E,0x0F,0x07,0x07,0x87,0xC7,0xFF,0xFE,0x3C,0xE0,0xF0,0xF8,0xFC,0xEF,0xE7,0xE3,0xE1,0xE0,0xE0, _
0x0C,0x0E,0x0F,0xC7,0xC7,0xC7,0xC7,0xFF,0xFE,0xFC,0x30,0x70,0xF0,0xE1,0xE1,0xE1,0xE1,0xFF,0x7F,0x3F, _
0xFF,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0xFF, _
0xFF,0xFF,0xFF,0xC7,0xC7,0xC7,0xC7,0xC7,0x87,0x07,0x31,0x71,0xF1,0xE1,0xE1,0xE1,0xE1,0xFF,0x7F,0x3F, _
0xFC,0xFE,0xFF,0xC7,0xC7,0xC7,0xC7,0xCF)                                                                '*6 @ register 120
gosub incLoc
hi2cout loc,(0x8E,0x0C,0x3F,0x7F,0xFF,0xE1,0xE1,0xE1,0xE1,0xFF,0x7F,0x3F, _
0x07,0x07,0x07,0x07,0x07,0x07,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF, _
0x7C,0xFE,0xFF,0xC7,0xC7,0xC7,0xC7,0xFF,0xFE,0x7C,0x3F,0x7F,0xFF,0xE1,0xE1,0xE1,0xE1,0xFF,0x7F,0x3F, _

' ---- snip! ----

0x00,0x00,0x00,0x30,0x78,0x78,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x1E,0x1E,0x0C,0x00,0x00,0x00, _
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00)                                                                '*, @ register 760
gosub incLoc
hi2cout loc,(0x00,0x00,0x00,0x00,0x00,0xC0,0xF0,0xF0,0x30,0x00,0x00,0x00,_
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xF0,0xF0,0x60,0x00,0x00,0x00, _
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00)
sertxd (13,10,"Done!",13,10)
stop
incLoc:
    sertxd("Wrote ",#loc," to ")
    loc = loc + 127
    sertxd(#loc," bytes.",13,10)
    inc loc
    pause 4000
    return
Here's a code snippet of how I pull the characters from the external EEPROM and display them:

Code:
ShowBigChar: ' A-Z, 0-9, space
  sertxd(b0) 'contains the letter to write to the display

  if b0 >= "A" and b0 <= "Z" then
    eeAdr = b0 - "A" * 20 + 200 '    
  elseif b0 >= "0" and b0 <= "9" then
    eeAdr = b0 - "0" * 20 + 0'
  elseif b0 = " " then
    eeAdr = 800
  elseif b0 = ":" then
    eeAdr = 740
  elseif b0 = "?" then
    eeAdr = 720
  elseif b0 = "." then
      eeAdr = 780
  elseif b0 = "," then
        eeAdr = 760
  endif
    bptr = RAM_base ' base of upper ram
      hi2csetup i2cmaster, eeprom_addr, I2CSpeed, i2cword ;EEPROM
   
    hi2cin eeAdr,(@bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc,@bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc,_
        @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc,@bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc) ' copy from eeprom to ram
     bptr = RAM_base ' base of upper ram
    hi2csetup i2cmaster, Display, I2CSpeed, I2cbyte   'Display
    hi2cout (0x40, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, 0) 'write top half of char
    inc row : gosub SetPosition
    hi2cout (0x40, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, 0) 'write bottom half of char
    dec row : col = col + 12 : gosub SetPosition
  return
Hope this helps out.
 

Attachments

Thanks lbenson for that explanation - I think I understand it now - I need to go away and try it out. So in the offset method that you suggest above, the character would be stored as its upper row character bytes immediately followed by its lower row character bytes, and the offset would shift between the two of them for the two halves of the character?
 

lbenson

Senior Member
So in the offset method that you suggest above, the character would be stored as its upper row character bytes immediately followed by its lower row character bytes, and the offset would shift between the two of them for the two halves of the character?
Right. As mentioned, there may be easier ways to do it, but this seems quickest with the code available.
 

westaust55

Moderator
Another aspect that you may wish to consider when using pixel based displays as opposed to character based displays is variable width letters.
I did this long ago when using 128 x 64 colour displays from earlier mobile/cell phones. I had also developed code for:
(a) double width only,
(b) double height only, and
(c) double width and height.
My approach for variable width based an font data saved with constant number of data columns was:
The 1 column spacing between letters was not part of the font and “printed”/moved past as a separately
If when about to print the first column check if the first and second column are both blank and if so skip “print” of the first column
If when “printing” the last/5th column check the prior column and if both are blank then skip the last column.
In this manner, some letters such as upper and lower case I and lower case L could be reduced to 3 columns width instead of 5 on the display.
Some other to 4 columns.
the end result was more consistent “white” space between characters and getting and extra couple of characters per line.
 

Hemi345

Senior Member
Thanks lbenson for that explanation - I think I understand it now - I need to go away and try it out. So in the offset method that you suggest above, the character would be stored as its upper row character bytes immediately followed by its lower row character bytes, and the offset would shift between the two of them for the two halves of the character?
In my code, the offset helps you get the position at the start of the upper row character bytes immediately followed by its lower row character bytes. Since my font is 10 bytes wide and they're double height, each character definition is 20 bytes, so the "20" in the equation is the offset.
I have saved all of the character definitions for "0" to "9" starting at position/register 0 in the external EEPROM. Characters "A" to "Z" start at register 200. For example if I wanted to display the letter "B":
Code:
b0 = "B"  
if b0 >= "A" and b0 <= "Z" then
    eeAdr = b0 - "A" * 20 + 200 '
elseif b0 >= "0" and b0 <= "9" then
    eeAdr = b0 - "0" * 20 + 0'
end if
The decimal for "B" is 66. So to display the letter B, the starting eeAdr will be: 66 - 65 * 20 + 200 = 220

If I wanted to display an "A", the decimal for "A" is 65. So the starting eeAdr will be 65 - 65 * 20 + 200 = 200
For the number "1", it would use the first elseif statement and the starting eeAdr will be 49 - 48 * 20 + 0 = 20
(I just have that + 0 in there to remind me that the numbers start at register 0)

Code:
' set the bptr to the first/base address of upper ram (essentially 'b28' on the 20X2)
bptr = RAM_base 

'reconfig the I2C to work with the external EEPROM chip
hi2csetup i2cmaster, eeprom_addr, I2CSpeed, i2cword 

'read in all 20 bytes for a character starting at register eeAdr of the external EEPROM:
hi2cin eeAdr,(@bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc,@bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc,_
        @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc,@bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc)

'set the bptr back to the first/base address of upper ram so we can sequentially read the values back out for the display
bptr = RAM_base 

'now let's work with the OLED Display
hi2csetup i2cmaster, Display, I2CSpeed, I2cbyte   

'display the upper row of character bytes
hi2cout (0x40, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, 0)

'jump down to the lower row so we can write out the bottom half
inc row : gosub SetPosition
 
'display the lower row of character bytes
hi2cout (0x40, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, 0) 

'now jump back up to the upper row and move the cursor 12 columns so we're ready for the next character
dec row : col = col + 12 : gosub SetPosition
 
Last edited:
Thanks Hemi345, I made some progress with this the other evening and was planning to share my revised code shortly. I put my string of characters into upper ram starting at 28 in order to keep it simple for now. I initially had some chimera characters because I was using an offset of 10 until I realised (as you have explained above) that I needed an offset of 20 to take account of the 2 blocks of 10 top and bottom. I also discovered that I didn’t need to change the offset between rows because the @bptrinc automatically serves the 11th code into the second row. And after some fiddling I had also discovered just incing the row command between the two halves. It’s good to see from your very thorough explanation that I’m on the right track, so thanks again!
 

Hemi345

Senior Member
Excellent! Good idea starting with your char definitions in upper ram to take fiddly EEPROM issues out of the equation. Once you have your code solid, then you can move them to external storage (or PICAXE's internal EEPROM) out so you have more program space :)
 
Thanks to the assistance received so far, I've made good progress. I'll be using a 28x2 for the project, so I moved it onto that. I have put the character definitions into scratchpad from code running in Slot 0. 10 numbers, 26 lower case characters and a handful of upper case characters and symbols take up 3404 out of the 4096 bytes in the slot, so I won't be able to add all upper case characters. Using a separate slot to put the characters gives a lot more space for the actual working code in Slot 1. Thanks to Hemi345 for the Excel character string creator - it was a lot quicker than doing it manually as I had been doing before. The Slot 0 program attached below is the full set of my characters in case it's of any use to anyone.

I've also worked out the way to display the characters, again thanks to useful pointers here ^. First the counter:

Rich (BB code):
Counter: ;sets up the counter with the starting value 32

      Let b8 = 32 'sets the counter to 32
      let b9 = b8 ' allows us to check whether there has been any change in b8

DisplayCountWord: ;displays the word "Count:"

      row = 1
      col = 0
      gosub SetPosition

      for b10 = 0 to 5 ; sequentially looks up each character in the word "Count:" and passes it to b15 which holds it to display
            LookUp b10, ( "Count:"), b15
            Gosub DisplayCharacter
      next b10

DisplayCountNumber: ; displays a number from the counter to follow the word "Count".  Initially I had the same line of code
                        ' displaying the word and the number, but redisplaying the word as well each time the number changed took time
                        'so I split them into two pieces of code, with only the number refreshing.  This was a lot quicker
      row = 1
      col = 72
      gosub SetPosition ; sets the display to print at the position after 7 characters of text.  All SetPosition commands for the start of a
                        ' set of characters are placed in the main code, not in the DisplayCharacter subroutine, so that the characters can be
                        ' placed at the desired location on the screen
      
      let b11 = b8 DIG 0 + "0" ; this splits the 2-digit b8 into two constituent digits for display.  Not sure why the + "0" is necessary,
                              'but it works
      let b12 = b8 DIG 1 + "0"
      let b13 = 0

      Do
      lookup b13, (b12, b11), b15 ;this takes the two digits from Counter and feeds them in succession to b13 which points to the right place
                                    'in ram
      gosub DisplayCharacter
      inc b13
      loop while b13 <= 1     
Then the counter - currently simulated by a button press:

Rich (BB code):
CheckCount:

      If pinC.0 = 1 then 'C.0 in this instance is a push button
            pause 75 ' debounce
            let b8 = b8 - 1 'decrease the value of b8
      else goto CheckCount 'keep looping until there is a button press
      endif

      if b8 <> b9 and b8 > 0 then goto DisplayCountNumber

      if b8 = 0 then
            for b17 = 0 to 5
                  gosub ClearDisplay
                  row = 1
                  col = 0
                  gosub SetPosition
            
                  for b10 = 0 to 7
                        LookUp b10, ("All gone"), b15
                        Gosub DisplayCharacter
                  next b10
                  Pause 500
                  gosub ClearDisplay
                  Pause 500
            next b17
      endif
The subroutine that displays the characters comes next...
 
And now the subroutine that displays the characters.

Rich (BB code):
DisplayCharacter: ; this is a universal piece of code to write all characters.  It doesn't set the starting position for them - that is done by the code that refers to here
 
      if b15 >= "a" and b15 <= "z" then
            ptr = b15 - "a" * 20 + 200 ' for lower case letters, point to the start of the top row char bytes in scratchpad.  Because scratchpad storage addresses for each character run sequentially, there is no need to identify the individual line within the block of letters or numbers
            
            elseif b15 = ":" then ptr = 720 ; some individual special characters and upper case letters are stored separately -
                                            ;not enough space to load all upper case characters ;
                                            ;here we identify lines individually,because these characters are not (yet) stored sequentially
            elseif b15 = "L" then ptr = 740
            elseif b15 = " " then ptr = 760
            elseif b15 = "A" then ptr = 780
            elseif b15 = "C" then ptr = 800
            elseif b15 = "S" then ptr = 820
            elseif b15 >= "0" and b15 <="9" then
            ptr = b15 - "0" * 20 + 0 ' for numbers, point to the start of the top row char bytes in scratchpad
    
      endif

      hi2cout (0x40, @ptrinc, @ptrinc, @ptrinc, @ptrinc, @ptrinc,@ptrinc, @ptrinc, @ptrinc, @ptrinc, @ptrinc, 0x00,0x00) ; this does the top half
      inc row; then move to the lower half.  We add 2 x column space codes (0x00) at the end because the character consists of 10 columns stored in scratchpad plus two space columns which are added here in order to save storage
      gosub SetPosition
      hi2cout (0x40, @ptrinc, @ptrinc, @ptrinc, @ptrinc, @ptrinc,@ptrinc, @ptrinc, @ptrinc, @ptrinc, @ptrinc, 0x00,0x00)
                                    ; this does the lower row
      col = col + 12 ; then move to the next character space, 12 columns further on
      dec row ; go back to the top row, ready to write the top half of the next character
      gosub SetPosition
return
I'm very pleased with it. The numbers load quickly - it counted down from 32 to 0 in about 4 seconds with my finger holding the button down.

Now I need to work on the project itself.
 

Attachments

Top