Interrupt Menu System Example and Help Needed
I have a working Interrupt Menu on an 18F2525, but need some help improving it. My program is constantly listening for data coming in on serial line via RB2, converting the binary data to usable information, and then either displaying that data, or writing the raw data to EEPROM. Since the data could come in as fast as every 100ms, I want to use an interrupt to manage the menu selection vs just polling for a button push. (but am open to simpler ways, certainly!)
The menu/interrupt needs to allow the user to change what is happening with as little disruption to the data read - store routine as possible. I don't need to get too fancy, because a few lost packets is OK, but I do want to allow user to select:
1. View 1 of data - eg long/lat
2. View 2 of data - eg speed/hdg
3. View 3 of data - eg volt/temp
AND,
4. Start recording data to EEPROM
5. Stop recording data to EEPROM
6. Play recorded data from EEPROM out the Serial port (Dump)
7. Erase EEPROM
Ideally, I'd use 2-3 buttons - 1/2 would scroll(up/down) throgh menus displayed on the LCD, the other would 'Select' the current view so the user can skip past those not wanted, and would then display that menu info on the LCD.
The routine I use now is a simple Interrupt with T1CON. I don't think this will do it for the more complex menu that I need. Here is what I have setup for the 18F2525:
@ __CONFIG _CONFIG1H, _OSC_INTIO67_1H
@ __CONFIG _CONFIG2H, _WDT_ON_2H & _WDTPS_128_2H
@ __CONFIG _CONFIG3H, _PBADEN_OFF_3H & _MCLRE_OFF_3H
@ __CONFIG _CONFIG4L, _LVP_OFF_4L & 0bfh ;_XINST_OFF_4L
'
' -----[ Includes/Defines ]---------------------------------------------------------
OSCCON=%01111000
DEFINE OSC 8
While OSCCON.2=0:Wend
include "modedefs.bas" 'include serout defines
'define loader_used 1 Used for bootloader only
ADCON0 = %00110000 ' turn off - select port AN12 (nothing)
ADCON1 = %00001111 ' turn portA to digital I/O (same as dec 15)
CMCON = $07 ' turn off
HLVDCON = %00000000 ' turn off
CVRCON = $00000000 ' turn off
SSPCON1 = %11011100 ' supposed to be turning on I2C
SSPCON2 = %01111000 ' supposed to be turning on I2C
INTCON = %11110000 ' TG guess at 2525 interrups for all INT pins
INTCON2= %01110101 ' rising edge of INT0,1,2 RB0,1,2
RCON = %10000000 ' no priority interrups
T1CON = %11000000 'Timer1 1:1 prescale.
...
variables, etc,
...
' ----- starting here ---------------------------------------------------
On Interrupt Goto ProcedureSwitcher
Bmenu = 1
ReStart:
TMR1H = 0 ' Clear time counts before Timer1 re-start
TMR1L = 0 ' Clear time counts before Timer1 re-start
PIR1.0 = 0 ' CLear over-flow flag before enable
T1CON.0 = 1 ' Turn Timer1 back on before entry into MainProcedure
Main:' ****** [Main Program Loop] ************************************************** ***************
wp=wp+1
gosub getdata ' go get data from radio
if stflag = 1 then
gosub storedata ' store ss data to EEROM if flag is on
endif
gosub calcvalue ' calculate values
if wp > 15 then
gosub waypoint ' ALWAYS put out a waypoint signal on the line (4800b)
endif
Select Case Bmenu ' display and act accordingly - driven from interrupts
case 1
gosub dis1 ' display information
Case 2
gosub dis2 ' display lat/long
Case 3
gosub dis3 ' display speed volts
Case 4
gosub dis4 ' Display Alt
Case 5
gosub dis5 ' RSSI - radio strenght
Case 6
gosub dis6 ' output memory - 9600b - same line as GPS is on
Case 7
gosub dis7 ' erase memory, reset address to 0
Case 8
gosub dis8 ' Start Recorder - set stflag to 1
Case 9
gosub dis9 ' Stop Recorder - set stflag to 0, write last address to EEPROM on 18F
End Select
' --------- Interrupt Handler ------------------------------------------------------------
IF PIR1.0 THEN ' IF Timer1 has over-flowed then
Timer = Timer + 1 ' Increment Timer variable
PIR1.0 = 0 ' Clear over-flow/int flag
' @20MHz 200nS * 65,536 * 8 = 0.1048576 seconds per over-flow
' 0.1048576 * 48 = ~5.033 seconds before jump to NextStage
IF Timer >= 48 THEN NextStage
ENDIF
goto Main
return
NextStage: ' Dont really know what you need here. Just a visual test
T1CON.0 = 0 ' Turn off Timer1 if you need to here
Timer = 0 ' Clear Timer var on entry here
TMR1H = 0
TMR1L = 0 ' CLear timer count registers as required
GOTO ReStart ' When you're ready to start all over
' Interrupt handler stuff here
'
Disable ' Disable interrupts in handler
ProcedureSwitcher:
Bmenu = Bmenu + 1 ' Changing tasks only
' I need to be able to do more here... A 'select' button?
If Bmenu = 10 Then Bmenu=1
serout2 ALCDout, LCDbd, [I,CLR]
serout2 ALCDout, LCDbd,[I,L1_C1]
serout2 ALCDout, LCDbd, ["Using Menu #: ",#Bmenu]
pause 500
Here:
While SWITCH = 0 ' waiting until
wend ' push-button is release
pause 100 ' debounce time
If switch = 0 then here
PIR1.0 = 0 ' Clear Timer1 over-flow flag
Timer = 0 ' Clear Timer counts before return
INTCON.1=0 ' reset RB0 interrupt flag
Resume ' Return to main program
Enable ' Enable interrupts after
' handler
The 18F2525 supports multiple interrupts, and even priorities of interupt (which I think I turned off) - EG there is INT on RB0, RB1, RB2... If necessary, I can move the incoming serial data from RB1 to RB5 or so to make space for multiple interrupts.
Anyone have any really good Interrupt/menu experience for such an application? I am sure this is a common type of activity - scroll through menus, then select the one you want, hopefully while something is still happening in the background....
Thanks,
Tom
I use a pot hooked to an analog port for scrolling.
That's right my secrets out. I have done a few projects that involve user interfaces and I like to use a pot to scroll through menu items. I also use a single button and assign it different funtions based on how long its held down. You see I like to use a timer to debounce my switch. I set a timer to run free with a 1 mS period. Then when I service its interrupt I check to see if the button is pressed and if it is I increment a counter. Back in my main loop I check the value of the counter and if it is > then say 1000 I toggle an output, if it is > then 3000 I enter the menu screens. If the button is not pressed I zero the counter.
Hope this helps,
Joe Kupcha.
P.S. Consider using a harware UART so that you won't need to wait and you won't miss any characters.
Edit: If you want to use buttons for navigation you can save I/O by using an analog port and setting up a resistor network with the switches shorting out indivdual resistors.
Great idea.. reading with POT?
So, a few questions:
1. What a 10k pot or something? Then setup a scale from 1-?? and convert this to X number of menu items? Hmmm..
2. My menu system is broken with my current interrupt system - for example, if I want to go PAST menu 5 to menu 6, I can't do it because menu 5 is already happening.. It will show me that I selected 'Menu 6', but then goes back to Menu 5 --
I need a way to SKIP menus I don't want to use and only dive in on the ones I want.. I think they way I have it set up, I HAVE TO go to each one.. no good..
Do I have a SECOND button which does the selecting? I've never made a menu system like this before that chooses basically Gosubs...
Tom
Not 'menu creative yet'..
I agree the hard part is keeping it usable!
My problem is that this is my first 'LCD Navigation' project with buttons. What I want and what I can do are not in alignment...
I WANT the program to behave like this:
Initialize:
- warm up LCD, etc.
- Ready interrupt buttons on RB0 and RB1 (menu & select)
- set counters to zero
main:
while no buttons are pressed
- go get data
- If recordflag=1 then
gosub recorddata
endif
- covert data to info
- show data view on LCD
If Menu=1: Gosub view1
If Menu=2: Gosub View2
If Menu=3: Gosub view3
Goto main
If main interrupt menu (B0) is pressed cycle through 3 options:
- Main Menu View - Explans to press B0 to move to next menu B1 to select
- pressing B1 here does nothing. Pressing B0 moves to next view
- Change View (BO/B1)
- If I press the B1 button while looking at this view, it selects it
- Select Menu1 - If I press B1, it selects it and returns to main
- If I press B0, it shows the next menu ->
- Select Menu2 - If I press B1, it selects it and retunrs to main
- If I press B0, it shows the next menu ->
- Select Menu3 - If I press B1, it selects it and retunrs to main
- If I press B0, it shows the next menu ->
- Select Exit - If I press B1, it selects it and goes to change view
- If I press B0, it shows Menu1 ->
- Start Recording(or stop recording if it is already on) (B0/B1)
- If I press the B1 button while looking at this view
- set recordflag to (on/off) depending on what current state is
- If I press the B0 button while looking at this view, it moves next view
- Playback recorded data(B0/B1)
- If I press the B1 button while looking at this view, it selects it
- takes me to a view that shows a warning that data will be dumped
- if I press BO/B1 it will cancel this and return to top
- If I press the B0 button while looking at this view, it moves next view
- Exit (go back to where I was)
- If I press the B1 button while looking at this view, it selects it
- If I press the B0 button while looking at this view, it moves first view
DURING this menu 'viewing', I want the processor to continue to take data in on the serial port.... If I DON'T click any BO/B1, after a certain amount of time, it should just COME BACK to the last menu# it was on...
My problem is that I can only seem to trap for B0, then cycle through one menu at a time, and ALWAYS select that one (can't skip it). Pressing B1 is not doing anything, and I am not sure how to turn it on as an interrupt.. (18F2525 supporst multiple INT, and I THOUGHT I turned them on..)
Any hints?
Thanks,
Tom