PDA

View Full Version : LCD text wrapping and looking at previous text cut off?



Circuitbuilder9
03-11-2012, 12:53 AM
Hey, guys.
(by the way, sorry for all my previous thread questions where i asked extremely broad questions and didn't get to the point)

I was wondering if anyone could direct me to any object that saves text on an LCD when it wraps and gets cut off the last line.
I would also like to know, in addition, if there where any sidebar scrolling (or just plain scrolling) objects/techniques for looking at the previous text put into the LCD, after it was cut off by new lines of text.

Dr_Acula
03-11-2012, 12:46 PM
If you want to scroll text on a LCD one technique is to set up a text buffer in the propeller memory. Then redraw the screen from that text buffer.

I know this won't be perfect code but it does demonstrate things like scrolling and wrapping text. This is a 20x4 display so declare an array with 80 bytes.

It won't be perfect but it might be a place to start.



'' parallel 20x4 lcd driver for the DracBlade including a 'Terminal' that scrolls properly, backspace etc

CON


_clkmode = xtal1 + pll16x ' use crystal x 16
_xinfreq = 5_000_000
LatchDirection =%00000000_00000000_00001111_11111111 ' 138 active, gate active and 8 data lines active
MaskLowByte = %00000000_00000000_00000000_11111111 ' used in lots of routines to & mask off low byte
LCDLatch = %00000000_00000000_00001011_00000000 ' LCD latch is xxxx101x and gate high xxxxxxx1
EnableBit = %00000000_00000000_00000000_00000010 ' Enable pin 6 on LCD displays

OBJ
delay : "timing"

VAR

long Char ' Character To Send To LCD
long Inst ' Instuction To Send To LCD
long Column ' column counter for line 4 ' don't use byte, causes all sorts of overrun errors
long LineNumber ' line number 1,2,3,4
long LCDChar ' character to send to LCD 0-$FF
long LCDByte ' byte to latch out to LCD display
byte Display[79] ' buffer for display 20x4 ' put this last as bizarre overwriting going on
PUB Start | n
Init_Lcd
Column:=0 ' start at zero
repeat n from 0 to 79 ' fill the display memory with spaces
Display[n]:=" " 'fill with spaces
Redraw
{
repeat 25 ' test code for running standalone
out(65)

out(10)
out(66)
out(13)
}


PUB str(stringptr)
'' Print a zero-terminated string
repeat strsize(stringptr)
out(byte[stringptr++])

PUB dec(value) | i '' Print a decimal number
if value < 0
-value
out("-")

i := 1_000_000_000

repeat 10
if value => i
out(value / i + "0")
value //= i
result~~
elseif result or i == 1
out("0")
i /= 10

PUB hex(value, digits) '' Print a hexadecimal number
value <<= (8 - digits) << 2
repeat digits
out(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))

PUB bin(value, digits)

'' Print a binary number

value <<= 32 - digits
repeat digits
out((value <-= 1) & 1 + "0")


PUB out(c) | i,j
' Output a character, ignores most <32 and all >126
' possibly add a 'turn on display' and 'turn off display' with selected ascii characters ?? esc seq
i:=c ' need a local working variable as an if statement seems to corrupt subsequent working variables!
j:=Column ' and need a local version of column otherwise corrupts this too

if c==10 ' Linefeed
Newline
Column:=0 ' reset column counter
j:=21 ' ensures code below doesn't run


if j<20 ' this LCD driver code is slow so better to not print anything after line 20 and wait for CRLF

' if j > 19 ' is it off the end of the display? (old display wrapped around)
' Column:=0 ' reset to beginning
' Newline

if c==08 ' Backspace
Backspace

' if c==13 ' do nothing as LF includes CR as part of the code

if i >= 31 ' and statements don't work in if lines?!
if i<=126 ' send the ascii character
char := c ' get the character
Send_Text ' send it out
Display[Column+60]:=c ' store the character in the last column
Column:=Column+1 ' add 1 to column

if j==19
CursorOff

PUB CursorOn
Inst:=13 ' turn on the cursor
Send_Inst

PUB CursorOff
Inst:=12 ' cursor off
Send_Inst ' send the instruction

PRI Init_Lcd
LCDLow ' set the 6 LCD lines low
delay.pause1ms(200) ' short delay for LCD bootup 200ms
LCDByte:=%00001100 ' send out 0011 already shifted to xxnnnnxx
PulsOut
LCDByte:=%00001100 ' send out 0011 already shifted to xxnnnnxx
PulsOut
LCDByte:=%00001100 ' send out 0011 already shifted to xxnnnnxx
PulsOut
LCDByte:=%00001000 ' send out 0010 already shifted to xxnnnnxx
PulsOut
LCDByte:=%00001000 ' send out 0010 already shifted to xxnnnnxx
PulsOut
LCDByte:=%00100000 ' send out 1000 already shifted to xxnnnnxx
PulsOut
Inst:=14 ' cursor on
Send_Inst
Inst:=1 ' cls
Send_Inst
Inst:=13 ' flashing cursor
Send_Inst
Inst:=128 ' to position 1
Send_Inst

pri Send_Inst ' send instruction in Inst
RSLow ' not actually needed see below but makes code more logical
LCDByte:=Inst ' get high nibble
LCDByte:=LCDByte>>2 ' shift right two bits so xxnnnn00
LCDByte:=LCDByte & %00111100 ' mask the nibble and sets RS low as well (bit0)
Pulsout ' send it out
LCDByte:=Inst ' get low nibble
LCDByte:=LCDByte<<2 ' shift left two bits so xxnnnn00
LCDByte:=LCDByte & %00111100 ' mask the nibble and sets RS low as well (bit0)
Pulsout ' send it out
RSHigh ' back to text mode

pri Send_Text ' pass Char = a byte
'RS always high = bit 0, bits are xxnnnnER where nnnn is the nibble, E=enable and R=RS
LCDByte:=Char
LCDByte:=LCDByte>>2 ' shift right two bits so xxnnnn00
LCDByte:=LCDByte & %00111100 ' mask off just the nibble
LCDByte:=LCDByte | %00000001 ' keep RS high
Pulsout ' send it out
LCDByte:=Char ' get low nibble
LCDByte:=LCDByte<<2 ' shift left two bits so xxnnnn00
LCDByte:=LCDByte | %00000001 ' keep RS high
Pulsout

