GLCD Graphing Advice Wanted

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
LCD = 132x64 LCD
Controller = ST7567
Interface = SPI
MCU = PIC 16F enhanced, Fosc = 32 MHz, SPI = Fosc/4

The SPI interface does not allow reading DDRAM of the display. I am trying to graph Temp (Y-axis) v. Time (abscissa). Drawing the screen is relatively easy and plotting points in the Y-axis is not a problem, as the bytes (columns) can simply be offset to avoid messing up that axis and tick marks. The tick marks are at 5-pixel intervals.

The X-axis is a different story. Calculating that axis to determine what needs to be re-drawn can be done, but is messy. The ST7567 has only one controller for the entire screen, and it is very fast (no delays are needed). I would like to keep those advantages.

Here's a draft of what the screen template will look like. Up to three lines of additional data will be added to the right half of the screen. Ignore the unit spacings.

upload_2019-2-22_6-47-53.png

Options I have considered:
1) Get a version that allows reading DDRAM. That will require be a parallel interface for the ST7567.

2) Save an image of the display in program memory and update the whole display. The graph is currently 64x64 pixels. That is 8x64 = 512 bytes. A full write of the screen takes about 3.5 ms. Just half of the screen would be less.

3) Save only the bottom row and re-write that as needed. That will take a few more lines of code, but will be faster that doing option #2.

4) Move the abscissa to the "top" of a row and don't mess with it. That reduces the Y-axis range from about 64 to 56. That would allow a label of the axis.

I am tending toward options #1 or #4. Are there other alternatives? Opinions?

Regards, John
 

ericgibbs

Joined Jan 29, 2010
18,766
hi john,
When using colour TFT version I added some special characters to the AlphaNumeric 'include' file.
You could create a special symbol [or short string] that you call 'n' times to print a full horizontal line, with an X axis scale.
E

 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
Hi Eric,

Thanks for the input. That is basically how I drew the axes. The problem is that the X-axis is in the middle of a vertical byte. If I change one pixel, I have to know what the byte was originally, which I can only do by reading it or by calculating its position on the abscissa. It's not the line per se that is hard to do, it's the tick marks.

After more thought, I think I will just push the line and tick marks to the top of those bytes and add a label ("TIME") made with a special, short font. I am also considering color for version 2, but first I need to get a prototype to my daughters.

John
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
Individual pixels can be identified, for example the tick marks are individual pixels, but I cannot write an individual pixel without writing the whole byte it is in. If I could read a byte, then set a bit and write it back, that would be easy, and is what I did on another GLCD. Calculating what is in a byte gets messy. I looked at the link you gave. I can't read C, but it appears to be a demo program for drawing shapes. It refers to a memory buffer. I am not sure, but that may serve the same purpose as my options 2 and 3. For just one row, it is not that much (about 64 bytes). I could then read, modify (set a pixel), and write from the row that has the X-axis and tick marks.

Although the Y-axis template was the hardest to calculate, from the standpoint of this issue, it is the easiest to address. I just add 2 to whatever column I am writing to and avoid the Y-axis completely (i.e., the X-axis data will be offset by 2). The X-axis can be calculated (mod 5 ) to find the tick marks (or saved in program memory as mentioned above), but I think I will just add a label with a short short font and lose a little spread for the Y-axis.

I did find a 3x5 pixel font (4x6 if spaces included), which will work out fine for the max of 2 pixels used for the axis. We will see how it looks. A follow-up will follow.

John

Edit: Here's a link to the tiny font. Looks pretty good and is open license: https://robey.lag.net/2010/01/23/tiny-monospace-font.html
 

MrChips

