Strange screen glitch while changing values in menu function
eagletalontim
Posts: 1,399
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.
If anymore code is needed or any questions, please let me know! Thanks for any help in advance!!!!!!!
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
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 :
This strange issue is still happening and I have no idea how to fix it. Could someone please help me get this fixed?
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 ....
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.
What is this good for??? Just go to the place where you call DisplayingMenu and call DisplayMenu instead.
you can work with modulo operation instead:
line1:=startid
line2:= (startid+1)//(menuItems+1)
more to come ... ;o)
You should always try to use one device from within one COG.
... still more to come ...
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 :
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.
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.
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....
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 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:
And in boxLCDMenu you can do: 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).
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)
Repaint of the Menu should be covered by the repeat in ShowMenuFrom if you simply change lastId to -1.