Shop OBEX P1 Docs P2 Docs Learn Events
Strange screen glitch while changing values in menu function — Parallax Forums

Strange screen glitch while changing values in menu function

eagletalontimeagletalontim Posts: 1,399
edited 2012-12-21 04:19 in Propeller 1
I am still working on my menu system and thought I had everything working properly until I started getting some strange thing happening when editing different menu items. Sometimes it will work properly, then every once in awhile I will get a strange jumbled character on the screen then the whole screen will just lock up or the program locks up. Hopefully someone can help me out with this. I can't post all the code, but I can post bits and pieces of it to help get this fixed. Some things in the code below are changed for proprietary reasons.

Here is the basic code for my menu function.
' -------------------------
' THIS PART IS IN MAIN PROGRAM 
VAR
  LONG LCDstack[100]

OBJ
  screen : "boxLCDMenu"

  repeat iloop from 0 to menuItems Step 1
    EVal1 := i2c.ReadByte(i2c#BootPin, i2c#EEPROM, (iloop * 4) + 32768)
    Stored[iloop] := EVal1
    evaltotal := evaltotal + EVal1

  if evaltotal == 0
    Stored[0] := 1
    Stored[1] := 1
    Stored[2] := 40
    Stored[3] := 6
    Stored[4] := 65
    Stored[5] := 20
    Stored[6] := 65
    Stored[7] := 22
    Stored[8] := 65
    Stored[9] := 23
    Stored[10] := 9
    Stored[11] := 40
    
  repeat iloop from 0 to menuItems Step 1
    screen.TransferArray(Stored[iloop], iloop)          ' Send EEPROM data to Screen Functions

  cognew(StartLCD, @LCDstack)

PUB StartLCD
  screen.init

  repeat
    screen.ShowMain
    pause(150)


    repeat until DisplayMenu == 1
      ReadThrottle 
      screen.Update(var1, var2, var3, var4)
      pause(130)
      
    screen.EnterMenu
    repeat while DisplayMenu == 1
      DisplayMenu := screen.DisplayingMenu
      pause(10)

'-------------- END OF MAIN PROGRAM -----------------

CON
  upbutton = 0
  downbutton = 1
  menubutton = 4
  menuItems = 12

VAR
  LONG cogon, cog
  BYTE Stored[255]
  BYTE DisplayMenu

OBJ
  lcd : "Serial_Lcd"
  i2c : "Basic_I2C_Driver"
  num : "simple_numbers"
  
DAT
  miSel       byte      $FF
  mi0         byte      "EXIT            ",0
  mi1         byte      "Menu 1      ",0
  mi2         byte      "Menu 2",0
  mi3         byte      "Menu 3     ",0
  mi4         byte      "Menu 4",0
  mi5         byte      "Menu 5  ",0
  mi6         byte      "Menu 6",0  ' GLITCHES MOST IN THIS MENU ITEM
  mi7         byte      "Menu 7 ",0
  mi8         byte      "Menu 8",0
  mi9         byte      "Menu 9 ",0
  mi10        byte      "Menu 10",0
  mi11        byte      "Menu 11",0
  mi12        byte      "Menu 12",0
  mis         long      @mi0, @mi1, @mi2, @mi3, @mi4, @mi5, @mi6, @mi7, @mi8, @mi9, @mi10, @mi11, @mi12

  null        long      $00 

PUB init
  DisplayMenu := 0
  lcd.init(24, 9600, 2)
  lcd.displayOn
  lcd.backLight(TRUE)
  pause(500)

PUB TransferArray(variable, index)
  Stored[index] := variable

PUB Update(var1, var2, var3, var4)
  lcd.gotoxy(5,0)
  lcd.str(num.dec(var1))
  lcd.gotoxy(11,0)
  lcd.str(string("     "))
  lcd.gotoxy(11,0)
  lcd.str(num.dec(var2))
  lcd.gotoxy(4, 1)
  lcd.str(string("    "))
  lcd.gotoxy(4, 1)  
  lcd.str(num.dec(var3))
  lcd.gotoxy(12,1)
  lcd.str(string("    "))
  lcd.gotoxy(12,1)
  lcd.str(num.dec(var4))

PUB SelectMenuByIndex(idx)
  if(idx > menuItems)
    return @null
    
  miSel := idx  
  return @@mis[idx]

PUB GetMenuItemSelected
  return miSel

PUB EnterMenu | startid
  DisplayMenu := 1
  startid := 0
  lcd.gotoxy(0,0)
  lcd.str(string("Programming Menu"))
  lcd.gotoxy(0,1)
  lcd.str(string("Loading....     "))
  pause(1000)

  ShowMenuFrom(startid)

PUB ShowMenuFrom(startid) | btnpresstime, lastid
  ShowMenuList(startid)

  repeat until DisplayMenu == 0
    btnpresstime := 0
    if(lastid <> startid)
      lastid := startid
      ShowMenuList(startid)
    repeat while ina[downbutton] == 1 or ina[upbutton] == 1 or ina[menubutton] == 1
      if(ina[downbutton] == 1)
        if(btnpresstime == 0)
          startid++
      if(ina[upbutton] == 1)
        if(btnpresstime == 0)
          startid--
      if ina[menubutton] == 1
        if(startid == 0)
          DisplayMenu := 0
        else
          DisplayMenuItem(startid)
      if(startid > menuItems)
        startid := 0
      elseif(startid < 0)
        startid := menuItems
      pause(100)
      btnpresstime++
    
PUB DisplayingMenu
  return DisplayMenu

PUB ShowMenuList(startid) | line1, line2
  line1 := startid
  if(startid < menuItems)
    line2 := startid + 1
  elseif(startid == menuItems)
    line2 := 0
    
  lcd.gotoxy(0,0)
  lcd.str(SelectMenuByIndex(line1))
  lcd.gotoxy(0,1)
  lcd.str(SelectMenuByIndex(line2))  
  lcd.cursor(1)
  pause(10)
  return

PUB ShowMain
  lcd.gotoxy(0,0)
  lcd.str(string("Testing Version "))
  lcd.gotoxy(0,1)
  lcd.str(string("Nulled out........ "))
  pause(1000)
  lcd.cls
  lcd.str(string("Val1:"))
  lcd.gotoxy(7,0)
  lcd.str(string("Val:"))
  lcd.gotoxy(0,1)
  lcd.str(string("VAL:"))
  lcd.gotoxy(8,1)
  lcd.str(string("VaL:"))
  lcd.cursor(0)

PUB DisplayMenuItem(id) | btnpressed
    btnpressed := 1
    lcd.gotoxy(0,0)
    lcd.str(SelectMenuByIndex(id))
    lcd.gotoxy(0, 1)
    lcd.str(String("Value :         "))
    lcd.gotoxy(8, 1)
    lcd.str(num.dec(Stored[id - 1]))
    lcd.cursor(1)
    repeat while btnpressed == 1
      if(ina[upbutton] == 1 or ina[downbutton] or ina[menubutton] == 1)
        pause(100)
        btnpressed := 1
      else
        btnpressed := 0
    
    repeat
      btnpressed := 0
      repeat while ina[upbutton] == 1 or ina[downbutton] or ina[menubutton] == 1
        if(ina[upbutton] == 1)
          if(btnpressed == 0)
            UpdateMenuValue(id - 1, 1)
            btnpressed := 1
        if(ina[downbutton] == 1)
          if(btnpressed == 0)
            UpdateMenuValue(id - 1, 0)
            btnpressed := 1
        if(ina[menubutton])
          lcd.gotoxy(0, 1)
          lcd.str(string("Saving...     "))
          pause(10)
          saveval(Stored[id - 1], id - 1)
          pause(500)
          ShowMenuFrom(id)
        pause(50)
        'do nothing

PUB UpdateMenuValue(storedid, amount)
  lcd.gotoxy(8, 1)
  if(amount == 1)
    Stored[storedid] := ++Stored[storedid]
  else
    Stored[storedid] := --Stored[storedid]
  lcd.str(string("       "))
  lcd.gotoxy(8, 1)
  lcd.str(num.dec(Stored[storedid]))
  pause(10)
  return

PUB saveval(value, address) | startTime, addr
  addr := (address * 4) + 32768
  if i2c.WriteByte( i2c#BootPin, i2c#EEPROM, addr, value)
    lcd.gotoxy(0,1)
    lcd.str(string("Failed : "))
    lcd.str(num.dec(value))
    abort ' an error occured during the write
  startTime := cnt ' prepare to check for a timeout
  repeat while i2c.WriteWait( i2c#BootPin, i2c#EEPROM, addr)
    if cnt - startTime > clkfreq / 10
      lcd.gotoxy(0,1)
      lcd.str(string("Failed : Timeout"))
      abort ' waited more than a 1/10 second for the write to finish
  lcd.gotoxy(10,1)
  lcd.str(string("Done"))
  return
  
PRI pause(Duration)  
  waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt)
  return

If anymore code is needed or any questions, please let me know! Thanks for any help in advance!!!!!!!

Comments

  • eagletalontimeagletalontim Posts: 1,399
    edited 2012-12-16 19:07
    And to add to this problem... When saving values to the EEPROM (pressing the menu button again after setting the value), I will get a "Failed" error when it executes this line in the saveval function :
    if i2c.WriteByte( i2c#BootPin, i2c#EEPROM, addr, value)
    

    Once the "Failed" error appears, the program will either lockup or restart the entire Prop.

    I have changed the saveval function to this for better error catching :
    PUB saveval(value, address) | startTime, addr
      addr := (address * 4) + 32768
      if i2c.WriteByte( i2c#BootPin, i2c#EEPROM, addr, value)
        lcd.gotoxy(0,1)
        lcd.str(string("Failed : "))
        lcd.str(num.dec(value))
        pause(2000)
        DisplayMenuItem(address-1)
        abort ' an error occured during the write
      startTime := cnt ' prepare to check for a timeout
      repeat while i2c.WriteWait( i2c#BootPin, i2c#EEPROM, addr)
        if cnt - startTime > clkfreq / 10
          lcd.gotoxy(0,1)
          lcd.str(string("Failed : Timeout"))
          pause(2000)
          DisplayMenuItem(address-1)
          abort ' waited more than a 1/10 second for the write to finish
      lcd.gotoxy(10,1)
      lcd.str(string("Done"))
      return 1
    

    This strange issue is still happening and I have no idea how to fix it. Could someone please help me get this fixed?
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-12-16 23:48
    Will take a while to go through all the code, so here are some hints on the way even if they don't solve the problem:
      if evaltotal == 0
        Stored[0] := 1
        Stored[1] := 1
        Stored[2] := 40
        Stored[3] := 6
        Stored[4] := 65
        Stored[5] := 20
        Stored[6] := 65
        Stored[7] := 22
        Stored[8] := 65
        Stored[9] := 23
        Stored[10] := 9
        Stored[11] := 40
    
    DAT
    initVals  long 1,1,40,6,65,20,65,22,65,23,9,40
    ...
    
      if evaltotal == 0
        longmove( @Stored, @initVals, 12 )
    
    
    Smaller, faster and easy to change, as you can put the DAT-section to the very top of your program.
      repeat iloop from 0 to menuItems Step 1
        screen.TransferArray(Stored[iloop], iloop)          ' Send EEPROM data to Screen Functions
    
    currently I assume that we talk about 2 different objects (you should always archive your code and upload the archive).

    2 better ideas:
    1. You could have a StoredValue-object which has the array in a DAT-section. The object would provide getter and setter functions. Then you can use this object in all parts that need these values all accessing the same variables. This object could also contain the code for reading from/writing to EEPROM.

    2. You could pass the address of the Store-array. Then you can access the values with
    long[ store_address ][ idx ]


    more to come ....
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-12-17 02:38
          if(ina[downbutton] == 1)
            if(btnpresstime == 0)
              startid++
          if(ina[upbutton] == 1)
            if(btnpresstime == 0)
              startid--
    

    you should limit the startid here!
    startid := ++startid<#menuItems

    startid := --startid#>0

    I don't know what the benefit of passing down the same value across several subsequent function calls is. Just make startid a VAR.
    PUB DisplayingMenu
      return DisplayMenu
    
    What is this good for??? Just go to the place where you call DisplayingMenu and call DisplayMenu instead.
      line1 := startid
      if(startid < menuItems)
        line2 := startid + 1
      elseif(startid == menuItems)
        line2 := 0
    
    you can work with modulo operation instead:
    line1:=startid
    line2:= (startid+1)//(menuItems+1)


    more to come ... ;o)
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-12-17 02:47
    Problem with saving data might be that you use Basic I2C in the main and in the menu object. The main might leave the I/Os in a state that it's impossible to use it from other COGs.

    You should always try to use one device from within one COG.

    ... still more to come ...
  • eagletalontimeagletalontim Posts: 1,399
    edited 2012-12-17 17:19
    Both objects are never run at the same time. The one in the main is only used when the device is booted up. If the device is booted up for the first time, with the eeprom data not saved, it will load all defaults into the "Stored[]" variable then save them to the eeprom. After that, the saveval function in the main code is never used again. The one in the Menu code is used each time a user exits a menu item after changing the value. The data is saved, then the "scrolling menu" reappears on the screen where they left off.
      repeat iloop from 0 to menuItems Step 1
        screen.TransferArray(Stored[iloop], iloop)          ' Send EEPROM data to Screen Functions
    

    This part simply transfers the "Stored[]" array to the Menu code so it can be modified via the programming menu and stored in the eeprom. When the user exits the menu, the flag DisplayMenu is set to "0" and the code here :
    ' Main program code that checks if the menu is still being displayed or if it needs to call "screen.ShowMain"
        repeat while DisplayMenu == 1
          DisplayMenu := screen.DisplayingMenu
          pause(10)
    
    ' Display program that returns the "DisplayMenu" flag when called by Main program.  0 = Main Screen, 1 = Programming Menu.
    PUB DisplayingMenu
      return DisplayMenu
    

    And last but not least.... I am not quite sure how the code below works or what it is doing. Just by guessing, it would appear as though the values are stored in the initVals variable until "longmove" is called which dumps the variables into the "Stored" variable as an Array? The way I have it set up currently is the Stored[] array and the Menu items share the same "index" number sequence. Example : Menu 1 = Stored[1], Menu 2 = Stored[2]. The EEPROM also uses the index number to specify where each value will be stored on the EEPROM using this equation : addr := (index of menu * 4) + 32768. Keeping all three using the same index sequence makes things sooo much easier for programming.
    DAT
    initVals  long 1,1,40,6,65,20,65,22,65,23,9,40
    ...
    
      if evaltotal == 0
        longmove( @Stored, @initVals, 12 )
    
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-12-17 23:16
    You're lying to me! Your index numbers are not equal for all three cases

    UpdateMenuValue(id - 1, 1)

    because mis[0] is pointing to EXIT, mis[1] is pointing to Menu1 which shall change Stored[0] ;o)

    Anyway ... what I proposed was not to change this relation or that you will no longer access variables via index.
    initVals is nothing else but an array where the first value has the index 0. longmove is only a faster and in terms of program size a smaller way to copy one array into another.

    I know what you do with the screen.TransferArray, but I don't know why! You duplicate the Stored array for no good reason. Again, using more memory and wasting CPU time. If you simply pass the address of the array to screen you save this. In screen accessing Stored would then look like

    long[ Stored_adr ][ index ] instead of
    Stored[ index ]

    that's the only difference.

    But to be honest, I think you should think a bit more object oriented in this case. Simply put anything that belongs to Stored into it's own object. Meaning:
    * a function start, which tries to read the values from EEPROM and defaults the values if there is nothing in EEPROM
    * a function for increase(idx) (including the check for limit)
    * a function for decrease(idx) (including the check for limit)
    * a function for getting the actual value like get(idx)
    * have the arrays for max and min values there
    storing the Stored-array in a DAT section makes it unique even if this object is used in several other objects/COGs.

    Sorry for not being a better help regarding the real problem you have, but if you don't come with a running reproducer it's just too much work. Sometimes bugs hide really good and it's hard to find them via simple code review.
  • eagletalontimeagletalontim Posts: 1,399
    edited 2012-12-18 04:39
    Lol. I never said the index values were equal... just the index "sequence".

    The Stored[] array is used in both the main and the screen program. The screen program uses it to show the values on the screen so the user can change it, and the main program uses it to adjust sensor readings and other things. Therefore, both codes need to have access to it.

    I am still working with the "flow" of things and am trying to learn as much as I can as I go. Starting and Stopping cogs on demand from code is a little tricky for me and so is transferring information between programs (Main and the Screen). I am trying to keep the two separate since I would like to change to a different screen in the future and updating the screen code would be much easier than having to find each and every bit of code in the Main program....
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-12-18 06:17
    The Stored[] array is used in both the main and the screen program. The screen program uses it to show the values on the screen so the user can change it, and the main program uses it to adjust sensor readings and other things. Therefore, both codes need to have access to it.

    I am totally aware ... and that's why it is better to keep the values at a central place.

    be aware, this code has not been tested so far, but should sketch what I mean:
    ValueContainer.spin
    CON
       ARRAY_SIZE     = 12
       EEPROM_ADDRESS = 32768
    
    DAT
    initValues	long 	1,  1, 40,  6, 65, 20, 65, 22, 65, 23,  9, 40
    maxValues	long  100,100,255, 50,100, 40, 99, 99, 99, 99, 10, 99
    minValues       long    1,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
    
    actualValues    long    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
    
    OBJ
      i2c:          "BasicI2C"
    
    PUB init | i, eeval, eevaltotal
      eevaltotal := 0
      ' here you have the code that reads the EEPROM
      i2c.init '???
      repeat i from 0 to ARRAY_SIZE
        eeval := i2c.ReadByte( i2c#BootPin, i2c#EEPROM, i<<2 + EEPROM_ADDRESS )
        actualvalues[ i ] := eeval
        eevaltotal += eeval
    
      ' if EEPROM does not contain valid values (maybe it is better to check each value against min and max instead of against 0 ?)
      if eevaltotal == 0
        longmove( @actualvalues, @initValues, ARRAY_SIZE )
      
    PUB increase( idx )
      actualValues[ idx ] := ++actualValues<#maxValues[ idx ]
    
    PUB decrease( idx )
      actualValues[ idx ] := --actualValues#>minValues[ idx ]
    
    PUB getValue( idx )
      return actualValues[ idx ]
    
    PUB store
      ' here is the code to store the values back in EEPROM
      i2c.init  ????
      ...
    
    The array is now in a DAT section which tells the compiler that it should only be available once, no matter how often you use the object.

    Then you can write in your main:
    OBJ
       myValues:   "ValueContainer"
         screen :     "boxLCDMenu"
    
    PUB main
      myValues.init
      cognew(StartLCD, @LCDstack)
      ....
      bla := myValues.getValue( 4 )
      ...
    

    And in boxLCDMenu you can do:
    OBJ
      myValues: "ValueContainer"
    
    PUB someFunction
    ...
      myValues.increase( idx )
    ...
      myValues.decrease( idx )
    ...
      myValues.store
    ...
    
    without a need to copy the values from main to screen and from screen to main.

    Maybe in case several values only make sense in combination, it might be wise to add another level besides actualValues named changedValues. Then you'd only modify the values there and a commit/revoke would copy the whole bunch of variables from changedValues to actualValues (commit) or back from actualValues to changedValues (revoke).
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-12-18 12:45
    Ok ... this is the problem:
    PUB ShowMenuFrom(startid) | btnpresstime, lastid
      ShowMenuList(startid)
          ShowMenuList(startid)
              DisplayMenuItem(startid)
        
    PUB DisplayMenuItem(id) | btnpressed
                UpdateMenuValue(id - 1, 1)
                UpdateMenuValue(id - 1, 0)
              ShowMenuFrom(id)
    

    In ShowMenuForm you call DisplayMenuItem and in DisplayMenuItem you call ShowMenuForm. So you run out of stack-space overwriting things you should not overwrite ;o)
  • eagletalontimeagletalontim Posts: 1,399
    edited 2012-12-19 16:45
    I am not quite sure how to go about it another way. It needs to cycle back to the menu list after the DisplayMenuItem function is run and completed. The DisplayMenuItem screen is the part where the user can adjust the menu item value, and the ShowMenuFrom is a function that shows the "scrolling" menu starting with "startid" which allows me to return the user to the programming menu where they left off.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-12-19 22:22
    Your DisplayMenuItem has to RETURN back to ShowMenuFrom instead of calling it again!

    Repaint of the Menu should be covered by the repeat in ShowMenuFrom if you simply change lastId to -1.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-12-21 02:30
    Hi, eagletalontim, did you solve the issue? Need more help?
  • eagletalontimeagletalontim Posts: 1,399
    edited 2012-12-21 04:19
    I have not been able to make the changes yet due to my work schedule. I will try to get it done this weekend :)
Sign In or Register to comment.