Joined Oct 2, 2009
30,708
Do the bytes correspond to x-rows or y-columns?
Why not save an image of the bytes written (for the scale only) and then read/modify/write the bytes.
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
Bytes correspond to columns. A 1-pixel wide column is composed of 8 bytes to give the 64 pixel height. Individual columns can be addressed (it's a 9-bit value so requires a two-command sequence ). That part is easy. Sixty-four bytes L to R make a row. Rows are easily accessed, but "lines" (say one row of pixels across the screen) cannot be accessed/written. One must write the whole byte or 64 bytes in total.

That arrangement is similar to other GLCD's I have (e.g., NHD12864MZ w/ KS0108B controllers). The difference is that with a parallel interface you can read the byte, modify and re-write it. That is effectively the same as being able to access individual pixels top to bottom.
 

MrChips

Joined Oct 2, 2009
30,708
Hence, for your 64 x 64 graph, you need to access 64 bytes across the bottom of the graph.
Do you have 64 bytes of RAM on the PIC to spare and simply rewrite all 64 bytes of the GLCD?
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
Right now, RAM is probably not an issue. I am using a 16F1829 with 1024 bytes linear RAM and 16 K of program memory for this part of the project. For the thermometer part, I used the lowly 16F1783, because I had it and wanted 28 pins. That device is available up to the 16F1789 with 2048 bytes of linear RAM and 16K program flash. I am not suggesting writing to program memory during run time. It would be used for making a template or like Eric suggested as a very special font.

I added a label to the abscissa (nick named tom_thumb by its creator) just to see and modified the font a little, but am still not happy with the asymmetric "T" and fat "m" and "n." It was not too hard to do, since the line and tick marks are the lower nibble and the characters are mostly the high nibble. It was just a matter of writing the characters, then adding them to the bytes -- much as one would do if the display could be read. The "e" that forces the other l.c. characters to be bigger. Also, I did not bother adjusting the Y-axis.

Here's a rough version:
upload_2019-2-22_12-32-26.png


This weekend, I will give a large RAM mirror a try or at least a mirror for the abscissa.

Thanks, John
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
Type of user = 3 wonderful daughters, one son + me. They won't complain about it, but I agree. That font does not look good.

John
 

ericgibbs

Joined Jan 29, 2010
18,766
hi,
Will the TFT be mounted in a bezel surround, if so and if the Xaxis is always Time, perhaps a 'Time label' printed on the bezel may be an option.
The actual scale will be written to the TFT
E
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
A year or so ago, I got a box, buttons, and display from Pax Instruments:
upload_2019-2-22_14-54-14.png

I got none of the electronics, but really liked the compactness and way the screen fit into the box. Everything fit together very nicely. Adafruit sold it for awhile, but it is discontinued and Pax does not seem still to be in that business. Here's the Adafruit link: https://www.adafruit.com/product/3081 I believe that animation is done by scrolling. If I make more, I will have to get another enclosure and machine it. The soft button covers are really nice, but very hard to find. I think Pax had them molded in China.

I was planning to play with the font a little this afternoon.

The only reason to use squished fonts is to keep the abscissa template and characters in the same byte-high row. I am thinking of some other ways, including full size fonts and scrolling to solve the problem of too small a range in both X and Y.
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
That 5x5 might be worth a try. The l.c. "e" and other rounded letters are the problem. All caps helps, as do smaller pixels that I don't have. I also played around and got rid of "Time" like this:

upload_2019-2-22_16-29-11.png

Then squared up the remaining letters. HOrizontal space is not a problem, so the 5x5 may work better.

John
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
UPDATE
After a lot of interruptions and a little "quality time for coding," I can report some progress:

1) Linear memory (RAM) to save and retrieve data points works great. Rather than have one file for each row, I have a single file of 540 bytes (9 hours at 1 sample per minute) starting at 0x2020.

2) A plan to pre-calculate the Y-rows and bytes before saving to linear RAM was scrapped as it saved a minuscule amount of time/instructions. The linear file will be just sequential temperatures rounded to nearest degree.

3) Original plan to keep X-data as part of the graph data was scrapped. X-data are time increments of 1 minute. The clock does that, and X increments with each write. I haven't finished the scroll routines yet, but my plan is just to add an offset, then re-calculate and write the entire graph (7 pages x 60 columns approx.). Scrolling will be in both X and Y as needed. The axes are created by a template that, for now, doesn't change.