pub Next_Line

Inst := %11000000 ' Move Cursor To Line 2
Send_Inst

PRI ToggleGate
OUTA[8]:=0 ' set gate pin low
OUTA[8]:=1 ' and high again - may need a delay in PASM
PRI EnableLatchPins
DIRA:=LatchDirection ' 138 active, gate active and 8 data lines active
PRI Tristate ' tristate all pins
DIRA:=%00000000_00000000_00000000_00000000
PUB LCDOutput | n ' sends byte LCDByte to latch
EnableLatchPins ' turn on the pins needed to talk to the latches
n:=LCDByte ' byte value to send out
n:=(n & MaskLowByte) ' 2 AND mask the low byte
n:=(n | LCDLatch ) ' 3 OR with the LCD address 138
OUTA:=n ' 4 send it out
ToggleGate ' 5 and latch it
tristate ' back to tristate on all pins
PUB LCDLow ' sets all LCD pins low for startup
LCDByte:=0
LCDOutput
PUB PulsOut ' pulse the enable pin 6 high then low - uses LCDByte and returns it unchanged
LCDByte:=(LCDByte | %00000010) ' logical OR to set just this bit high
LCDOutput ' send it out
' delay.pause1ms(1) ' short pause not needed as spin very fast
LCDByte:=(LCDByte & %11111101) ' logical AND set just this bit low
LCDOutput ' send it out
' delay.pause1ms(1) ' delay for LCD to process it
PUB RSHigh ' set RS pin 4 high - uses LCDByte and returns it unchanged
LCDByte:=(LCDByte | %00000001) ' logical OR to set just this bit high
LCDOutput
PUB RSLow ' set RS pin 4 low - uses LCDByte and returns it unchanged
LCDByte:=(LCDByte & %11111110) ' logical AND set just this bit low
LCDOutput

PUB Backspace | i,j
' backspace is a special case - need to subtract 1 off the column, make the last character a space
' and redraw the screen and print out column number of characters on last line
' only really works on line 4 but most of the time the display is on line 4 anyway
j:=Column ' the last character added 1 to column so start with one less
if j>1 ' not reliable at 1
Display[Column+60-1]:=32 ' was one past the character
Column:=Column-1 ' subtract 1
Redraw
repeat i from 0 to j-2
Char:=Display[60+i] ' print out the last row with one less character
Send_Text


PUB Redraw ' redraw the entire screen
Inst:=128 ' line 1
Send_Inst
LineNumber:=1 ' print line 1
Printline
Inst:=192 ' line 2
Send_Inst
LineNumber:=2 ' print line 2
Printline
Inst:=148 ' line 3
Send_Inst
LineNumber:=3 ' print line 3
Printline
Inst:=212 ' line 4
Send_Inst
LineNumber:=4 ' print line 4
Printline
Inst:=212 ' cursor to beginning of line 4
Send_Inst
CursorOn
PUB PrintLine | n ' Prints the line in LineNumber 1 to 4
n:=(LineNumber-1)*20 ' convert to pointer for the array 0-79
repeat 20 ' do this 20 times
Char:=Display[n] ' get the character from the array
Send_Text ' send it out
n:=n+1 ' increment counter

PUB Newline | i ' move the lines all up one, clear line 4, move cursor to position 1 of line 4
repeat i from 0 to 59 ' move up to next line
Display[i]:=Display[i+20]

repeat i from 60 to 79
Display[i]:=32 ' space

Column:=0 ' column counter to zero
Redraw ' redraw the screen

MagIO2
03-11-2012, 02:24 PM
Have a look at BenkyLCDdriver in the obEx, it allows to setup a screen buffer and allows to scroll either driven by magic or by your code. The demos included should show what's possible.

Circuitbuilder9
03-11-2012, 05:43 PM
Thanks, guys! Really appreciate it!

So, to clarify, if i were to use a 2 * 16 display, i would just use an array of 32 bytes?

Next is, if i were to use buttons to do my scrolling to see the text, what would i insert into the program to do so?

Circuitbuilder9
03-11-2012, 07:32 PM
Oh, one more question:
If i wanted to write the data to an SD card or Micro SD, would i direct the memory string of bytes to the SD card object instead of main memory address?
How would you do so?

By the way (this is for MagIO2), i couldn't find the benkylcddriver on the OBEX. is it named differently, or what? plz help me.

MagIO2
03-11-2012, 08:24 PM
Oh .. sorry ... it's only the name of the SPIN-file and it's called differently in the ObEx. Well .. here it is: http://obex.parallax.com/objects/576/

If you want to write data to an SD-card you need an SD-card driver like FSRW or Kyes FAT driver. You'd then open a file and call the str, dec or whatever functions of that driver to write the data into the file. It depends on what you want to do later on with the file. If it should only be used by a self written PC-program you can store the values directly as long, word, byte ... whatever.
If you want to open that file with a text-editor or Excell you'd write those values as strings separated by commas.

Circuitbuilder9
03-11-2012, 11:00 PM
OK.

Thanks, MagIO2, for the redirection. But still, one of my questions haven't been answered yet: if i going to use buttons for scrolling, what would i do in spin (or assembly) to do so?
And, if i am using FSRW specifically, how does Dr. Acula's object previously mentioned intertwine with the FSRW object? What would i do to make sure things worked smoothly with the two, to write the data to the SD and transfer the text to the LCD while you can scroll to see the text previous?

MagIO2
03-12-2012, 12:08 AM
I can only talk for the driver I wrote.

