Need help with programming a Simple LCD menu system - Page 1
Forum section: electronics / beginners

author: ostamo1
author_rank: Newbie
timestamp: January 29, 2014, 06:09:31 pm
content: I am fairly new to PIC Microcontroller programming and would like to know how people make menu systems where you can select options and update variables. Any help ,ideas ,or tutorials would be great i am using a PIC16f690 currentlycurrently i have a single button that progresses through one of three states but would like to make it easier to program and i wold also like to update variables within the program while it is running. I also understand i may need more that one button to accomplish this and to have these menu buttons use interrupts.so far i have the code below that i am usingCode:#define STATE_MENU 0#define STATE_TEMP 1#define STATE_RTC 2#define STATE_MAX 3void ShowTEMP();void ShowRTC();void MainMenu();void main (void){ char state;state = STATE_RTC;LCD_Init(); // Initialize LCDi2c_setup();// Bitbanging I2Cwhile(1){switch(state){case STATE_MENU:MainMenu();break;case STATE_TEMP:ShowTEMP();break;case STATE_RTC:ShowRTC();break;default:break;}if(PORTAbits.RA5 == 0){state ++;if (state >STATE_MAX)state=0;}}}and what i would like to have is a menu system that i can do what is shown below with a cursor informing you of your current selectionCode:// Main Menu//* Show Temperature//* Time and Date// ** Show Time and Date// ** Update Time and Date// *** Hours// *** Minutes// *** Seconds// *** Month// *** Day// *** Year

author: valentinc
author_rank: Regular Contributor
timestamp: February 01, 2014, 05:45:41 pm
content: First, you should write a function that uses one of the timers to generate a 1 second reference... And have an interrupt routine every second and in that routine you increment a variable s"second"...In the main program you calculate the minutes and hours based on that variable and display them on the LCD...For updating time and date, just increment the variables that keep that at every interrupt generated by a button press...I don't know if this PIC does have an internal temperature sensor... If not, you must read the ADC (that is wired to an external sensor), and make the calculation to obtain the value directly in degrees C or F...

author: ostamo1
author_rank: Newbie
timestamp: February 03, 2014, 06:12:15 pm
content: Just so you know the sensors i have connected to the system are i2c sensors.i already have the code to show and display the current date,time, and temperature.i am mostly trying to figure out how to implement a menu system that i can visually select things and alter them while the program is running.such as my i2c RTC sensor runs about 1 minute fast a day and i would like to alter the minutes to be the correct time.i currently have one button that switched through all stated as shown in my previous post and it cycles through showing each output specified.but it does so with out any user feed back such as a cursor.