4) The fact that pages only wrap end column to beginning column, not page up and down is a bit of a pain. That requires that the bytes for each page be recalculated for each page change. I am using rotates as the simplest way to convert a bit number to a byte with that bit set. For example, a value of 83° offset by 50 = 33° = b'0010 0011'. That needs to be converted to page = 2, byte = 0x08 . In other words, the byte is mod 8 with the lsb at top. Let's say I scroll the graph part of the screen down 10°. Now, 83 becomes 23°, which is page = 4, byte = b'0000 0010'. Rotations make that pretty easy to visualize.

5) I also scrapped labeling the axes, as I just didn't like the way they looked. Besides, the right half of the display will show both current temperature, final temperature, rate, elapsed time, and expected time to finish. I may add ordinary characters to the axes to show the current offsets, but am leaning against adding that clutter.

6) The last major challenge is making the graph look like a continuous line. This demo starts at 1°/min, then increases to 2°/min at about 15 on the Y-axis, and finally goes to 3°/min at about 30 on the Y-axis. Of course,the 1°/min can't be improved upon without making a very thick line. At 2°/min it is still readable. I am not really happy with 3°/min and am considering making elongated data points, say 2 or 3 pixels. Haven't done anything on that yet, Page crossings will make it more complicated. Three degrees per minute is pretty uncommon in my experience, except with small roasts, like a rack of lamb.

Any opinions on the readability of 3°/min?

upload_2019-3-12_7-9-43.png

In case someone notices that I have not considered setting the start line for scrolling the Y-axis, that is because the right half of the display is being used for characters, and I don't want them to scroll too.

Since this sub-forum is about MCU's, here's the code I used to calculate pages/rows and bytes:

Code:
Mod8                          ;since tempF is in WREG, might re-write and 
                              ;save a step?
     movwf     tempF          ;f = rrrrrbbb
     lslf      tempF,f        ;f = rrrrbbb0 , discard msb
     movlw     0xF0           ;
     andwf     tempF,w        ;w = rrrr0000, rrrr =< 0x60
     xorwf     tempF,f        ;f = 0000bbb0     
     lsrf      tempF,f        ;f = 00000bbb
     sublw     0x60           ;w = rrrr0000 w/reversed order, 6->0 ... 0 ->6 
     btfss     STATUS,0       ;
     bra       ScrollY        ;rrrr > 0x60,increase Y offset, redraw LCD graph
;03/12 delete following 3 instructions & uncomment "xorwf" line, if saving byte 
     swapf     WREG           ;w = rrrr0000 ->0000rrrr
     addlw     0xB0           ;set page number
     call      PutCMD         ;sets page and returns in |B4 w/A0 clear     
;     xorwf     tempF,w        ;w = rrrr0bbb, ready to put to RAM (if wanted)
;     movwf     tempF
     
MakeByte                      ;enter with b'00000bbb' in tempF
;     movlw     0x07           ;if tempF = rrrr0bbb, uncomment
;     andwf     tempF,f        ;             "
     incf      tempF,f        ;f = bit # +1
     clrw                     ;     
     bsf       STATUS,0       ;     
ByteLoop
     rrf       WREG           ;@rot1 = f = 1000 0000       
     decfsz    tempF,f        ;@rot3 = f = 0010 0000
     bra       ByteLoop       ;@rot5 = f = 0000 1000
     bra       PutDat         ;@rot8 = f = 0000 0001
                              ;PutDat returns to original call
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
11,087
After more thought, I decided the excuse in post #19 is a bit lame. The display is readable, but was relatively easily changed to this:

upload_2019-3-14_7-6-33.pngupload_2019-3-14_7-7-51.png

That looks a little more readable, and 3°/min is quite uncommon in my experience with roasts. The code turns out to be similar to the previous snippet in that rotations are used to calculate bytes and rows. The biggest difference is resetting the column when a single "byte" crosses a page boundary before putting the rest of that byte to the display.

Sure would be nice to be able to read the display for that purpose. Instead, I used an FSRn to keep an index of the columns.

EDIT: Does anyone know of a graphical display controller where one can inhibit auto-increment for writes? (Inhibiting auto-increment for reads is common.)

John
 
Last edited:
Top