You can setup the driver in a way that it uses a portion of the HUB-RAM as a display buffer. This buffer can have any number of columns (I think in the range of 1-128) and any number of rows (only limited by amount of available HUB-RAM) and you can see the display as being a window showing only a portion of this buffer.
For displaying something you simply copy the string you want to display into this buffer.
So, let's say if you want to have 2 rows with 128 columns you simply reserve 256 bytes and tell the driver where row 1 starts and where row 2 starts. For scrolling left/right you'd simply send an CMD_LCDOTSCROLL (LCD one time scroll) to the driver giving the number of columns to go left or right. You can have a look at LCD_menu.spin which uses this to scroll a menu up and down automatically. The difference between up and down versus left and right is that for up/down the number of bytes to move is equal to + or - of the line length where for horizontal scrolling you simply use +1 or -1.

Well .. Dr. Acula's object comes with it's own conversion-functions, which send the string representation (like HEX or BIN) of a long/word/byte directly to the display. FSRW on the other hand does not have such functions at all and my display driver neither. Why? Because the conversion from long/word/byte to a string should rather be coded in a separate object which avoids duplicate code.

So, Dr.Acula + SD means that you need a string object for the conversion. Call LCD.hex to output a number to screen, call string.hex to convert the number again, call SD.pputs to write the string to SD card. -> duplicate code + duplicate conversion
BenkyLCD + SD means that you also need a string object. Call string.hex to convert a number, copy string into screen buffer (bytemove), call SD.pputs.

Dr_Acula
03-12-2012, 12:22 AM
If you want to pull something to bits, Kyedos has many useful code fragments http://forums.parallax.com/showthread.php?128779-KyeDOS-an-operating-system-for-the-Propeller&highlight=kyedos

There is the LCD driver above, plus a String object that can do all sorts of string manipulation, plus the SD driver code, plus a keyboard. There is a lot of other stuff you won't need so it might take some time to find the useful parts.


So, to clarify, if i were to use a 2 * 16 display, i would just use an array of 32 bytes?

Next is, if i were to use buttons to do my scrolling to see the text, what would i insert into the program to do so?

32 bytes will hold the 2*16 display but later you might want a bigger text buffer. The propeller can do this. Say it held 100 lines so 1600 bytes. Plenty of room in the propeller for that. Then you can load in a large text file from an SD card and scroll it up and down with two lines visible on the display.

Are you going to have a keyboard?

Circuitbuilder9
03-12-2012, 12:32 AM
32 bytes will hold the 2*16 display but later you might want a bigger text buffer. The propeller can do this. Say it held 100 lines so 1600 bytes. Plenty of room in the propeller for that. Then you can load in a large text file from an SD card and scroll it up and down with two lines visible on the display.

Are you going to have a keyboard?

Yes, Dr. Acula, i do plan to have a keyboard, and it will be attached via the keyboard hardware from parallax's store. But when i said 32 bytes (one for each character, right?), i meant for just those lines of text being shown on the LCD. So that means i need more than 32 multiplied by the amount of total space needed for all the text put into bytes for the whole buffering thing?

And to just put the whole buffering thing into a simple view, the characters are arranged on main memory, and the LCD simply is a window to part of the text, right?
Sorry for being simple here, i am a freshman at high school with only some computer science experience. If i'm wrong, can you explain the buffering part to me?

Dr_Acula
03-12-2012, 12:53 AM
And to just put the whole buffering thing into a simple view, the characters are arranged on main memory, and the LCD simply is a window to part of the text, right?


You can do all sorts of clever things. You could certainly scroll up and down. You could also scroll left and right as well. The display acts like a little window on a page of text. The page of text can be loaded from the SD card into the propeller memory. Then you can edit it. Then save back the edited text onto the SD card.

So one thing to think about - if you have a line of text, say 20 characters wide, do you "wrap" that at character 16 onto the next line, or do you not display the last 4 characters unless the user scrolls right?

If you have 'word wrap' off, then an new line is a 'carriage return' and 'line feed'. Ascii characters 13 and 10.

Another thing to think about is Backspace and Delete. Are you going to add those?

If you have a keyboard, you can use the arrow keys and Home and Page Down etc to scroll your text.

Circuitbuilder9
03-12-2012, 01:00 AM
A question about carriage return and line feed in spin:

