Shop OBEX P1 Docs P2 Docs Learn Events
Ability to scroll lines in 4x20 display — Parallax Forums

Ability to scroll lines in 4x20 display

Don MDon M Posts: 1,653
edited 2011-12-29 11:16 in Propeller 1
I need some help in understanding how to do this. If anyone has some examples or sample code it would be much appreciated.

What I have is a 4x20 display with a rotary encoder and I want the ability to scroll through received data sent to the display. Lets say for example I have a buffer I want to scroll through that has 300 bytes of characters in it. The display is only capable of 80 at a time.

So what I am thinking is there needs to be a way that I can store the 300 bytes of characters maybe in an array? And then be able to scroll through the array in 20 byte chunks only moving the lines up or down depending on the turn of the encoder.

I have no idea how to do this or if it can even be done.

Thanks for your help.

Don

Comments

  • StefanL38StefanL38 Posts: 2,292
    edited 2011-12-27 13:14
    surely this can be done.

    For LC-Display-characters an array of bytes is sufficient

    I haven't tested the code below. It is meant as a raw template to give you an idea how it can be done
    The buffer contains the 300 ASCII-codes of the data you want to display.
    VAR
      byte Buffer[300]
    
      long Index
      long n
    
    
    
    PUB OneLineUp 
      Index := Index - 20
    
      if Index < 0
        Index := 0
    
      repeat n from 0 to 79   
        LCD.SendChar(Buffer[Index + n]
    
    
    PUB OneLineDown 
      Index := Index + 20
    
      If Index > 220
        Index := 220
    
      repeat n from 0 to 80   
        LCD.SendChar(Buffer[Index + n]
    

    mabye you have to add a command to switch to the next line

    keep the questions coming
    best regards
    Stefan
  • pedwardpedward Posts: 1,642
    edited 2011-12-27 14:46
    To improve on this:
    StefanL38 wrote: »
    CON
    BUFFER_SIZE = 300
    LCD_SIZE = 80
    LINE_SIZE = 20
    
    VAR
      byte Buffer[BUFFER_SIZE]
    
      long Index
    
    PUB DisplayBuffer(offset) | n
      Index += offset * LINE_SIZE 'offset is -1 to scroll back, +1 to scroll forward
      Index := Index #> 0 <# constant(BUFFER_SIZE - LCD_SIZE) 'constrain Index to 0 or last screen
    
      repeat n from 0 to constant(LCD_SIZE - 1)
        LCD.SendChar(Buffer[Index + n]
    
    
    

    The use of constant is to optimize the code a bit so that the math operations are done at compile time and a fixed value is inserted into the actual bytecode.
    The #> and <# are used to constrain the values in one shot instead of doing multiple evaluations.
    The code simplifies scrolling by simply sending the direction the encoder has turned. This allows you to use standard encoder reading objects that return direction.
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-12-27 14:56
    You can use my LCD-driver from the obex. It's taking care of the scrolling for you. The demo-code included shows you how to setup.

    http://obex.parallax.com/objects/576/
  • pedwardpedward Posts: 1,642
    edited 2011-12-27 15:15
    Even better, using existing code!
  • Don MDon M Posts: 1,653
    edited 2011-12-28 05:12
    Thanks for the tips. I'll give it a try and see what I can come up with.

    Thanks.
    Don
  • Don MDon M Posts: 1,653
    edited 2011-12-28 12:34
    pedward wrote: »
    To improve on this:
    CON
    BUFFER_SIZE = 300
    LCD_SIZE = 80
    LINE_SIZE = 20
    
    VAR
      byte Buffer[BUFFER_SIZE]
    
      long Index
    
    PUB DisplayBuffer(offset) | n
      Index += offset * LINE_SIZE 'offset is -1 to scroll back, +1 to scroll forward
      Index := Index #> 0 <# constant(BUFFER_SIZE - LCD_SIZE) 'constrain Index to 0 or last screen
    
      repeat n from 0 to constant(LCD_SIZE - 1)
        LCD.SendChar(Buffer[Index + n]
    



    The use of constant is to optimize the code a bit so that the math operations are done at compile time and a fixed value is inserted into the actual bytecode.
    The #> and <# are used to constrain the values in one shot instead of doing multiple evaluations.
    The code simplifies scrolling by simply sending the direction the encoder has turned. This allows you to use standard encoder reading objects that return direction.

    pedward- what does the variable "offset" in the method DisplayBuffer(offset) represent?
  • Don MDon M Posts: 1,653
    edited 2011-12-28 13:05
    Ok here is some code I'm trying and I'll be the first to admit I don't fully understand it so please don't laugh. What I was trying to do was make a demo with an existing project I made using the display and encoder connected as-is. The encoder doesn't produce a + or - as to direction of turn but a number depending on how it's initialized. I initialized it with a range of 100 starting in the middle. What I was attempting to do was if the number incremented it would act as though it was turning in a positive direction and move the message to display and increment it a line at a time.

    Help!
    con
    
      _clkmode = xtal1 + pll16x
      _clkfreq = 80_000_000
    
      MS_001   = 80_000_000 / 1_000
      buffer_size  = 300   
    
    obj
    
      oled   : "oled"                                       ' 4-bit OLED driver
      encdr  : "grayenc2_with_pb"                           ' 2-bit, graycode encoder
    
    var
                
      byte  buffer[300]
      long index
          
    pub main | n
    
      oled.init(0, 20, 4)                                   ' initialize display on P0
      pause(500)
      encdr.init(20, 1, 0, 100, 50, 22)                     ' detented encoder on P20 and pushbutton on P22
      oled.cmd(oled#CLS)                                    ' clear display
      pause(200)
      oled.moveto(1, 1)
      oled.str(string("Character Scrolling "))
      oled.moveto(1, 2)
      oled.str(string("demo on 4x20 display"))
    
      if encdr.read > 50
        index += 20
        index := index #> 0 <# constant(buffer_size - 80)
        oled.moveto(1, 1)
        repeat n from 0 to constant(79)
          oled.str(message[index] + n)
          
     
    pub pause(ms) | t
    
      t := cnt
      repeat ms
        waitcnt(t += MS_001)
    
    dat
    
       message    byte          "Line 1 of display..."
                  byte          "Line 2 of display..."
                  byte          "Line 3 of display..."
                  byte          "Line 4 of display..."
                  byte          "Line 5 of display..."
                  byte          "Line 6 of display..."
                  byte          "Line 7 of display..."
                  byte          "Line 8 of display..."
                  byte          "Line 9 of display..."
                  byte          "Line 10 of display.."
                  byte          "Line 11 of display.."
                  byte          "Line 12 of display.."
                  byte          "Line 13 of display.."
                  byte          "Line 14 of display.."
                  byte          "Line 15 of display.."
                  byte          "Line 16 of display.."
                  byte          "Line 17 of display.."
                  byte          "Line 18 of display.."
                  byte          "Line 19 of display.."
                  byte          "Line 20 of display.." 
    
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-12-28 13:19
    Do we talk about a 4-bit interface LCD-display which is HD44780-compatible at all?

    If so I'd still suggest to use my driver, as it takes care of all. I checked it with the LCD_menu.spin demo which comes with the driver. It uses exactly what you need for the scrolling:

    This line will start the driver
      LCD.start( LCDBasePin,0 )
    

    Setup of lines which use a buffer:
      ' setup the 3 lines below the headline for the menu
      ' this will set the length of a menu entry to 11 chars and disables auto-scroll
      LCD.exec( LCD#CMD_SETLEN, 0<<16 + 11 )
      ' point each line to different entries of the big menu-string
      LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE1 + $42<<16 + @menu1 )
      LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE2 + $12<<16 + @menu1+14 )
      LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE3 + $52<<16 + @menu1+28 )
    

    In CMD_SETLEN you specify the width of the line. In the demo it's 11 because I want some cursor-characters on the left and on the right. You would simply set it to 40.
    With CMD_SETLINE you specify the start-position for a line. $40 is the LCD-address of the 2nd line, so specifying $42 means that the 3rd character of line 2 is the start of the characters controlled by the buffer. ($00,$40,$14 and $54 are the start addresses of the different lines of the display.) @menu is the address of the buffer itself.

    In my demo-code I setup 3 lines for a menu using the same buffer at different positions. Of course you can also setup 3 lines with 3 different buffers of different size ... or only 1 line with 1 buffer.

    Scrolling all 3 lines is then as easy as that:
         if scroll_left
            LCD.exec( LCD#CMD_OTSCROLL, 14<<16 + 14<<8 + 14 )
          else
            LCD.exec( LCD#CMD_OTSCROLL, (255-13)<<16 + (255-13)<<8 + 255-13 )
    
    where 14 and (255-13) is the number of characters to scroll and <<16 / <<8 / <<0 tells which line to scroll.
    So, scrolling only one line 40 characters looks like
    LCD.exec( LCD#CMD_OTSCROLL, 40 )

    In your code you simply update the buffer as you like. It's like a screen-buffer and the display-driver will take care of refreshing the LCD.
  • Don MDon M Posts: 1,653
    edited 2011-12-28 13:25
    Yes the display I am using is HD44780 compatible in 4 bit mode. I will attempt to incorporate your driver code in my project to see how far I get.
  • Don MDon M Posts: 1,653
    edited 2011-12-29 08:13
    A good friend of mine (Mike O) helped me with this last night and came up with a working solution:
    con
    
      _clkmode = xtal1 + pll16x
      _clkfreq = 80_000_000
    
      MS_001   = 80_000_000 / 1_000
      buffer_size  = 300   
    
    obj
    
      oled   : "oled"                                       ' 4-bit OLED driver
      encdr  : "grayenc2_with_pb"                           ' 2-bit, graycode encoder
    
    
    VAR
      byte Buffer[300]
    
      long Index
      long n ,lyne
    
    pub Main      | oldval, newval
    
      oled.init(0, 20, 4)                                   ' initialize display on P0
      pause(500)
      encdr.init(20, 1, 0, 100, 50, 22)                     ' detented encoder on P20 and pushbutton on P22
      oled.cmd(oled#CLS)                                    ' clear display
      pause(200)
      oled.moveto(1, 1)
      oled.str(string("Character Scrolling "))
      oled.moveto(1, 2)
      oled.str(string("demo on 4x20 display"))
      bytemove(@Buffer, @message, 300)   'fills the buffer
      encdr.set(10)
        oldval := 0
        Index := 0
        repeat
           newval := encdr.read // 10
           if newval > oldval
             OneLineDown
             
           elseif newval < oldval
             OneLineUp
             
           oldval := newval  
           oled.moveto(17, 1)
           oled.dec(Index)
           pause(250)
           
      
    
    PUB OneLineUp 
      Index := Index - 20
    
      if Index < 0
        Index := 0
      repeat lyne from 1 to 4
        oled.moveto(1, lyne)
        repeat n from 0 to 19      
          oled.out(Buffer[Index+((lyne-1)*20) + n])
        
    
    PUB OneLineDown 
      Index := Index + 20
    
      If Index > 220
        Index := 220
    
      repeat lyne from 1 to 4
        oled.moveto(1, lyne)
        repeat n from 0 to 19      
          oled.out(Buffer[Index+((lyne-1)*20) + n])
        
    PUB send20
      oled.moveto(1, lyne)
      repeat n from 0 to 80   
        oled.out(Buffer[Index + n])
    
    
    
    pub pause(ms) | t
    
      t := cnt
      repeat ms
        waitcnt(t += MS_001)
        
    dat
    
       message    byte          "Line 1 of display..."
                  byte          "Line 2 of display..."
                  byte          "Line 3 of display..."
                  byte          "Line 4 of display..."
                  byte          "Line 5 of display..."
                  byte          "Line 6 of display..."
                  byte          "Line 7 of display..."
                  byte          "Line 8 of display..."
                  byte          "Line 9 of display..."
                  byte          "Line 10 of display.."
                  byte          "Line 11 of display.."
                  byte          "Line 12 of display.."
                  byte          "Line 13 of display.."
                  byte          "Line 14 of display.."
                  byte          "Line 15 of display.."
                  byte          "Line 16 of display.."
                  byte          "Line 17 of display.."
                  byte          "Line 18 of display.."
                  byte          "Line 19 of display.."
                  byte          "Line 20 of display.." 
    
  • Don MDon M Posts: 1,653
    edited 2011-12-29 10:33
    MagIO2- I played with your code a bit and it scrolls the messages horizontally. I was looking to scroll a whole line vertically sorry if I didn't mention that in my original post. I had some trouble trying to figure out why it doesn't place the characters correctly on my 20x4 screen. I changed your driver to specify 20 characters as in your notes but it acts like it is only a 2 line display.
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-12-29 11:16
    No problem ... seeing your code tells me that you exactly want what the menu demo also shows, except that my menu demo only uses 3 lines and one is fixed. You simply add another line and do:
      LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE1 + $00<<16 + @messages )
      LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE2 + $40<<16 + @messages+20 )
      LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE3 + $14<<16 + @messages+40 )
      LCD.exec( LCD#CMD_SETLINE, LCD#PAR_LINE4 + $54<<16 + @messages+60 )
    

    No need to create an extra buffer.

    And scrolling works with:
           if newval > oldval
            LCD.exec( LCD#CMD_OTSCROLL, 20<<24 + 20<<16 + 20<<8 + 20 )        
           elseif newval < oldval
             LCD.exec( LCD#CMD_OTSCROLL, 20<<24 + (255-19)<<16 + (255-19)<<8 + 255-19 )
    

    Maybe you can extend the if statements a bit to avoid scrolling when you reached the boundaries.
Sign In or Register to comment.