A Graphical User Interface for the PIC18F

 

For a long time, I have wanted to give my PIC projects a decent user interface, something beyond a few buttons and flashing LEDs.I recently discovered a very low cost color LCD display available for $15 at www.sparkfun.com (part number LCD-00569) and for as little as $10 on ebay.

 

 

Unlike the picture here, the display shows very crisp graphics and colors.

 

The resolution is 130 x 130 pixels, each individually addressable with 8 or 12 bit colors.The display is small, having outside dimensions of 1.35" x 1.58".The size of the screen is only 1.2" x 1.2", but it is large enough to display menus, pictures and text messages.

 

This document describes the PIC user interface that I have created.I've made the source code, along with the schematic / pcb layout open source.All of the files can be downloaded below.

 

To see how the user interface looks and works, there is short movie on YouTube:

http://www.youtube.com/watch?v=TDNtWvRc730

Interfacing the LCD:

Connecting the display to a PIC is fairly straight forward, requiring a couple of voltage regulators and a level shifter.The LCD has a built in LED backlight that runs on 7 volts.The display uses 3.3v logic and requires a 3.3v regulator to power it.I use PICs that run on 5 volts, so a chip that shifts the TTL logic levels down to 3.3v is needed.

 

In addition to the display, my user interface requires four buttons.Here is how the LCD and buttons connect to the PIC:

 

 

The PIC is clocked with a 10Mhz crystal and configured internally with a 4x PLC, resulting in a 40Mhz clock rate.

Circuit Board:

Electrical connections are made to the LCD display via a tiny 10 pin connector.The mating connector can be purchased from Sparkfun (part number LCD-00570) for $2.

 

Unfortunately this surface mount connector is also very small, having just .5mm spacing between the leads.This type of connector requires that it be soldered to a circuit board.I choose to design my own PCB, but if you don't want to, you can buy a carrier board from Sparkfun.The picture on the right is of a breakout board from their website.It is a little more money, but it is assembled and includes the display.

 

I designed my board using the free schematic and PCB layout tools from www.expresspcb.com.After laying out the circuit, I order the bare boards directly from ExpressPCB, using their $51 MiniBoard service.The picture below shows my board after I assembled it.Mounted on the back of the board, behind the LCD, are 3.3 and 7 volt regulators, and the 74HC4050 level shifter.

 

 

You will find a PDF schematic of this board here:

Schematic page 1

Schematic page 2

 

Here are the schematic and circuit board files in the ExpressPCB format:

Schematic and PCB

 

To open the schematic and PCB files, you will need the software from ExpressPCB.It free from their website:

http://www.expresspcb.com/ExpressPCBHtm/Download.htm

Developing the Software:

The software presented here was developed using Microchipís MPLAB Integrated Development Environment.All of the code is written in C, using the free version of Microchipís C18 compiler.To program the chip and debug, Microchipís ICD2 was used.

 

The complete PIC source code for the user interface, including an example program can be downloaded here:

 

PICUserInterface.zip

 

It is recommended that these files be unzipped into the directory c:\PICUserInterface.By placing the files here, all of the path names in the project will be preserved.

 

Note: The MPLAB project is configured for:

*          General / Diagnostics level: Errors only

*          General / Default storage class:Auto

*          General / Enabled integer promotions unchecked

*          Memory Model / Small code model (<= 64K)

*          Memory Model / Large data model (all RAM banks)

*          Memory Model / Single-bank stack model

*          Optimization / Custom / All checked except Procedural abstraction

Writing to the Display:

The majority of the user interface is written in two .C files:LCDDriver.candUserInterface.c

 

LCDDriver.c contains the routines that write directly to the screen.They include functions for drawing pixels, lines, circles, text and bitmaps.Text can be drawn using one of five fonts and justified left, right or centered.

 

 

 

It is possible to configure the LCD for different coordinate systems.I choose to mount the display with its connector tab on the top.I setup the coordinate system with 0,0 in the upper left corner, having X move down and Y move to the right.The display does not show the X=0 and X=131 columns, or the Y=0 and Y = 131 rows.This results in the useable coordinates running from 1 to 130.

 

 

†††††††††††††††††††† ----------- Y ---------->

††††††††††††††††††††††††††††††††††††† ___

††††††††††††††††††† _ _ _ _ _ _ _ _ _|_ _|_ _

†††††††††††††††††† |†††††††††††††††††††††††† |

†††††††††††† (0,0) |-------------------------| (131,0)

††††††† |††††††††† |†††††††††††††††††††††††† |

††††††† |††††††††† |†††††††††††††††††††††††† |

††††††† |††††††††† |†††††††††††††††††††††††† |

††††††† |††††††††† |†††††††††††††††††††††††† |

