Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

GLCD MPASM Character Set Lookup Table(s)

Status
Not open for further replies.

jpanhalt

Well-Known Member
Most Helpful Member
MCU= 16F1519
GLCD = NHD-12864MZ (KS0108B based)

That display does not have a built-in character set. Since each ascii character is 5 to 8 bytes, the problem is how to address such a large table.

Peter Anderson described one method here: https://www.phanderson.com/PIC/16C84/mult_string.html

That method was used by a student for his thesis at the University of Cincinatti for creating a character set for that particular display (**broken link removed**), which I have gotten to run.

I am wondering about other alternatives, particulary ones that might use the enhanced mid-range PIC's capabilities.

One thought was to have each column of the ascii set in a separate table -- the offsets could be the ascii numbers. Another was to have a separate table for each ascii character and use bra. My goal is to retain the simplicity of the ascii set numbering for lookup and manual modification (if needed) and avoid having to do a more or less tedious calculation of the offset for each column of each character.

Has anyone compared the speed of the potential solutions to addressing that issue?

Related to this question of ascii characters, assuming a 5x7 character, is there any standardized practice related to putting a space above or below and before or after the character. My inclination is to put a one-pixel space above and before the character.

Regards,

John

BTW, I am spending more and more time in an Internet-free zone (aka, "The Farm") and find myself only going to McDonald's for e-mail once a day and civilization (Cleveland) twice a week. As such, my responses may be delayed.
 
Yep a 1 pixel space around the 5x7 character is all you need, and is standard.

Regarding the font lookup table, the 12864 GLCD is written in vertical bytes (8 vert pixels = 1 byte) so each ascii character only needs 5 bytes. And you only need 96 chars for a "full" ascii font (ascii 32-127). Sp total font storage is 96*5 = 480 bytes.

There should be tons of font files available on the net in people's source code for 128x64 GLCD projects.

As for calculating offsets etc, this sounds like you are working in Assembler? Just put the 480 bytes of the font into a RETLW data table, then you access the first byte of a char as (char -32)*5. (because the first char in the table is SPACE, ie char 32).
 
I don't think I actually understand the question... what's wrong with using a standard lookup table?

If as Mr RB said, the data is written to the GLCD vertically, then only 7 bits of column data need be written at a time. The pic16F1519 has 14 bit wide instructions, and can read these directly using the PMADRx and PMDATx registers; the 14 bit word can contain two columns of data. To get the address of the data, simple use something like:

dataOffset = ASCII * 5 + col;
PMADR = tableStartAddress + dataOffset / 2;
columnData = dataOffset & 1 ? (PMDATA >> 7) : (PMDATAL & 0x7F);

where ASCII is the ASCII code of the character and col is the column (in {0,1,2,3,4}) of the character to read.

Reading the data in this way uses only half the space required for the equivalent RETLW type table.
 
Are you using "C" John!! .... Most libraries on the web have a font blitting function.... I keep my lookup tables in rom space ( as do others )

If you are using asm then you'll find a couple of examples on this forum to do this.... In fact I'm sure Roman has pasted code for this!!!

The other alternative is to store the font and screen dumps to SPI or I2C memory...
 
Last edited:
Hi,

You might find this of interest.
Its a working example for a pic18F / KS0108, written in Assembly code by another forum member and me.
 

Attachments

  • ks108.zip
    25 KB · Views: 272
From McDonalds, thank you all for the replies.

Yes, I am writing in Assembly. I should have been more explicit in stating that.

As for calculating the offset as 5*ascii (or 5*(ascii- .32)), that was one option I considered. The multiplier could be adjusted to make a space. But, is there a common practice of where to put the space(s) -- I don't see that one would need it both before and after each character.

Doing a look-up table, regardless of size, is not the problem. An ascii character, such as 0x20 could be set at the start and the next bytes for that character ("n") easily read. However, the offset for the next character is not 0x21, but 0x20+n+1 and so forth. There is no way that I know of to offset to rows in the look-up table without additional calculations. That is effectively what the Anderson double-table look-up does. And, of course, it works. If there is a way to directly access rows (i.e., by an increment of 1), please let me know.

All of these calculations for each character affect speed. My simplified question was whether given the increased capability of the enhanced mid-range chips whether there is a faster way to access ascii character bit maps. I may just try having a lot of one-character tables and see how sped is affected compared to a double-look-up or calculated jump.

John
 
...
As for calculating the offset as 5*ascii (or 5*(ascii- .32)), that was one option I considered. The multiplier could be adjusted to make a space. But, is there a common practice of where to put the space(s) -- I don't see that one would need it both before and after each character.
...
An ascii character, such as 0x20 could be set at the start and the next bytes for that character ("n") easily read. However, the offset for the next character is not 0x21, but 0x20+n+1 and so forth. There is no way that I know of to offset to rows in the look-up table without additional calculations.
...

