Shop OBEX P1 Docs P2 Docs Learn Events
LCD text wrapping and looking at previous text cut off? — Parallax Forums

LCD text wrapping and looking at previous text cut off?

Circuitbuilder9Circuitbuilder9 Posts: 85
edited 2012-03-29 16:56 in Propeller 1
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.
«1

Comments

  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-03-11 04:46
    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
            
    
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-11 06:24
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-11 09:43
    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?
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-11 11:32
    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.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-11 12:24
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-11 15:00
    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?
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-11 16:08
    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_AculaDr_Acula Posts: 5,484
    edited 2012-03-11 16:22
    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?
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-11 16:32
    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_AculaDr_Acula Posts: 5,484
    edited 2012-03-11 16:53
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-11 17:00
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-12 11:35
    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.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-12 14:45
    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_AculaDr_Acula Posts: 5,484
    edited 2012-03-12 15:42
    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?
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-12 16:56
    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_AculaDr_Acula Posts: 5,484
    edited 2012-03-12 17:24
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-12 17:25
    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_AculaDr_Acula Posts: 5,484
    edited 2012-03-12 18:27
    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])
    
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-13 00:32
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-13 04:41
    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?
  • SarielSariel Posts: 182
    edited 2012-03-13 08:07
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-13 11:11
    The code looks sensible. But what are the spaces in the DAT section after the numbered bytes? Do you need the spaces?
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-13 14:47
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-13 18:01
    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
    TransmitLCD 3.spin

    :blank:
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-13 18:16
    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
    TransmitLCD 3.spin

    :blank:

    paperclip.png Attached Files
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-14 02:11
    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]
  • SarielSariel Posts: 182
    edited 2012-03-14 04:44
    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.
  • SarielSariel Posts: 182
    edited 2012-03-14 04:46
    MagIO2 wrote: »
    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?
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-14 06:52
    Sariel wrote: »
    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.
  • Circuitbuilder9Circuitbuilder9 Posts: 85
    edited 2012-03-14 11:09
    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?
Sign In or Register to comment.