††††††† |††††††††† |†† Nokia 6100 display††† |

††††††† X††††††††† |†††††††††††††††††††††††† |

††††††† |††††††††† |†††††††††††††††††††††††† |

††††††† |††††††††† |†††††††††††††††††††††††† |

††††††† |††††††††† |†††††††††††††††††††††††† |

††††††† |††††††††† |†††††††††††††††††††††††† |

††††† \|/†††††††† |†††††††††††††††††††††††† |

†††††††††† (0,131)-------------------------(131,131)

 

At a low level, the easiest way to write to a graphics display is to specify the X and Y coordinates of a pixel, then the pixel value, however this method is very inefficient.To make writing to the screen faster, this LCD uses an auto increment and wrap feature.It works by first setting the upper left coordinate, then the lower right.Next you send all of the pixels values in that block, it automatically advances the coordinate with each pixel that you give it.

 

The display uses the RAMWR command to auto increment and wrap.My initialization code configures this command to write pixels in the Y direction, going from top to bottom, then wrapping left to right. This choice was made to best support drawing fonts having variable character widths.(Note: Because of this wrapping method, bitmap data should be stored in arrays with the first byte representing the pixel in the upper left corner, then advancing downward and to the right.)

 

 

This document does not go into detail explaining the low level communications of the Nokia display.You can get a better understanding by reviewing the source code presented here.For additional information, James Lynch has written a great tutorial.It is available at:

Nokia 6100 LCD Display Driver Tutorial

 

 

The datasheet for the Philips controller chip used in the display is also a helpful resource:

Philips PCF8833 Datasheet

The User Interface:

As mentioned earlier, this user interface is built around an LCD display and four push buttons. Mounted on the right side of the display are the Up and Down buttons.Two more buttons are mounted below.The intention of this user interface is to make adding menus, sub menus, messages boxes and sliders easy to any PIC18F device.

 

The user interface divides the display into three sections, each section is the full width of the screen.

 

Along the top is the TitleBar.It displays things like the name of the selected menu, or the name of the application.The subroutine UIDrawTitleBar() is called to draw the TitleBar.

 

At the bottom of the screen is the ButtonBar.The function of the buttons mounted below the LCD change, so the ButtonBar is used to label what they do.Typically the left button is labeled Select and the right Back.The routine UIDrawButtonBar() is called to draw the ButtonBar.

 

The middle of the screen is the DisplaySpace.This is where menus, message boxes, configuration screens, along with the application's main displays are shown.

 

Here you can see examples of a menu, message box and a slider drawn in the DisplaySpace:

 

 

The code that supports the user interface (menus, messages boxes, Ö) is in the file UserInterface.c and is built on top of the LCDDriver.c module.

Creating Menus:

Menus give a user interface a clean and consistent approach to supporting applications that have numerous commands and configuration settings.These menus here can have up to 6 entries.Menus can also have sub menus, providing an easy way to group related commands.For example, the Main menu shown above includes a Settings entry.Selecting it will present a sub menu of settings related commands.

 

Menus are constructed using a table of menu entries.Each entry includes the menu text, along with what to do when the menu item is selected by the user.There are three types of menu entries: Commands, Alternates, and Sub Menus.

 

Calling the function UISelectMenuDisplay() draws a menu in the DisplaySpace.This function is called with a pointer to the table that defines the menu.A typical main menu table might look like this:

 