I think i see where the problem is now!

You need two separate systems. The font data (pixels for each char) is kept in a storage array with 5 bytes for every character, as discussed. The font data always represents all 96 ascii characters (32-127).

Displaying however is a different process. You make a function display_character, that has these 3 variables; x, y, char

x and y are the screen pixel coords, ie where to display the char on screen. char gives the offset to where to get the font pixel data from. Then you address the GLCD to the x, y memory location, and then get the first vert row of pixels (1 byte) from the font table, then write that byte to the GLCD. Then increment x, to get the next vert row onscreen, and inc char offset to get the next pixel data. After writing the 5 vert pixel rows, you write one BLANK vert row, that is the space between onscreen characters.

So the entire purpose of that function is to write one char at a location onscreen of your choosing.

Then to write TEXT (a word or phrase), you make a function display_text etc, where you write the first desired character, then just increment x by 6, then write the next character. In assembler you can even skip that function and just manually write one character after another if you like, especially for fixed text.
 
Thanks Roman,

Yes, the position will be defined. I am using a lot of Pommie's routines, but every other GLCD routine I have seen allows for that function.

Yesterday I got the char bitmap look-up "by column" using tables to work (i.e., the first pixel column for each ascii char is in Table1 and so forth). The ascii value is the offset. Indirect addressing allows me to rotate through the 5 tables, but it can also be done by brute force. It was a PITA to troubleshoot character bitmap problems, as you might expect. The bitmap I copied and used gave lousy looking characters. I need another set of bitmaps.

I'll be pretty busy the next day or two installing a storm culvert at my place, so I may not get back to this until Wednesday or so, unless it rains. It is rare that I wish for rain, but this is one occasion. :)

As for the third method mentioned above (each char has its own table), that is very similar to what Peter Anderson did (see link above) with his double table. But today, with the enhanced abilities of the 16F1519, it may be simplified (I am currently planning on just using indirect addressing). That is what I will be working on when I get time. Failing that, I will go back to the double-table method, which is also similar to what you describe in that one table is used to get the offsets for the bit maps of each character that are in another table. At least, the 16F1519 makes larger tables easier. The Brw instruction also helps.



John
 
... Yesterday I got the char bitmap look-up "by column" using tables to work (i.e., the first pixel column for each ascii char is in Table1 and so forth). The ascii value is the offset. Indirect addressing allows me to rotate through the 5 tables, but it can also be done by brute force.
...

I think you might still be making life a bit difficult for yourself?

Why use 5 tables? Just use one table, with 5 bytes for each character.

So char 32 starts at 0, and has 5 bytes
char 33 starts at 5, and has 5 bytes etc.

Total table size is 96 chars * 5 = 480 bytes.

Then your indirect addressing is easy, you just make a single indirect addressing pointer to the first byte of the desired character, then read out 5 bytes in turn.
 
Having done more thought, reading, and practice, I have decided to use one table for each character. Those tables will be accessed using a second table (i.e., Peter Anderson's method) or by indirect addressing, which would take advantage of the 16F1519's enhanced ability in that area.

Anderson's method is really slick, at least it seemed that way to me once I was able to see it in operate in simulation. With a normal "watch" window in MPSim, it was not entirely clear what was happening, as the cursor didn't move (it blinked) while accessing the table that called each character's table. However, when the program was disassembled, it was clear what was happening. With appropriately placed markers, you could see it get built. One neat thing was that different character widths are automatically accommodated by the assembler. That is, I didn't need to do any multiplications to establish the offset for calling each character's table. The assembler did it based on the number of bytes used in that character's definition.

It stopped raining, so back to the culvert problem. May be a day or two before any more progress. Will post a finished snippet later.

John
 
Update -- Character Table for GLCD

MCU = 16F1519
Display = NHD-12864MZ
Language = MPASM (Assembly)
Culvert = 30" X 40'

1) New culvert was installed Saturday. Wow, what a difference it made. The 20-year-old culvert was 18" diameter, rusted out, and bent. Most of the drainage flowed under it, and heavy rain always caused a flood across my driveway. We had a real downpour Sunday after it was installed, and the water level up-stream barely rose. Finished inlet_0590.jpg

2) Progress on the ASCII character program has been made. As mentioned earlier, I tried doing the whole character map by columns, i.e., eight look-up tables of 94 bytes. That was easy and worked, but was a pain to maintain. Also got a high/low table program to work, but it was about the same as the original double-table look-up from the U. Cincinnati dissertation. Actually, it was a little slower, but I didn't put much effort into it.

