After getting started with developing test code for the ILI9341 TFT LCD I thought I'd continue along the same path and develop the LCD menu that will be used to configure the MIDI controller's settings. This implementation involved several elements - designing the menu layout, processing rotary encoder messages, creating a data structure for storing settings data, drawing to the LCD, and reading and writing to EEPROM - all of which I've described below.
First, here is a quick demo video of the LCD menu in action - using the three rotary encoders to navigate through the menu, change settings values, and showing the data being recalled after powering off the microcontroller:
(On a related note, I've now set up a GitHub repo for the project, which will include all code and design files of the project. I've made a good start on a lot of different elements of the software for the project - some of this I'll describe below, and some I'll talk about in later logs when relevant)
Designing the Menu Layout
As described in my design log of the project, the LCD menu is controlled by a set of three rotary encoders - one for selecting the settings category, one for selecting the category parameter, and one for adjusting the parameter value. Based on this, the most obvious layout choice is a three-column menu, which is the main reason why the encoders are positioned as they are.
Processing Rotary Encoders Message
To process rotary encoder messages I've created a RotaryEncoder class (see header file / source file) that uses the Teensy Encoder Library for processing encoder turns and the Teensy Bounce Library for processing the push switch messages. The classes uses callback functions to provides increment/decrement values. See the controls.h file to see how this class is used.
Data Structure for Storing Settings Data and Metadata
To store settings data as well as settings metadata (e.g. setting data min and max values), I've created a couple of structs:
- ParamData for storing data for a specific parameter
- SettingsCategoryData for storing data for a specific category
I've then created an array of SettingsCategoryData for storing all needed settings data and metadata for the device. This array will be used through the code - for setting the MIDI messages that the MIDI controls transmit, for displaying values on the LCD, and for loading and saving to EEPROM. All code relating to settings is in it's own Settings.h file.
Drawing to the LCD
I've created an Lcd.h file that includes all code for setting up and drawing to the LCD. The basic implementation of the menu is as follows:
- Text is drawn on the LCD using the LCD library's print() a println() functions, using the setCursor() function to position the text. For currently selected text, the text and background colours are reversed to highlight the text.
- All category names, parameter names and parameter values displayed on the LCD are accessed from the SettingsCategoryData array described above.
- If a new 'category' encoder message is received, it increments a 'currently selected category' value and redraws the menu's first column of text to visually update which category is selected, and then redraws the menu's second and third columns of text to show the list of parameters and their values for the new category.
- If a new 'parameter' encoder message is received, it increments a 'currently selected parameter' value and redraws the menu's second and third columns of text to visually update which parameter and value is selected.
- If a new 'value' encoder message is received, it increments the value of the currently selected parameter and redraws the value in the third column of text to visually update the parameter value.
See the Lcd.h file for the implementation of this.
Reading and Writing to EEPROM
In order for the settings to be recalled after the device has been powered off, the settings values need to be stored in the Teensy board's built-in EEPROM memory using the Arduino EEPROM library.
Each parameter value of each category is stored in it's own memory address (which stores a single byte). The memory address structure is grouped and ordered by category, with space for up to 16 parameters for each of the 13 categories. For example, addresses 0-15 and for the parameters of category 1, 16-31 for category 2, 32-47 for category 3, and so on.
All data is read from EEPROM when the Teensy is booted, being put directly into the SettingsCategoryData array.
As EEPROM has a limited write endurance (on Teensy this is 100,000 cycles) I've implemented writing to EEPROM to only happen periodically and only for parameter values that have changed (using a member of the ParamData struct to flag when a value has changed).
See the Settings.h file for the implementation of this.