††††† MENU_ITEM MainMenu[] = {

††† ††{MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Main menu", SelectApplication},

††††† {MENU_ITEM_TYPE_COMMAND, "About", ShowAboutCommand},

††††† {MENU_ITEM_TYPE_ALTERNATE, "Enable sound", SetEnableSoundCallback},

††††† {MENU_ITEM_TYPE_SUB_MENU, "Settings", SettingsMenu},

††††† {MENU_ITEM_TYPE_END_OF_MENU, "", 0}};

 

The first line in the table always specifies what type of menu it is, either a Main Menu, or a Sub Menu.The table's last line marks the end of the table.In between are the menu items.The first and last lines are not displayed.

 

A MENU_ITEM_TYPE_COMMAND entry indicates that a function will be executed when this menu item is selected.A pointer to the function is third field in the entry.

 

A MENU_ITEM_TYPE_ALTERNATE is used somewhat like a Radio Button in a dialog box.It allows the user to select one of a fixed number of choices (such as On / Off,†† or†† High / Medium / Low).Each time the user clicks on this type of menu item, it alternates the selection (i.e. toggles between On and Off, or rotates between High, Medium and Low).The third field in this entry points to a callback function that alternates the value.

 

A MENU_ITEM_TYPE_SUB_MENU entry is used to select a different menu.For example, a main menu might reference a Settings sub menu. The third field in this entry points to the table that defines the sub menu.

 

As described above, the first line in a menu table indicates the type of menu:

 

A MENU_ITEM_TYPE_MAIN_MENU_HEADER specifies the main menu.The second field is the text displayed on the TitleBar when the menu is shown.The third field is a pointer to a callback function executed when the user presses the Back button, indicating that they are done with the menu.Typically this callback function would reselect the device's main application.Note: If the third field is 0, then the Back button is not displayed.

 

A MENU_ITEM_TYPE_SUB_MENU_HEADER is used in the first line of a sub menu table.(Sub menus that are menus that are called from a main menu, or another sub menu)The second field is the sub menu's title (which is displayed on the TitleBar).The third field is a pointer back to the parent menu table (typically this would be the main menu).This is used to reselect the parent menu when the user presses the Back button, indicating that they are done with the sub menu.

Messages Boxes and Sliders:

In addition to menus, there are a few other standard displays that can be shown in the DisplaySpace.

 

A MessageDisplay presents a text message.The function UISelectMessageDisplay() is called with the string to show.The source string can be located in ROM or RAM, as indicated by the StringType argument.If the string is too wide to fit on one line of the LCD, it will automatically be broken into multiple lines.The string can also contain '\r' characters to break the line.The Justify argument specifies left, right or centered alignment.A pointer to a callback function must be given in the CallbackFunc argument.The callback function is executed when the user presses one of the two bottom buttons.The MessageType argument sets how the two buttons are labeled.The choices are:

Back

Yes/No

OK/Cancel

 

 

A Slider allows the users to input a numeric value (such as 0 to 255,or-1000 to 1000).Sliders show a graphical representation of the value that moves as the users presses the Up and Down buttons.Sliders are displayed by calling UISelectSliderDisplay().The MinValue and MaxValue arguments sets the range for the number.The Step argument indicates how much the value is incremented / decremented each time the user pushes a button.The StepAutoRepeat value is added several times per second while the user holds down a button.If the arguments ShowMinMaxValuesFlg and ShowNumericValueFlg are set to TRUE, then the numeric values are displayed in addition to the graphical slider.

Bitmaps and Fonts:

Pictures and fonts are compiled directly into the projectís source code.A choice of five fonts are in the file fonts.c.They all have a variable pitch (i.e. the i character is narrower than the w character).Variable pitch fonts have a much prettier appearance and print more compactly than fixed pitch fonts.

 

The stock fonts are:

Font_Small

Font_SmallBold

Font_Medium

Font_MediumBold

Font_LargeNumbers

 

The LargeNumbers font only includes the characters: 0 1 2 3 4 5 6 7 8 9 - + . % , :

and a space.

 

 

In the example project, the file images.c contains two sample bitmaps, one is a picture of a pretty woman, and the other is an hourglass icon.†† Bitmaps can be saved using two color systems:RGB8 bitmaps represent each pixel with an 8 bit value (rrrgggbb).RGB12 bitmaps require 12 bits of storage per pixel but give a better representation of colors (rrrrggggbbbb).

 

Including many small graphics, such as an hourglass icon, can nicely dress up an application.†† Unfortunately, large pictures should be used very sparingly because of the massive amount of ROM space they require.An RGB12 picture the size of the screen (130 x 130 pixels) eats up 25K of ROM.

 

The source code for the hourglass bitmap looks like this:

 

//

// Image:Hourglass

//†††† Data is packed with 1 pixel in 1 byte, beginning with the upper left

//††††††††† pixel, the pixel order goes down, then to the right

//†††† Byte0 = Bitmap type: 1 = RGB12, 2 = RGB8

//†††† Byte1 = Image width (1 - 130)

//†††† Byte2 = Image height (1 - 130)

//†††† Byte3 = Byte count of image (low byte) (excluding this 5 byte header)

//†††Byte4 = Byte count of image (high byte) (excluding this 5 byte header)

//†††† Byte5 = rrrgggbb pixel 1, (x = 0, y = 0)

//†††† Byte6 = rrrgggbb pixel 2, (x = 0, y = 1)

//†††† ...†† = remaining pixel data

//

rom byte Image_Hourglass[] = {

††††† 0x02, 0x0D, 0x12, 0xEA, 0x00,

††††† 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

††††† 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

††††† 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

††††† 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,

††††† 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,

††††† 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,

††††† 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF,

††††† 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00,

††††† 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00,

††††† 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

††††† 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00,

††††† 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00,

††††† 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF,

††††† 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,

††††† 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,

††††† 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,

††††† 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

††††† 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

††††† 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

††††† 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00};

 

 

The good news is that you do not need to understand the format of bitmaps and fonts in order to use, or to create them.They are generated with a Windows application written to support this user interface.The Windows program LCDBitmapAndFontCreator.exe is shown below.

 

 

The controls on the left side of the program are used to generate the data for pictures.Pressing the Load image file button allows you to select a .BMP, .JPG or .GIF file.Here you can see that a bitmap of an hourglass image has been loaded.The Copy image data to clipboard button converts the image to textual data, then copies it to the Windows clipboard.Add this data to your PIC project by opening a .C file in MPLAB, then selecting Paste from the Edit menu.

 

 

The fonts in the file fonts.c include a reasonable selection of type faces that are practical for this LCD display.It is likely that you will not need any other fonts.However if you want to make your own, you can.The controls on the right side of the LCDBitmapAndFontCreator.exe program are used.Do so by first choosing a font.Then adjust the yellow band in the preview window to select what is captured.Press the Up, Down, Taller and Shorter buttons to set the top and bottom of the yellow band.Click the Left and Right buttons to set the left edge of the yellow band over the left edge of the first character.You can view different text in the preview window by pressing the Sample Text button.For example, previewing lower case characters will help you to insure that the yellow band includes the decenders of a lower case y.Do not worry about the right edge of the yellow band.All of the characters in the ASCII set are automatically generated.

 

One should note that the fonts created with this method are likely owned and copyrighted by Microsoft and should not be used in commercial applications.

 

 

The installation program for the LCDBitmapAndFontCreator Windows applications is available here:

LCDBitmapAndFontCreator.exe

The LCDís Built in Controller Chip:

One of the most appealing qualities of this display is its low cost.It is inexpensive because it is an OEM part, used widely in Nokia cell phones.However, in the words of James Lynch: "The major irritant in using this display is identifying the graphics controller; there are two possibilities (Epson S1D15G00 or Philips PCF8833)".

 

According to Sparkfun, the controller chip used by the display can be identified as follows:

 

Philips PCF8833 compatible controllers

Epson S1D15G10 compatible controllers

Have a red tab on the protective cover of the LCD

 

Marked with one of these:

UMSH-8118FD-1CS

U.R.T. 080109-0010

LM355C11-B

AT-A2-071227-F604-1

GE-12

 

Have a green tab on the protective cover of the LCD

 

Marked with one of these:

EV-LG-0298

73-01824

GMP355C0 0403

C-SNB6C1504.50.21

GE-8

 

The source code presented here is written for the Nokia 6100 LCD displays having a Philips controller.Unfortunately when ordering these displays, either from Sparkfun or ebay, it does not seem possible to specify which controller you want.In my last Sparkfun order, I wanted a display with the Phillips controller so I emailed them first.In my email I asked that they check their stock to tell me "the color of the tab on the LCDís protective cover".In their reply, they indicated Red, so I placed my order.While Iím not sure a red tab will always guarantee the correct result, it has worked for me.

 

 

Looking at the datasheets for the Phillips and Epson controllers, you will find that they are very similar, however there are differences.The software driving the display must know which controller it is writing to.If your display uses the Epson controller, it is possible to modify this software to support it.These are the functions that would need to be updated:

 

LCDDriverInitialize()

LCDSelectColorMode()

LCDSetContrast()

WriteCommandToLCD()

WriteDataByteToLCD()

 

along with the RGB8ColorMap color table and the list of #defines for the controller's command set.For a discussion of the differences between the Philips and Epson versions of this display, see James Lynch's document:Nokia 6100 LCD Display Driver Tutorial

The Unexplained:

In developing the LCDDriver.c module, I found that my display adheres to the Philips datasheet in all ways except for the RGBSET command.I tested this code with two versions of the Nokia 6100 LCD, purchased from two different sources (Sparkfun and the Yallstore on ebay) and found the same results with both.The problem relates to colors.Initially I could only display washed out colors, not getting the full range of brightness.

 

My driver uses the LCD in two different color modes: 8 bit colors and 12 bit colors.The Philips datasheet indicates that it is necessary to setup a color table (using the RGBSET command) with the 8 bit color mode.I found that the documented method of programming this table did not result in correct colors.Through experimentation, I discovered that a 48 byte table (rather than the 20 byte table documented) was needed.I also found it necessary to setup a color table for the 12 bit mode.

 

In LCDDriver.c, the color table is defined in the array RGB8ColorMap[].You will find this array is declared twice.One has the data that works correctly with my displays, the other one is commented out.This table matches the requirements as documented in the Philips datasheet.It may be necessary to use the commented out version for some LCD displays.

Acknowledgements:

In developing the LCDDriver.c module, James Lynch's document Nokia 6100 LCD Display Driver Tutorial was extremely helpful.The line and circle drawing routines were derived from the 1962 algorithms of the famous Jack Bresenham.

 

 

 

 

 

Last edited:7/20/2009