My most recent attempt used indirect addressing and some of the newer instructions of the 16F1519 chip. It seems to work OK and is a little faster (data below).

I have attached my working files that have only been minimally cleaned up. The first (062013) is basically Ganti's program; (061913) "FSR" is the indirect addressing version. Please note:

1) These programs still use the macro set (LCD.inc) from the Ganti's dissertation. Be sure it is properly loaded. You will need to change its extension from .asm to .inc.
2) I also deleted his sections for checking LCD busy status (same function as Pommie's WaitNotBusy), because waiting for the busy flag to clear was a PITA in MPLab SIM debug mode.:D By slowing the MCU to 4 MHz, that routine was not necessary.
3) For debugging and timing, you need to comment out lines 161 and 162 (right after "Start") that test for oscillator stability, and uncomment line 196, "goto Stay_ON" (at LCD_Power). That instruction bypasses the on/off switch logic. Of course, to run the program, you need to reverse those changes.
4) If someone wants the schematic, I can post it once I get back to my PC in Cleveland. I presume no one would actually want to set it up exactly the same.
5) There is a lot of garbage before you get to the start of the program, "Mainloop."
6) The actual meat of the program(s) begin in the section, "Tables and Data," at, "Out_Str." That is where the printing of an ASCII value to the LCD begins.

Timing Results:

I set a breakpoint at the beginning of the look-up (i.e,. first instruction in Out_Str) and measured the cycle time beginning at the breakpoint and ending at the return. The double-table method, as expected, gave slightly different times depending on the ascii character. Low asciival characters were just a few cycles faster. Second-table (page6) characters (i.e., most of the alphabet) took 142uS. The indirect addressing method was 113uS and was invariant with asciival. The simulation was done at 4 MHz, so uS translates to cycles.


Questions:

1) This is my first attempt at adapting a typical look-up table to indirect addressing. Please contribute freely any suggestions for improvements.

2) I am a bit unsure of which Assembler directive is most appropriate for putting the data into program memory. It seemed that DT and DTM were not needed in the indf method; although, I didn't try either. DB does not work, but DA, DATA, and DW all appeared to work the same. What criteria, if any, should be used for chosing the best directive for one-byte data? I wanted to avoid packing, as I assumed the unpacking would add time.

Regards,

John

View attachment LCD_code_raw_062013_cleaned.asm
View attachment LCD_code_raw_061913_FSR_clean.asm
View attachment LCD.asm
 
Last edited:
I'm just going to add if I was doing assembly, C or BASIC on a PIC I'd avoid the 16F series in favor of the 18F (which has a table instruction set and far less bank switching, LAT instruction etc...) The 18F4620 might be pin compatible with the 16F1519.

I'm an PIC assembly programmer, enjoyed the awesome Swordfish BASIC and currently learning XC8. But I've little reason to use 16F PICs anymore.
 
On a program I recently completed I stored data in flash using DW. As your data is 7 bits long you could store 2 bytes in each flash location. To access the data use the base address times 2 and then add 5 times the index in the table. In your read flash routine you check the lowest bit and return the lower 7 bits if set otherwise the upper 7. To store 1,2,3,4,5,6 in flash you would do,
Code:
        DW      1 | 2<<7, 3 | 4<<7, 5 | 6<<7

It's not very readable but very efficient on memory.

If you're not worried about memory then just store 1 value per byte.

Edit, Should have looked at your code first, I see you are using DW and the new instructions. You can of course do the same but use the read flash facility.

Mike.
 
Thank you Bill and Mike for the constructive comments.

Much of the documentation for the 16F1519 includes comments for the 18F series (4620, as I seem to remember). Maybe that is a hint from Microchip. I did a little Basic many years ago on a TI99-4 and a very early IBM PC. It was fun, but on retreading, I got into Assembly. I find that language easy to use for a few weeks during a project, and the instruction set seems seems to stay with me. When I come back after a long absence, it doesn't take long to get back up. Like the 18F series, it is clear that even Microchip seems to be moving to C, and I may well move with it.

@Pommie, I am beginning to learn more of those Assembly directives. Thanks for the suggestions. I didn't mention that the "raw" in the file names meant that the GLCD commands were taken pretty much straight from Ganti's dissertation. The "raw" will disappear when I put everything together using some of the GLCD routines you kindly provided.

Compared to the 12F509 and 12F683 I have been using, the program memory space in the 16F1519 seems more than adequate for the simple stuff I may write in the near future. That is one reason I put just value per byte. The other reason is that I am actually thinking of using a different design for many of the characters than Ganti's designs. Some characters may be a full 8 columns wide (e.g., a special character or an em dash), while others will be narrower. So, I will probably stick with 8 bytes per character for awhile at least.