So one thing to think about - if you have a line of text, say 20 characters wide, do you "wrap" that at character 16 onto the next line, or do you not display the last 4 characters unless the user scrolls right?
Carriage returns and line feeds are available in spin even though it is not mentioned in manuals, correct? So does that mean spin automatically responds to hexadecimal code in ASCII? If so, how would you put that in code corresponding to the LCD objects? I do plan to use those, as well as backspace (i don't know about delete).

I am also confused about main memory. Is it that easy to access; simply put data into the memory address for later retrieval? Or is it far more complicated than that; are there protocols? Or am i over-complicating things? Please tell me about this, this is where i get confused in programming: memory and methods to retrieve and write memory.

Circuitbuilder9
03-12-2012, 07:35 PM
I have inserted this response because it went off the first discussion page. If at all possible, i would like my latest questions answered, please.

MagIO2
03-12-2012, 10:45 PM
The availability of carriage return and line feeds and backspaces does not have anything to do with SPIN as such. These are simply special byte values which are interpreted by some code in the known manner. For example if you have a terminal program this will interpret the backspace as "go back one character and delete what's there".

LCD drivers that I know do not implement these special characters. This has to be done in your code for example in the code that reads a keyboard. If you receive a byte you need to check whether it's a backspace and if so delete the last character in the input buffer and update the LCD.

You already know ways how to access main memory (HUB-RAM): Define a variable and use it, define an array and use it.

Dr_Acula
03-12-2012, 11:42 PM
re
Carriage returns and line feeds are available in spin even though it is not mentioned in manuals, correct? So does that mean spin automatically responds to hexadecimal code in ASCII? If so, how would you put that in code corresponding to the LCD objects? I do plan to use those, as well as backspace (i don't know about delete).

I am also confused about main memory. Is it that easy to access; simply put data into the memory address for later retrieval? Or is it far more complicated than that; are there protocols? Or am i over-complicating things? Please tell me about this, this is where i get confused in programming: memory and methods to retrieve and write memory.

and


I have inserted this response because it went off the first discussion page. If at all possible, i would like my latest questions answered, please.

There may be some reluctance to answer as it is a little unclear what your level of experience is, and so therefore is harder to phrase an answer. If the meaning of CRLF is unclear then we may need to take a step towards a simpler explanation.

As MagIO2 says, CR and LF do whatever you code them to do. For a simple terminal program, you might write some Spin code that looks at the ascii value of a character. If >31 and <127 then print it. If 13, move cursor to the beginning of the line. If 10, scroll up one line. If 8 then delete the last character and move the cursor left by 1 (but not if already as far left as it can go). Home/End/Page Up etc come out of the keyboard as two byte codes and you can process them as well if you want to. If so, you will need to think about a way to pass two byte codes from the keyboard object to the display object.

This is the code you need to write, although there are many examples that have already been written.

Re memory, yes it is easy to access. Like MagIO2 says, you can define variables and arrays. For a screen buffer you would use a byte array. Say your text page was 80 columns wide and 40 high = 3200 bytes, then define an array with 3200 bytes. Then there is some maths to work out where you are - address = row * 80 + column. And to convert back the other way from address to row and column. Then work out the window for your display.

Some of this can be a bit daunting. In fact, I don't think this has actually been fully coded for the propeller yet.

Where it gets really complicated is where you have a page of text originally written for a wider screen and you now want to display on a smaller screen. Do you word wrap or not? What size screen do you emulate - 16 col, 40 col, 80 col? How do you handle a paragraph of text 300 characters long?

I'm not sure of the answers to that - I'm vaguely thinking of taking an existing page of text on an 80 col VGA display and squeezing it into a 16x2 but if I ever coded that I'd like the original text in front of me on the VGA screen to check the window is correct as you move it around. Nothing the prop can't do but it would be quite a coding effort.

People tend to stick with much simpler LCD objects which just put text on the screen and auto wrap and possibly scroll.

I've found the best way to code LCD drivers is to start very simple. Get the display to display an "A". Then work on each piece one at a time - eg how to handle backspace. Then test it.

Some things have already been coded - eg talking to an SD card is quite complicated but thanks to existing objects, it becomes one line of code to open the file, one line to read in an entire text file to hub memory, one line to close the file.


I would also like to know, in addition, if there where any sidebar scrolling (or just plain scrolling) objects/techniques for looking at the previous text put into the LCD, after it was cut off by new lines of text.

I don't think exactly that code has been written. But it can be. Are you ok with writing this code yourself?

Circuitbuilder9
03-13-2012, 12:56 AM
Some things have already been coded - eg talking to an SD card is quite complicated but thanks to existing objects, it becomes one line of code to open the file, one line to read in an entire text file to hub memory, one line to close the file.


I would also like to know, in addition, if there where any sidebar scrolling (or just plain scrolling) objects/techniques for looking at the previous text put into the LCD, after it was cut off by new lines of text.


I don't think exactly that code has been written. But it can be. Are you ok with writing this code yourself?

I would love to write it myself. However, since i am fairly new to propeller assembly (i'm good at knowing the code itself, but i still need to practice for a consistent, effective use as well as knowing protocols and standard procedure), and would like to know where to start. In other words, how do you start a propeller project effectively so it can actually be finished? If i had some guidance, i'll know what to do for this and perhaps many more projects.

Dr_Acula
03-13-2012, 01:24 AM
I would love to write it myself. However, since i am fairly new to propeller assembly

It may be easier to write this in Spin and later translate to assembly. Spin may be fast enough anyway.


how do you start a propeller project effectively so it can actually be finished?

ha ha - didn't someone start a poll recently where people had to confess about all the unfinished propeller projects they had lying around?

Seriously, start simple and break the project up into manageable pieces. For an LCD, I'd start with a simple 32 byte array. Have a row and column pointer for the array. When each character comes in, send it to both the display and the array.

Normally characters just go through. But there are some special cases. eg
If the column is 15 on the display, then newline
If the character is 10 then newline
If the character is 8 then process a backspace and redraw the line from the array.

'newline' scrolls up one line.

Grow it from there. Have you got a character on the display yet? Have you got a keyboard working?

I must say I was pretty pleased with the propeller chip when I managed to find some LCD code in the Obex, couple it with some keyboard code, and glue them together with a few lines of Spin so the characters went to the display.

Circuitbuilder9
03-13-2012, 01:25 AM
By the way, i was looking at the propeller manual, and i didn't see in the long, word, and byte section if you could directly tell the program where you want the data exactly in main hub memory, by a hexadecimal address

Dr_Acula
03-13-2012, 02:27 AM
I don't think you can tell it exactly where to put it, and it may change anyway as the program changes and grows in size.

Arrays are fairly easy to work with. Declare the array at the beginning in the VAR section


VAR
byte myarray[32]


and this becomes a global array so any PUB can use this



myarray[15] := 65
lcdout(myarray[15])

MagIO2
03-13-2012, 08:32 AM
What would be the reason for telling the compiler where to put your variables? There is no good reason except a few! But as you're a beginner I assume that you are not thinking about those few exceptions. In all other cases if you feel the need of giving addresses for me it would be a reason to think about your software design. And that's the reason for me not to tell you how to do that at the moment ;o)

I think when you start a bigger project you first have to have a big picture already including all known components. Which devices do I want to attach - keyboard, LCD, Sensors?, Motor? With this big picture you can estimate which platform should be feasible. Do I need external RAM, SD card, a bigger EEPROM, a port extension, motor drivers .... With this information you can also search for objects in the object exchange. When you find more than one driver for a device - for example for the LCD - you can now investigate which one fits best to your needs.
LCD is actually a good example:
There are several LCD drivers available. When I wrote mine all of them were written in SPIN and having some disadvantages. One was not capable of doing a proper reset. The SPIN drivers block your code as the output functions run in the same COG. My PASM driver is fire and forget in several ways. You can set it up as usual where you simply call output functions to print a string. But even then you only pass the address of the string to the driver COG and you own COG can continue immediately. You can also set it up in screen-buffer mode where a part of propeller memory is displayed. In this mode your scrolling left/right and up/down thing is easy to implement. And I think this is the easiest way of driving an LCD when several COGs should output values/strings totally independent. On the negative list there is of course the need of a COG - which actually is negative only if your project runs out of COGs. Oh .. and maybe that for using my driver you really need to know how the display works (it's described in the comment). There are no wrapper functions which increase the comfort ... hmm ... I posted these somewhere, but they are not part of the driver-package. But the demos should help a lot.

So, ... for a beginner I think it's a good idea to pick the objects that you might need and write some test-code per object to really understand how you can use these objects.

After that you can really go on with the project. As you have the big picture you can now devide it into several small AND preferably independend pieces. Independent because you can then reuse your own code or parts of the code for the projects coming up in future!
1. Make the LCD run in general
2. Do the setup for screen-buffer mode
3. Do the scrolling

4. Make the SD card run
5. Make the keyboard run
6. Write your input-routine according to your needs (backspace....)

x. put all pieces together

For your backlog ... how big shall this backlog be? Just a few lines? Will it be reset after switch off/switch on or should it remember the old data?
Anyway .. I have an enhancement of the FSRW which would help! ( http://forums.parallax.com/showthread.php?117573-Virtual-Memory ) I called it virtual memory because you create a big SWAP-file on the SD-card and can easily access each byte/word/long by simply giving it's address. It's functions also support moving memory blocks from HUB-RAM to the file and the other way around.

Let's say you setup a screen-buffer mode LCD driver with 128 x 2 characters. When appending you write the line into the screen-buffer and then directly copy it into virtual memory. Line 0 at address 0, line 1 at address 128, line 2 at address 256 and so on.
If you scroll up (which means go back in the backlog) you'd simply copy the actual lines back into the screen buffer. This is only a few lines of code.
Scrolling left/right is done in HUB-RAM only.

And .. by the way ... if you need help it also helps to share the big picture here on the forum. It helps in giving you the right tipps, because one solution might be good in a special case where it's bad in another case.

Circuitbuilder9
03-13-2012, 12:41 PM
And .. by the way ... if you need help it also helps to share the big picture here on the forum. It helps in giving you the right tipps, because one solution might be good in a special case where it's bad in another case.

OK, that sounds great. I'll post my "master object code" when i am finished implementing the LCD, SD, the modified FSRW, and Keyboard objects. However, during the "screen buffering", i will probably ask a few questions. Does that sound good?

Sariel
03-13-2012, 04:07 PM
I'm not sure if this idea will be helpful for your application or not, but what I have done in the past is store txt files on an SD card, and when it comes time to read/display the info I copy the contents of the file to a buffer in the HUB ram, then display from there, like...


CON

DownButton = 0 ' Input from the "Down Button"
UpButton = 1 ' Input from the "Up Button"
VAR
Long LineNo

DAT
TextBuff
byte "1 ", 0
byte "2 ", 0
byte "3 ", 0
byte "4 ", 0
byte "5 ", 0
byte "6 ", 0
byte "7 ", 0
byte "8 ", 0
byte "9 ", 0
byte "10 ", 0

OBJ

LCD : "Lcd_NHD-0420D3Z-FL-GBW.spin" ' NewHaven LCD Driver

PUB Init
' Init LCD
LCD.init(LCDPin, LCDBaud)
LCD.setContrast(30)
LCD.setBright(7)
LCD.dispOn
LCD.cls

Main

PUB Main

LineNo := 1
repeat
if ina[DownButton] and LineNo > 0 ' If down was pressed, and LineNo is not out of bounds
LineNo++ ' Advance to the next line
waitcnt(clkfreq / 3 + cnt) ' One shot timer for Down button

if ina[UpButton] and LineNo < 11 ' If up was pressed, and LineNo is not out of bounds
LineNo++ ' Go back to previous line
waitcnt(clkfreq / 3 + cnt) ' One shot timer for Up button
DispOut



PUB Dispout
''Method here for displaying the buffer on the screen, based upon the position stored in the LineNo variable



I have not even compiled this, so I have no idea if it is going to work or not, but I think it gets my idea across. The biggest limitations would be text file size vs. how much free hub ram you have, and how long it would take to copy the contents to the buffer just after the init of the SD card. On the plus side tho, it would take hardly any time at all to fetch the info out of the buffer and get it to the screen.

Circuitbuilder9
03-13-2012, 07:11 PM
The code looks sensible. But what are the spaces in the DAT section after the numbered bytes? Do you need the spaces?

MagIO2
03-13-2012, 10:47 PM
Hi ... just was interested in this problem and wrote some lines of code as a proof of concept. This is all you need for a backlog:



con
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000

LCDBasePin = 8
LEDOnPin = 15
SDpinBase = 0

WIDTH = 128

obj
LCD: "BenkyLCDdriver"
sdfat: "fsrw"

dat
scrbuf1 byte " "[WIDTH],0
scrbuf2 byte " "[WIDTH],0

pub main | i,j,k
' if LEDOnPin is a valid pin number, set to output a 1 (enable LED)
if LEDOnPin < 31
outa[ LEDOnPin ] := 1
dira[ LEDOnPin ] := 1

LCD.start( LCDBasePin,0 )
sdfat.mount( SDpinBase )
sdfat.vOpenSwap( 0, string("SWAP3.SYS") )

' clear display
LCD.exec( LCD#CMD_INSTR, %1)

' set the driver to screen-buffer-mode
LCD.exec( LCD#CMD_SETLEN, 16 )
LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE1 + @scrbuf1 )
LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE2 + $40<<16 + @scrbuf2 )
LCD.exec( LCD#CMD_SETRATE, 100000 )

' write the lines to screen and to SD-card - this code is pretty fast and you won't see much on the LCD ;o)
k := 0
repeat j from 0 to 999
' copy the bottom line to the top line
bytemove( @scrbuf1, @scrbuf2, 128 )
' and create the new content of the bottom line
printLine( @scrbuf2, j )
' NOTE: this only changes the content of the memory, the LCD driver automaticaly updates the LCD

' store the bottom line in the virtual memory at address k
sdfat.vwMemCopy( 0, k, @scrbuf2, 128 )
k+=128

' and now scroll back
repeat until k==0
k -= 128
' copy the top line to the bottom line
bytemove( @scrbuf2, @scrbuf1, 128 )
' and fetch the content to be shown in top line from the virtual memory
sdfat.vrMemCopy( 0, k, @scrbuf1, 128 )

waitcnt( clkfreq/2+cnt )

PUB printLine( adr, num )
bytemove( adr, string( "number " ), 7 )
dec( adr+7, num )

PUB dec( adr, val ) | i
i := 0
IF (val < 0)
-val
byte[ adr ][ i++ ] := "-"

i:=5
if val>0
repeat while val<>0
byte[ adr ][i--]:="0"+val//10
val:=val/10
else
byte[ adr ][i]:="0"

The FSRW is of course the version including my virtual memory functions, which needs some preparation of the SD-card. First you have to format it and then copy an empty file with 256MB named SWAP3.SYS to the card.
Also make sure that the PIN-numbers are correct. The backlight of my LCD display is driven by a propeller PIN (plus transistor) which is defined by LEDOnPin.

Circuitbuilder9
03-14-2012, 02:01 AM
Hey, guys.
I started practicing LCD byte buffering, and i have one minor problem:

when i write the write to the code, i look at the compile current, and then look at the array i made, and there are no values in the designated addresses. Did i do something wrong? Also, when i use the repeat loop to insert the data (with a limit of 32 bytes : the 2 * 16 LCD), the data never shows up on the LCD. Otherwise, how's the rest of the code (it's short, i know. But i wanted to make sure)?

90561
90563

:blank:

Circuitbuilder9
03-14-2012, 02:16 AM
Hey, guys.
I started practicing LCD byte buffering, and i have one minor problem:

when i write the write to the code, i look at the compile current, and then look at the array i made, and there are no values in the designated addresses. Did i do something wrong? Also, when i use the repeat loop to insert the data (with a limit of 32 bytes : the 2 * 16 LCD), the data never shows up on the LCD. Otherwise, how's the rest of the code (it's short, i know. But i wanted to make sure)?

Binary(LCDtest).binary (http://forums.parallax.com/attachment.php?attachmentid=90561&d=1331686819)
TransmitLCD 3.spin (http://forums.parallax.com/attachment.php?attachmentid=90563&d=1331687549)

:blank:
http://forums.parallax.com/images/misc/paperclip.png Attached Files

http://forums.parallax.com/images/attach/spin.gif TransmitLCD 3.spin‎ (http://forums.parallax.com/attachment.php?attachmentid=90562&d=1331686835) (689 Bytes, 1 views)

MagIO2
03-14-2012, 10:11 AM
Oh ... you have a serial LCD ... must have missed this info somewhere in the thread!

The binary you attached is pretty sure not the binary of TransmitLCD3.spin!

For this

byte char[320]

you would see 320 zero bytes in a row somewhere and for this

List byte $1F[320]

you would find 320 bytes with value $1F somewhere. I'd make TransmitLCD3.spin the top-file and always to a compile top. Otherwise you get what you see, which might mean that you compiled Debug_Lcd.spin for example.



lcd.putc(char[temp])
temp++
index++ 'increment the index for the array address
temp <#= 32

is this by intention? You will never see something else but the content of char[0]-char[31]. At least I'd expect to have an offset which can be set, so that the display starts at other lines.

Why you don't see anything ... $1f might be undefined in the display. The useable characters usually start at $20 (=space). You can easily print out all the characters of the display to see which will show something and which not, as this is depending on the brand of the display anyways. Also above 128 you can find anything, for example chinese characters....
In other words: Try it with a character where you know that it should display something
Line byte "X"[320]

Sariel
03-14-2012, 12:44 PM
The code looks sensible. But what are the spaces in the DAT section after the numbered bytes? Do you need the spaces?

I just put those in there to illustrate what you would want the maximum length for one line to be. = 20 characters. I account for this in my code, making sure to leave the zero terminator because that is the flag for the end of the string, so the prop knows to stop outputting at that point.

Sariel
03-14-2012, 12:46 PM
The FSRW is of course the version including my virtual memory functions

OOOOOOOOOOOOOOOOOOO... I did not know that was out there..... that is VERY slick. It's in the obex I assume?

MagIO2
03-14-2012, 02:52 PM
OOOOOOOOOOOOOOOOOOO... I did not know that was out there..... that is VERY slick. It's in the obex I assume?

No, not yet. I wanted to make it an object on it's own which in the end is a memory map. Some portion of the 4GB address-space we can have using long should be
HUB-RAM
HUB-ROM
EEPROM
Memory map file
external RAM
....

So, for a program using this kind of virtual memory driver it would be totally transparent on which physical memory it's operating. Somehow I got distracted from this, but the as is version included in my private version of FSRW 2.6 is already very usefull.
It's available in the forum http://forums.parallax.com/showthread.php?117573-Virtual-Memory.

As you can see in the thread it's already used in an editor written by CassLan which implements a gap buffer.
I use it in my newCogOS for all kind of stuff. For example all the text messages and help pages for commands are put into virtual memory keeping HUB-RAM free for more important stuff.
I also started to code a home automation system using a 320x200x16 LCD touchscreen where all the background images, animations and icon files are stored in virtual memory. This keeps away tons of files from the filesystem and would also allow software-delivery by simply passing the swap-file....

I think I should add an blog-entry which explains how it currently works and what it's good for. If you have any questions, don't hesitate to ask.

Circuitbuilder9
03-14-2012, 07:09 PM
To MagIO2,


You will never see something else but the content of char[0]-char[31]. At least I'd expect to have an offset which can be set, so that the display starts at other lines.

Why you don't see anything ... $1f might be undefined in the display. The useable characters usually start at $20 (=space). You can easily print out all the characters of the display to see which will show something and which not, as this is depending on the brand of the display anyways. Also above 128 you can find anything, for example chinese characters....
In other words: Try it with a character where you know that it should display something
Line byte "X"[320]

How do you set an offset? Also, to clarify, i used $1F as an example, but had no intention that it would present other character. So, what is the highest hexadecimal number that shows english text and computer commands (i.e.- carriage return)?
And what do you mean by "over 128, you can find anything? Is that also referring to hexadecimal value?
What also do you mean by "Line byte "X"[320], if it can't exceed 128? I am confused about that.
Lastly, since my LCD is asynchronous serial, it can decifer hexadecimal by the ASCII chart?

Circuitbuilder9
03-15-2012, 09:51 PM
http://forums.parallax.com/images/icons/icon1.png Re: LCD text wrapping and looking at previous text cut off?
To MagIO2,


You will never see something else but the content of char[0]-char[31]. At least I'd expect to have an offset which can be set, so that the display starts at other lines.

Why you don't see anything ... $1f might be undefined in the display. The useable characters usually start at $20 (=space). You can easily print out all the characters of the display to see which will show something and which not, as this is depending on the brand of the display anyways. Also above 128 you can find anything, for example chinese characters....
In other words: Try it with a character where you know that it should display something
Line byte "X"[320]


How do you set an offset? Also, to clarify, i used $1F as an example, but had no intention that it would present other character. So, what is the highest hexadecimal number that shows english text and computer commands (i.e.- carriage return)?
And what do you mean by "over 128, you can find anything? Is that also referring to hexadecimal value?
What also do you mean by "Line byte "X"[320], if it can't exceed 128? I am confused about that.

MagIO2
03-15-2012, 10:56 PM
$1F is what you print on the LCD right? And you expect to see something! But some of the values available in a byte simply show nothing on the display as the ROM font of the LCD display does not contain pixels for these characters.
I would start with a small program which shows you all the possible characters from 0-255 ($00-$FF). Then you will see what characters the brand which produces the LCD has implemented. The first characters (I think 8) are definitely empty because you can add user defined character data there. As in most environments the byte values below $20 are considered as control characters, some LCDs will show nothing here. The range from $20-$7f is defined according to the ASCII characters (have a look in wikipedia). And above $7F it again depends on the brand what characters you will find there. As I said, I've seen displays showing chinese symbols.

If you want to store text-characters in a byte you can also use "X" for example. You don't have to know which byte value represents the X.
List
byte "X"[320]
will simply create 320 times X in the List-array.

"How do you set an offset?"
Well ... let's assume a line can be bigger than 16 characters, say 64 characters. If you have a backlog of 10 rows in HUB-RAM you will still only be able to show a 16x2 window on the LCD. The backlog buffer would be
byte backlog[ 64*10 ]
If you want to show a window on the LCD you have to define where to start - this is what I mean with offset. An offset of 0 will show the first row. An offset of 64 will show the 2nd row. 128 shows the 3rd row .....
With an offset of 1, the display will start at the 2nd column. An offset of 140 will start output in row 3 column 12.

A display loop would look like that:



repeat y from 0 to 1
repeat x from 0 to 15
lcd.putc( backlog[ offset + y*64 + x ] )

Dr_Acula
03-15-2012, 11:41 PM
So, what is the highest hexadecimal number that shows english text and computer commands

http://www.asciitable.com/

$7E according to the table.

It is probably a sad indication of being a nerdy propellerhead asking how many ascii values you can name off the top of your head. I'll start with (decimal) 7,10,13,27,32 and 65. Others will no doubt be able to do more. Bonus points for knowing the decimal and hex values!

Circuitbuilder9
03-16-2012, 01:37 AM
Great. I know what everything means now. Thanks!

(sometimes it takes awhile to get facts into your head and make it work properly. Thank you for your patience.)

I'll most likely post more code as time progresses for revision.

Circuitbuilder9
03-20-2012, 01:14 AM
Hey, guys.
I finally got some code up for LCD byte sending practice, and i would like to know about this problem:
When i send the characters through the repeat loop, and try to copy the characters through a different loop, all i get on the serial LCD is backslashes. What am i doing wrong?

90814

Dr_Acula
03-20-2012, 04:44 AM
That is slightly odd. Looking at the code, I would have expected you would have got nothing on the display.

What are you trying to do on the display? One thing that is sometimes helpful is to put comments on each line saying what that line does. Then you can go through each line and ask - is this doing what I actually want it to do?

Are you trying to print some ascii characters from 32 to 126?

MagIO2
03-20-2012, 07:54 AM
I think what you really want to do in the first loop is:



repeat backlog from 1 to 94
char[ backlog ] := char[ backlog - 1] + 1


backlog++ is wrong at this place, as the repeat loop already increments the variable.
This would create a sequence of characters starting from $21 in the char-array.

That's a good lesson to learn. But you can also change the code I gave you to work with the serial interface LCD. You simply write a little loop which permanently writes the content of a screen-buffer to the LCD. Pretty much the same you do in your second loop.

1. resize char array to char[ 2*16 ]
2. change the 1st loop to initialize the LCD with spaces (" " or $20)
3. put gotoxy(x, y) outside of the loop - I guess this display also will put chars coming in a sequence on behind the other
4. do the lcd.putc for 32 characters without waitcnt
5. start this 2nd loop in it's own COG
6. in my code replace all @scrbuf1 by @char and all @scrbuf2 by @char+16 and remove all LCD.* related code.

Circuitbuilder9
03-20-2012, 08:32 PM
Thanks, MagIO2.
To answer Dr. Acula's question, i was praciticing buffering code to the 2x16 character serial LCD by using a repeat loop to initialize the data, incrementing the ASCII hex value by 1 to get different characters. Then, the next repeat loop takes the characters and prints the first 5 (it can be any number up to 32). However, i get the first character right, but the rest are just slashes, which aren't next in the ASCII. What's going on? Sorry about my lack of documentation. I'll do that from now on.

MagIO2:


backlog++ is wrong at this place, as the repeat loop already increments the variable.
This would create a sequence of characters starting from $21 in the char-array.

That's a good lesson to learn. But you can also change the code I gave you to work with the serial interface LCD. You simply write a little loop which permanently writes the content of a screen-buffer to the LCD. Pretty much the same you do in your second loop.

1. resize char array to char[ 2*16 ]
2. change the 1st loop to initialize the LCD with spaces (" " or $20)
3. put gotoxy(x, y) outside of the loop - I guess this display also will put chars coming in a sequence on behind the other


1. where do i start if i was to use your own code?
2. resizing the char array would make the entire memory map short, right? i wanted to make a large array, then buffer parts to the LCD.
3. The gotoxy should be in the loop if i want to move over to the next column to fill the character, right? I'm confused about why it has to be on the outside.
4. Does all LCDs have to be initialized with spaces before buffering actual characters to and from the memory array?

MagIO2
03-20-2012, 10:17 PM
I'd first start with understanding the proposed changes to your code.

The goal is to have a screenbuffer just big enough to keep the content of one screen and continuously send the characters to the LCD. This will run in it's own COG endlessly! The advantage is that each other COG can simply write into the screenbuffer and the content will show up on the screen automatically.

About 2.)
Yes, the buffer you need in HUB-RAM is just as big as the LCD screen. The backlog is written to the SWAP-file which can be any size!

About 3.)
Gotoxy is only needed once if you write characters that should appear one after the other. The LCD has an internal pointer which is incremented automatically. So, if you call gotoxy 1,1 the pointer will be set. If you then call putc the character is written to position 1,1. If you then send another character with putc it's written on position 2,1 .....
At best you only need gotoxy once to set it to the upper left corner and then send 32 characters in a row
at worst it only works per line: goto upper left corner, send 16 characters, goto next line, print 16 characters

About 4.)
It's not about initializing the LCD, it's about initializing the screenbuffer which is sent to the LCD continuously.

About 1.)
1. Do the changes to your code.
2. Make your code a function
3. Copy into my code and call COGNEW on this function
4. Make my code write into your screenbuffer (@char/@char+16) instead of (@scrbuf1/@scrbuf2).

