User interface theory for the Prop
joeld
Posts: 49
I've got all the output pieces for one of my first propeller projects working and have been thinking on how to program the user interface. It will be a very simple interface. The display is an LCD and there will need to be the ability to set a time and date, the desired set points for the closed loop controller and a few misc options. I'm thinking a couple of push buttons and maybe a rotary knob.
I'm coming from an interupt background and am looking for some theory with the Prop. Do most of you write a user interface·object that runs continually in a separate COG constantly checking pins and storing the results in variables that will be returned to the top object for the appropriate actions to be taken? Or is it a better practice to keep the top object very simple and have it check for inputs and call other objects to act on them.
Joel
I'm coming from an interupt background and am looking for some theory with the Prop. Do most of you write a user interface·object that runs continually in a separate COG constantly checking pins and storing the results in variables that will be returned to the top object for the appropriate actions to be taken? Or is it a better practice to keep the top object very simple and have it check for inputs and call other objects to act on them.
Joel
Comments
So I like to keep my main runtime task free from the user interface. Have you seen my idea for using four buttons for a user interface?
http://forums.parallax.com/showthread.php?p=715868
Maybe it's a bit too radical for normal operators.
*Peter*
I like the idea of the LCD and the user input running in the same COG. I had it in my mind that i would need to call some function outside the user input object when an input ocurred. If I'm getting your concept, the user interface object could just sit and wait for input and operate the LCD. If inputs were made it would just store the new numbers in variables and the process control object could just check every so often to see if any of the set points had changed. I'd also have to send info to the user interface for LCD display.
Would it be smart to set up a common block of memory that the different objects could share this common information in?
The reason why I do it this way is because of the single level object accessibility in spin (since it·has no inheritance). Having the supervisor function in a place where it has easy access to everything makes programming much simpler.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
Post Edited (Paul Baker (Parallax)) : 3/27/2008 6:10:04 AM GMT
Initially more work, but as soon as you start to make changes, it pays off.
The multiple COGs are a nice fit to that concept.
<http://en.wikipedia.org/wiki/Model-view-controller>
Nick
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Never use force, just go for a bigger hammer!
The DIY Digital-Readout for mills, lathes etc.:
YADRO
http://www.rayslogic.com/propeller/Programming/BasicWindowForm/BasicForm.htm
I'm afraid I'm not familiar with the term "MVC". I can see where the main Cog 0 concept Paul uses would definitely simplify keeping track of all the variable values in one place and just passing them to other "drivers" as arguments. My input will definitely be low bandwidth, just buttons so I don't believe timing will be a problem.
One thing that troubles me is that I see some of the things you guys are doing with a prop and on this relatively simple project doing some PID control of a couple values with motor control and a very simple UI I'm eating up most of the available cogs. Makes me think I'm being very inefficient and missing some of the basic concepts of working with the prop.
I appreciate the thoughts. I'd rather get started on the right track.
Joel
Cogs are used to provide speed and/or tightly controlled timing for specific functions like video generation, floating point arithmetic, buffered serial port I/O, multiple servo control, stepper motor driver, etc. They're also used for things where isolating a function to an independent processor might simplify programming like some kinds of I/O device support.
Often people mix up the notion of object and cogs which really are independent. Often it makes sense to isolate certain code to a separate object and to have that object communicate with the main program through a specific interface, but none of the code in the object really needs to execute on a separate processor. Similarly, some very small functions are made much simpler by letting a separate cog execute them. For example, servicing a PING ultrasonic distance sensor. It just takes a small amount of Spin code to do it, but the main program can initiate the operation and come back to it later. The separate cog initiates the operation, waits for the return of the echo, and computes the transit time and distance, then stops its cog (freeing it for other uses) after storing the result.
I have not done many interfaces that are embedded, but what I have done I structured with a case statement.· What I mean is that in COG 0, the main cog, is all the code that glues everything together.· From that cog drivers are launched, variables initialized, etc.· Then I drop into the "game loop" as those in video games may call it.·
The game loop·is just an infinite loop that watches variables and responding accordingly.· Erase, move, and draw.· That simple - sort of.· At the top of the loop the·off screen buffer is cleared.· Then the various "events" are processed from the other cog/drivers.· The·GUI is·drawn based on case statements.· I setup a case statement for each screen and each time through the loop the screen is drawn.· Also in the case statement is where specific tasks are handled like moving a cursor or changing a value.· The limits are checked and the proper variables adjusted.· Finally the last thing done is the off-screen buffer is copied to the on-screen buffer.
I hope this helps.· I need to produce some code that I am allowed to post, the applications I have done are available for posting.
On a side note, you mentioned the limited I/O of the uOLED by 4D Systems.· I created an accessory that I call the uOLED-IOC which adds 16 GPIO through an I2C interface for the uOLED-96-PROP.· You can checked it out at www.brilldea.com.· Let me know what you think.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Timothy D. Swieter
www.brilldea.com·- check out the uOLED-IOC, an I/O expansion for the uOLED-96-PROP
www.tdswieter.com
One little spark of imagination is all it takes for an idea to explode
I looked at the MVC info. I'm not sure I have the brain cells available to comprehend it all right now.
I'm eager to try out some of these ideas. I need to pull the pieces off the breadboard and get them onto the proto board. The only item I haven't tried yet is the RTC.
Timothy, I have looked at your uOLED-IOC before. I'm still trying to convince myself the uOLED is overkill as far as displays go for this project. I just ordered 2 for the next project I have planned. I like the easy access for plugging in the prop-plug. I guess if you needed some high bandwidth pins you could use the prop pins available in extended mode and use the I2C pins for things that didn't need full speed. I'm assuming there has to be a considerable loss in speed during the I2C translation.
Thanks all for the ideas.
Joel
So, have you chosen a direction for your design? Have you coded it yet? Let us know how it goes.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Timothy D. Swieter
www.brilldea.com·- check out the uOLED-IOC, an I/O expansion for the uOLED-96-PROP
www.tdswieter.com
One little spark of imagination is all it takes for an idea to explode
I think I'm going to give the main CASE loop running in COG0 and calling other objects a try. I've started to rewrite the main object along these lines, just getting started. I'm trying to think of the most efficient way to use a CASE loop to read a bank of three buttons. I could read all the pins into the lower bits of a byte then use a variable assigned the number in that byte as the expresion. I.E if the button assigned to the second significant bit were pressed then the CASE for X=2 would run. Using this method I could have conditions run based on multiple buttons being held during one loop.
Sound feasible?
I think I need to reread your post a couple more times for the logic of what you are proposing to sink into my mind.·· In the mean time allow me a moment to describe what I did.· Perhaps I should work up example code that I can post since the code I am referencing isn't open for posting on the forum.
In my system I have a seperate cog that is reading data from a GPIO expander chip.· That cog is also debouncing the button inputs connected to the GPIO and placing the data in the HUB Ram.· Processing of the button input is important for debouncing and for detecting a change of the button (single shot, strobe, edge, what ever you want to call it)
In the "main" cog, usually Cog0, the psuedo code for the case statement stuff is something like this:
·· -Initiate objects, variables, etc
·· -Enter Infinite loop (repeat)
····· -Clear the screen (or screen buffer)
····· -Case currentScreen (currentScreen is a variable for the screen to be displaying)
········· -Screen1:
············ -draw the screen, text, numeric values, etc
············ -If forward button press (edge detected)
················· -nextScreen++
············ -if previous button press (edge detected)
················· -nextScreen--
········· -Screen2:
············ -draw the screen, text, numeric values, etc
············ -If forward button press (edge detected)
················· -nextScreen++
············ -if previous button press (edge detected)
················· -nextScreen--
......
········· -ScreenX:
············ -draw the screen, text, numeric values, etc
············ -If forward button press (edge detected)
················· -nextScreen++
············ -if previous button press (edge detected)
················· -nextScreen--
(case is finished, but still within the infinite loop)
···· If currentScreen <> nextScreen
······ -logic for copying nextScreen to currentScreen and checking for wrapping around
···· Any other logic you need to do per "frame"
···· Copy offscreen buffer to onscreen buffer
Maybe that will help.· There is a lot to it that I could expand on.· I defined constance for all my screennames so the case statements are easier to read than a bunch of numbers.· If you have a screen in which you have a cursor and variables changing you can also define those.· Inside each case statement you check for the cursor buttons and move the cursor appropriately and draw the screen.· If you have animation that is to occur, usually I setup a "FrameCnt" variable that increments each time through the infinite loop.· I then base the animation off of that instead of getting stuck in a loop within a case statement.· The system is unresponsive then.
Let me know if you have more questions or thoughts.· My post isn't well laid out, but there is a lot here to digest and I could clarify better.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Timothy D. Swieter
www.brilldea.com·- check out the uOLED-IOC, an I/O expansion for the uOLED-96-PROP
www.tdswieter.com
One little spark of imagination is all it takes for an idea to explode
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Timothy D. Swieter
www.brilldea.com·- check out the uOLED-IOC, an I/O expansion for the uOLED-96-PROP
www.tdswieter.com
One little spark of imagination is all it takes for an idea to explode
I haven't had much time to program on this much yet. Work keeps getting in the way. Hopefully I'll have a little time to work on it in the next few days.