author: Rerouter
author_rank: Super Contributor
timestamp: February 03, 2014, 07:48:17 pm
content: Here is the menus system I use (yes arduino-ish), mainly based around a switch-case, PinB,1 is my button, and pos is my increment counter (yours would be a timer) with each of the functions below being self-contained, but for you it could go to a common function just modifying which variable to change,then save that variable to EEPROM and exit, (or in the case of the RTC set the value and add a field to the menu to adjust the correction factor (seconds per hour))Code:void List() {char selected = 0; // when moved will land on either variable freq or K factor, 2 common optionsattachInterrupt(1, encoder, CHANGE);lcd.clear(); // clears displaylcd.setCursor(0,0);lcd.print("Turn the knob to");lcd.setCursor(0,1);lcd.print("select an option");delay(1000);while (bitRead(PINB,1) == 1) { // runs until encoder button is pressedif (Pos != 0) { // if encoder position has changedselected += Pos; // cases are incremented or decremented depending on the encoder clicks,Pos = 0;delay(50);if (selected < 0) selected=6; // if reaches less than 0, rollover to 6,if (selected > 6) selected=0; // if reaches greater than 6, rollover to 0,lcd.setCursor(0,0);switch(selected) {case 0:lcd.print(" Frequency Out ");break;case 1:lcd.print(" Pattern Out ");break;case 2:lcd.print(" Pulse Counting ");break;case 3:lcd.print(" 1318 K Factor ");break;case 4:lcd.print(" Ratio ");break;case 5:lcd.print(" Measure Hz ");break;case 6:lcd.print(" Settings ");break;}lcd.setCursor(0,1);lcd.print(" ");}}delay(150);detachInterrupt(1);EncoderC = 0;// when enocoder button is pressed, this is what voids the cases begin,lcd.clear();switch(selected) {case 0:Variable();break;case 1:delay(300);Pattern();break;case 2:PulseCount();break;case 3:Kmeasure();break;case 4:Ratio();break;case 5:FreqMeasure();break;case 6:break;}}

author: mariush
author_rank: Super Contributor
timestamp: February 03, 2014, 08:01:04 pm
content: Well, first you need to decide how many buttons you're going to use. You could use 3 buttons ( left, right, ok/esc) and that's very easy.If you want to use just one button, it's a bit harder but it's possible to do it. Just make some rules like...Outside menu : short button press = rotate through states , long button press = enter menuInside menu : short button press = enter or exit a menu , long button press = advance to next menu option or change valueThen make a rule saying a button pressed for less than 50ms means simple button press , button pressed for more time (let's say 150-250ms) means long button press.An interrupt based just on state change on a pic will not be able to do this for you so you might have to combine this button press (state change on pin) interrupt with a timed interrupt triggering once every 5-10 ms. It can also be done just with a simple timed interrupt triggering every 5-10ms, instead of complicating yourself with two separate interrupts.Detect the first push with a "pin state change" interrupt then disable this interrupt and enable the time based interrupt to recheck the button state for the next few ms periods to see if it's a short or a long button press.As the code determines if it's a short or long button press, update a variable in the main code and disable the timed interrupt as well, so that the main code in the loop can update the menu and do it's job and then re-enable the "pin state change" interrupt pr timed interrupt to detect next button press.So you could press once (long press) to see menu on display with the first option in many a Back/Exit. This way if user got into wrong menu, he can press once for short period and go back. To change an option in the menu, press and hold button pressed and slowly move through the options the options and take finger off when over a menu option. Press once to select it.To set time and date you could have on one line HH : MM : SSWhen user enters this, he's automatically on HH so you could show it like this : MM : SSKeeping the button pressed means incrementing hours up to 23 then reverting back to 0. Press once to move to minutes : HH : [ MM ] : SS ... repeat , you get the idea. When done with the seconds, automatically exit to previous menu.The detection of short button presses vs long button presses can be quite simple ... put a timer interrupt at 5-10ms and after each interrupt if the button is still pressed add 1 to a counter.When you detect a 0 (button not pressed), then if the counter was > 2 and less than 10ms (20ms-100ms) then it was a single button press, and update a variable telling the program is short button press and reset the counter .If you detect a 1 (button pressed), then if counter is > 20-25 (20-25x10 ms) that means button was pressed for more than 200-250ms so it's a long press, update variable telling the program it's long press and reset counter.In the loop in main program, you just monitor that variable and do your thing updating the screen with the temperature and whatnot (if you're not in the menu). As soon as that variable changes to tell you a small or long press happened, you do your thing (draw menu on screen for example) and reset variable to "no button pressed"With three buttons, you can reuse the same interrupt function.. trigger every 5-10 ms, check if button is pressed or not .. increment counters to detect if it's short pressed or not, update a variable accordingly. the variable is analyzed in main loop and reset after something is done.

author: Rerouter
author_rank: Super Contributor
timestamp: February 03, 2014, 08:10:16 pm
content: It can still be done with 1 button, and is common in VDO speedometers and Tachometers, you press the button to enter the menu, and it will every 3 seconds go to the next menu item and finally return to normal operation if nothing is selected,when you enter the function you can either stagger it to another or bring up the variable your interested in scrolling right through left of each character of the variable pressing the button to increment, and when it reaches the right most length for that variable stores it and returns to normal operation (its also 3 seconds per move on those gauges)

author: ostamo1
author_rank: Newbie
timestamp: February 03, 2014, 08:32:58 pm
content: more than one button is fine , i was just looking for some pseudo code or direction into making the menu more robust.also since this is a PIC writing it in C code is different than writing in c++(ArduinoISH) code and that is why it is more difficult for me to write as i cannot just create a class in C code.