Circuitbuilder9
03-21-2012, 01:55 AM
MagIO2,

i was copying your code, and i encountered a problem:

sdfat.vOpenSwap( 0, string("SWAP3.SYS") )
--- vOpenSwap is not a subroutine file. Is there another variation of fsrw, or is this your modification mentioned earlier?

....and second

PUB dec( adr, val ) | i
i := 0
IF (val < 0)
-val
byte[ adr ][ i++ ] := "-"


i:=5
if val>0
repeat while val<>0
byte[ adr ][i--]:="0"+val//10
val:=val/10
else
byte[ adr ][i]:="0"


What is the purpose of this loop? I am curious.

Circuitbuilder9
03-22-2012, 07:05 PM
(I posted this because it was not answered)

MagIO2,

i was copying your code, and i encountered a problem:

sdfat.vOpenSwap( 0, string("SWAP3.SYS") )
--- vOpenSwap is not a subroutine file. Is there another variation of fsrw, or is this your modification mentioned earlier?

....and second

PUB dec( adr, val ) | i
i := 0
IF (val < 0)
-val
byte[ adr ][ i++ ] := "-"


i:=5
if val>0
repeat while val<>0
byte[ adr ][i--]:="0"+val//10
val:=val/10
else
byte[ adr ][i]:="0"


