How to write an LCD menu for AVR in C

Let us write a simple LCD menu for AVR. I am using four buttons: 2 for menu scrolling up and down and two for changing submenu parameters. As the output indicator, I am using three LEDs that flash according to the menu’s parameters. Button states are captured by using timer0 overflow interrupts.

The circuit is elementary:

LCD AVR circuit with buttons and LEDs

 I have excluded the power circuit for simplicity, just left the main parts: LCD, LEDs, and buttons. This circuit works well with the Proteus simulator as it is. The Proteus circuit is attached to the project archive.

My idea is to store menu strings in Flash memory without occupying MCU RAM. This way, menu items are limited only by Flash memory, not by RAM.

As you can see in the code, the menu structure is simple, and there are many ways to optimize it. Feel free to do so. First, let us decide how many Menu items we are going to have. According to my example, there are four menu items:

//Menu Strings in flash
//menu 1
const uint8_t MN100[] PROGMEM="<<One Led>>\0";
//menu 2
const uint8_t MN200[] PROGMEM="<<All ON/OFF>>\0";
//menu 3
const uint8_t MN300[] PROGMEM="<<Auto scroll>>\0";
//menu 4
const uint8_t MN400[] PROGMEM="<<Blink All>>\0";

Then we have to describe submenus:

//SubMenu Strings in flash
//menu 1 submenus
const uint8_t MN101[] PROGMEM="R ON\0";
const uint8_t MN102[] PROGMEM="G ON\0";
const uint8_t MN103[] PROGMEM="B ON\0";
//Submenus of menu 2
const uint8_t MN201[] PROGMEM="All ON\0";
const uint8_t MN202[] PROGMEM="All OFF\0";
//Submenus of menu 3
const uint8_t MN301[] PROGMEM="Left\0";
const uint8_t MN302[] PROGMEM="Right\0";
//submenus of menu 4
const uint8_t MN401[] PROGMEM="Blink Fast\0";
const uint8_t MN402[] PROGMEM="Blink Slow\0";

Then we create array pointers to menu strings stored in a flash:

const uint8_t *MENU[] ={
		MN100,	//menu 1 string
		MN200,	//menu 2 string
		MN300,	//menu 3 string
		MN400	//menu 4 string
		//add more if more menu items are used
		};
const uint8_t *SUBMENU[] ={
		MN101, MN102, MN103,	//submenus of menu 1
		MN201, MN202,		//submenus of menu 2
		MN301, MN302,		//submenus of menu 3
		MN401, MN402		//submenus of menu 4
		//continue with other menu submenu strings
		};

These pointers are used to call the menu string when the menu/submenu item is changed.

Then we need to describe menu structure with number constants indicating how many menus and submenu items there are:

const uint8_t MSTR2[] PROGMEM ={
	4,	//number of menu items
	3,	//Number of submenu items of menu item 1
	2,	//of menu item 2
	2,	//of menu item 3
	2	//of menu item 4
		//enter further submenu items
	}; 

This array is also stored in Flash memory.

Last thing are functions. I used pointer to function with similar names:

//Functions for each menu item
void func101(void);
void func102(void);
void func103(void);
void func201(void);
void func202(void);
void func301(void);
void func302(void);
void func401(void);
void func402(void);
//...add more functions if more menu items are used

It is easy to use a function pointer, which can be changed during menu item change. To make things easier to manage, I have created  an array of function pointers in Flash

const FuncPtr FuncPtrTable[] PROGMEM=
    { 	func101, func102, func103,	//functions for submenus of menu 1
		func201, func202, 	//functions for submenus of menu 2
		func301, func302, 	//functions for submenus of menu 3
		func401, func402	//functions for submenus of menu 4
					//further functions...
		};

By using this function array, I can easily point to required function with single code line:

FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);

MFIndex(MN.menuNo, MN.subMenuNo) function returns array index according to menu and submenu number.

Leave a Reply