Regards,

John
 
I've programmed a handful of PICs from the ancient 16C54, 16F84, 12F509, 16F877, 16F887 to the advanced 18F series. I've even dabbled in the 24 but those are a different beast.

Moving from 16 to 18 is a no brainer, mostly the same core instructions plus plenty of really useful "advanced" instructions and a multiply instruction (that makes for a handy left shift).

I still have a fabulous pair Tech-Tools ICE's for those 16 series, I really should sell them. An ICE is an awesome tool for assembly debugging.
 
Took a brief look at the 18F4620 datasheet. I will probably add a couple to my next DigiKey order. Having the extra 16-bit timers could come in handy for reading the 100 Hz pwm from my Memsic accelerometer.

What worries me sometimes is spec creep. ;) Each step is an improvement, but also delays getting to the finish line. Fortunately, I think most of what I have written or will write for the 16F1519 at 8 MHz will transfer easily to the 18F with the same internal oscillator frequency. I am nowhere near pushing the capabilities of either chip.

John
 
The 4620 is an excellent choice. It's the recommend MPU for Swordfish BASIC (I'd bet with the free version of Swordfish you could rewrite your code in a few days).

I'm sure you'll never look back. Code will be smaller and you'll mostly have to hunt down all the bank switching and delete it. (the 18F still has banks, just not nearly as many). You might even be able to use the capture module from you 100Hz PWM source.

I'm currently working with the 18F46K22 (dual EUSARTs) but it's got so many features they bank switch a handful of SFRs.
 
Update and Correction

The code posted in #12 worked for the subroutine to print the ascii table, because it was sequential. There is a flaw in it, however. I forgot how to multiply -- but then, who can remember such details after 60 years. ;)

Code:
;*******************************************************************************
;                 ASCII CHARACTER LOOK-UP TABLE AND SUBROUTINE
;*******************************************************************************
;Enhanced indirect addressing capabilities of this chip are utilized    
;(See: Memory Organization and Section 3.1.1.2 of the datasheet).  Bit <7> of 
;FSR0H is a directive to access program memory as data.  Location of the table 
;in this instance is set by bit <5> of FSR0H to give 0x2000.  Actual table data
;are offset by 8*(0x20) to account for the ascii offset.
;*******************************************************************************
Out_Str                       ;10(9), one-cycle instructions
     clrf      FSR0H          ;an rlf after first lslf not needed as largest 
     movwf     FSR0L          ;Str_Num is <128(0x80)
     lslf      FSR0L
;    rlf       FSR0H          ;not needed 
     lslf      FSR0L
     rlf       FSR0H
     lslf      FSR0L
     rlf       FSR0H
     bsf       FSR0H,5
     bsf       FSR0H,7    
Out_Str_Loop
     movlw     0x08           ;NB:for characters <8 bytes, first byte will set    
     movwf     Byte_Cnt       ;Byte_Cnt_Loop will need appropriate modification   
Loop	
     moviw     FSR0++         ;movfw INDF0, post increment
;    movwf     CharByte       ;not needed this version 07.02.13
     call      WriteData
     decfsz    Byte_Cnt,f
     goto      Loop
     return
;*******************************************************************************
;                        ASCII CHARACTER DATA
;*******************************************************************************	
;Character set from Microchip forum link "5X8 Character Table Used in Most
;of the Graphic LCD" (top row of pixels are blank for line spacing). 
;No descenders for l.c., added degree sign (ascii = 7f)
;*******************************************************************************
     ORG 0x2100
     DW 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; 20 space
     DW 0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x00 ; 21 !
(etc.)

The ascii character value is passed to this routine in w. WriteData is from Pommie with a few mods. I also changed the character set bit mapping to a more common design. Characters are now only 5x7.

When I get Internet at my new location, I will post the complete code to my blog. In the meantime, suffice it to say that the enhanced INDF/FSR speeds up the find and return process for big tables quite a bit compared to using multiple tables and paging.

John
 
Update#2

Just thought I would add a screen shot of the GLCD taken with mock accelerometer data (see: my blog, Memsic).

both_sm.png

The "ON" for OFFSET blinks. One snapshot missed it. I made it blink, as having a few degrees of offset and not realizing it, could create problems. The wiggle line is an icon with 4 positions, l evel, +/- 1, +/- 2, and +/- 3 as a graphic indicator of tilt. I may eliminate it in the final version.

Right now, I am looking for bitmaps for double-size numbers, i.e., 10X16, that will be easier to read. Anybody have that or know of a good link? Presumably, the lines will be two-pixels or more wide.

John
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top