What is the purpose of this loop? I am curious.

MagIO2
03-22-2012, 09:21 PM
Hi ... sorry, did not see your latest post.

As I wrote in #30: "It's available in the forum http://forums.parallax.com/showthread.php?117573-Virtual-Memory ." Maybe it also helps to read the thread, as you have to prepare the SD-card. (1. format it, 2. copy a SWAP3.SYS to it which is big enough...)

The purpose of the loop is to convert a long to a decimal string. Each iteration will output a digit.
// gives you the rest of the division. val // 10 simply means it gives you the value of the least significant digit. For example: 123 will give you a 3.
Adding 3 to "0" converts the numerical 3 to the character "3". In the next step val := val / 10 the 3 is removed from the number, as in integer you have no fractional part. 123 / 10 = 12.
If this division is somewhen 0 you're done with converting the number.

Circuitbuilder9
03-26-2012, 12:13 AM
MagIO2,

I was analyzing your code for your LCD screen buffer and i would like to have a simulation of how the (PUB dec) works. What exactly does it do to the strings? Why does it say :

if val>0 repeat while val<>0
byte[ adr ][i--]:="0"+val//10
val:=val/10
else
byte[ adr ][i]:="0"

The second and third line confuses me by what it does. Please explain, thnx.

Circuitbuilder9
03-26-2012, 05:19 PM
I posted this because it went off the page...

MagIO2,

I was analyzing your code for your LCD screen buffer and i would like to have a simulation of how the (PUB dec) works. What exactly does it do to the strings? Why does it say :
Code:

if val>0 repeat while val<>0 byte[ adr ][i--]:="0"+val//10 val:=val/10 else byte[ adr ][i]:="0"
The second and third line confuses me by what it does. Please explain, thnx.

MagIO2
03-26-2012, 07:41 PM
I'm so sure that I already explained this dec ... either it's a deja'vu or I'm getting to old ...

Ok, this DEC seems to be a special version, as it can only convert up to 6 digits without overwriting something because of the
i:=5. The point is, that i goes from 5 to 0 - from right to left. The digit on the right is the units digit, so you are not interested in the any amount above 9. You get this by using the modulo operator //. So, let's say value := 1234 -> value // 10 = 4. But now you have the binary value and you have to convert it to ASCII for the output. Fortunately the ASCII characters for numbers are sorted starting with "0", then "1", "2".... As ASCIIs are also numbers for a uC, you can simply add "0" + 4 to get "4".

For the next loop the units digit is no longer needed, so we do a decimal "shift" of the number by dividing by 10:
value := value / 10 which results in 1234 / 10 = 123. Now the next units digit of value is really the tens digit of the original number, that's why it's written left hand side of the buffer.

Next loop will work with 12, next with 1 and the next loop gives you a 0 which tells the loop that we're done.

The point of doing it this way (right to left) is, that each number is right alligned no matter how many digits it has (0-6).

Circuitbuilder9
03-30-2012, 12:56 AM
I bought the parallax keyboard and microSD and the ps2 header, and i am ready to start. I've already got text onto the LCD from the keyboard using simple getkey commands from the keyboard object.

How do i start with the microSD and buffering the text from the mSD to the LCD and still get the key input from the keyboard?