Projects Research & Design Hub

Designing a Display System for Embedded Use

Written by Aubrey Kagan

Noritake Notes

In this project article, Aubrey takes us through the process of developing an embedded system user interface subsystem—including everything from display selection to GUI development to MCU control. For the project he chose a 7” Noritake GT800 LCD color display and a Cypress Semiconductor PSoC5LP MCU.

(This article is Part 1 of a two part series. Read Part 2 here.)

Years ago, I was working on a project that used a hierarchical menu system and was built around a simple 4-line alphanumeric display (later 8-line). Creating the menu sequence wasn’t that hard, but when the customer saw it, he wanted it completely changed. That meant a total re-write of the user interface software. Then when he got to work with the system, he wanted it changed again. I decided that there had to be a better way to create and then re-create a menu system. Once I had a solution, I wrote about it in an article in November 2003 (Circuit Cellar 160) [1]. I am told that 15 years later, it’s still a popular download, so I must have touched a nerve. Ever since then, I have wanted to try my hand at a graphical display menu system, to see how to adapt my approach.

I had had to wait until now to find a suitable project. Before I get to the menus, let me tackle some details of the display that I selected. While I hope that my techniques are going to be universally applicable, I suspect that there are significant differences between the multitude of graphical displays.

Selection of the actual display was a roundabout affair. I looked at several from companies like 4D Systems [2], but without any guidance or experience, they all looked super-sophisticated but seemed to require quite a few resources from the host processor. Moreover, it seems clear that the display market changes quickly. And given that I design for the industrial marketplace, I wasn’t sure about the longevity of some of the products. Initially, our customer required operation down to -40°C so that ruled out LCDs. I ended up with a Noritake Vacuum Fluorescent Display (VFD), partly because of the operating temperature range, and also because I am told that Noritake has never discontinued a display. Feature creep and a desire for a larger display led to the customer relaxing the temperature requirement to -20°C. That enabled us to change to a Noritake GT800X480A-C903PA LCD color display (7″ diagonal). For the rest of the article, I’ll abbreviate that GT800 to save ink.

The GT800 seems to aim at a target market between alphanumeric displays and up-market graphical displays. It enables you to get up and running using basic text within minutes, and has some simple graphical tools. However, it seems far less sophisticated than the other displays that I investigated. I don’t know if my impressions are accurate. Although the Cypress Semiconductor PSoC5 has significant memory resources compared to the system I used for my original article, I am sure it’s easy to run into limitations, given everything else it is controlling. As a result, I had no intention of developing fancy graphics or even very colorful displays for this project.

HARDWARE
The VFD I originally selected was also easy to seal against water spray, using a custom gasket around a cut-out in the panel. But when I investigated the touch sensitivity of the GT800, I found that no cut-out was necessary (Figure 1), because it could easily detect a touch through 3-mm thick polyester.

— ADVERTISMENT—

Advertise Here

FIGURE 1 – LCD mounted behind transparent display panel. Waterproofing the enclosure requires only sealing screws and standoffs. The 4 visible holes are from a previous experiment with the VFD.

The GT800 module allows for a host interface of I2C, SPI or serial. I opted for serial and adjusted the baud rate to 115,200—which meant jumpers installed on J5/J6 and J3/J4. I designed an interface board that mounted on the back of the display, to allow communication using RS-232 interface levels—a MAX232 type chip that would work at 3.3 V. The LCD display also has many digital I/O links. I added a buzzer to the PCB so that I could raise an alarm or give an indication that a button was touched. It’s driven by two of the digital I/O pins, allowing for different volumes. The logic on the LCD runs at 3.3 V, but the LCD itself runs at 5 V. I designed the interface PCB to accept a 5 V input, and used a linear regulator to drop the voltage to 3.3 V.

Finally, I am a great believer and keeping strict control on the operation of external modules. If the display is not responsive, I have provided the ability to activate the display’s hardware reset pin under microcontroller (MCU) control.

SOFTWARE
My system is based on a Cypress PSoC5LP MCU, but I hope that you can adapt it to any MCU. Perhaps the only significant difference between the PSoC and another processor is that the UART on the PSoC allows for independent transmit and a receive buffers. I stretched the transmit buffer to 1,000 bytes so that I could transmit a fair number of bytes without having to use software to monitor the transmit status continuously. The UART also has built-in flow control using RTS and CTS, which is also implemented on the LCD display.

I am not using a real-time operating system (RTOS), but instead work with what’s called “cooperative multitasking” (Figure 2). In this technique, the MCU is expected to control several devices, and so should not be tied up exclusively servicing the display. You can find MainDisplay1.c, the “phase” associated with driving this display, on Circuit Cellar’s article code download page. I have tried to keep everything needed in this one file, but some externals are in the memory1.h file. One other thing: Timing is achieved through a down counter that is decremented in another phase. It stops decrementing when the count gets to zero. MainDisplay1 is just a simple program to allow me to get to know the display and other components. A state machine is implemented in the function UpdateMainDisplay. The portion of code between the start and the statement switch (cMainDisplayPhase) statement is always executed when the function is called, and is there to analyze any serial messages received (via  interrupt—see
CY_ISR(isrDisplayTouch) in the listing). The value of the variable cMainDisplayPhase determines the state of the display interface. While waiting for some event to happen, cMainDisplayPhase does not change state. When the sub-process is completed, its state value is modified, and the next time UpdateMainDisplay is called, the next sub-process (state) is started.

FIGURE 2 – This shows my implementation of a cooperative multitasking system. Each “phase” can be broken up into subphases or “states” in exactly the same way.

You will see in the code that the system will call a function, and if the action is not complete, the function will return a value indicating this; the next step will be held up pending the next time the function is called. Meanwhile, the processor can handle some other task.

FUNCTIONS
To allow multiple actions to be executed—for instance setting character size and character color—I created a buffer array entitled cCompositeMessage. It’s “composite” because it can be made up of several messages, including those derived from both fixed (“const”) messages and variables. On command, this buffer is emptied one byte at a time via the UART as handled by the built-in function on the micro. The following functions form the heart of communication with the LCD module:

void StuffSquirtBuffer (uint8 cFirst, uint8 cNumberOfBytes, char cCharTransferBuffer[])

This function loads the transmit buffer with multiple messages. A non-zero first parameter will initialize the load process, the second parameter defines the number of bytes in the message, and the third is the source array of the transfer. I had to add the number of bytes because 0x00 is a valid data message, and the C library function strlen would provide erroneous results to the length of the string array.

void ConstantStuffSquirtBuffer (uint8 cFirst, uint8 cNumberOfBytes, const char cCharTransferBuffer[])

— ADVERTISMENT—

Advertise Here

This is similar to the StuffSquirtBuffer function above, except that it transfers constant messages. I found it difficult to write a procedure that could handle both const char and char, so I split the functions. No doubt some C guru will provide me with a trivial answer.

uint8 cSquirt (uint8 cNumberOfBytes,const char cTransmitBuffer[])

cSquirt primes and then pumps out a constant message. As described above, it returns a non-zero value when the serial transmission is complete.

uint8 cSquirtCompositeBuffer(uint8 cNumberOfBytes,char cTransmitBuffer[])

This is similar to cSquirt, but it takes a built buffer array (as opposed to only a constant message) and squirts it out of the UART. As described above, it returns a non-zero value when the serial transmission is complete.

I have defined fixed messages as constant arrays. For example:

const char cTextSizex3x3[10]={0x1f,0x28,0x67,0x40,0x03,0x03};
const char cBackGroundColourWhite[10]={0x1f,0x28,0x67,0x51,0xff,0xff,0xff};
const char cBackGroundColourGreen[7]={0x1f,0x28,0x67,0x51,0x00,0xff,0x00};
const char cBackGroundColourRed[7]={0x1f,0x28,0x67,0x51,0xff,0x00,0x00};
const char cBackGroundColourBlue[7]={0x1f,0x28,0x67,0x51,0x00,0x00,0xff};

These are the byte sequences needed for the commands to the display module. There are many more, which you will find in the MainDisplay1 listing.

OBSERVATIONS
As a method of understanding the different operations, I created a simple user interface that will end up looking like Figure 3. I will note different characteristics and limitations as I discuss the features, but there are some overall observations that don’t fall into obvious categories.

FIGURE 3 – Shown here is the keyboard interface used in developing my understanding of the display.

Interfacing to the GT800 is rather like a write-only memory. There is no confirmation that anything has been written, so you have no verification that the display module is even working. When you write anything to the display (including text), it is immediately converted to the graphical pattern. If you try to read it back, you will only get the bit pattern.

Two issues arise from this. The first occurs if you have a menu structure with lines of different selections that can scroll up and down, and the user selects one of them by touch. Even if you were to preface a line with a number—for example “3. Set Time”—it is difficult to read back the line at the touch location and know from that data which selection was made. You have to keep a record within the MCU which lines are on the screen and where they are located. There is a possible workaround by introducing a bit of code by using a custom character as the first character of the message. By making the text color of that character the same as the background, it would become invisible to the user. But that would mean creating as many custom characters as the maximum number of menu entries. For now, I’ve chosen not to develop that idea.

The second issue is that if you would like to keep tabs on the operation of the display, you have to find some way of convincing yourself that all is well. The only way I can think of is to use a command that would trigger a response. Because I am going to need many more I/O lines on the display module, I will hardwire some of the output pins back to the inputs, periodically write a pattern to the outputs and see whether I can read the pattern back correctly. If the communication fails, I will then force a hardware reset on the GT800. I should mention that it takes in the order of 1.5 seconds for the GT800 to start to respond to commands over the serial port, once the hardware reset line is deactivated.

There is no clear way to terminate an unfinished instruction. Noritake suggested to me: “… the safest way to get back control if the host has lost its way, is to send a number of ‘safe’ single-byte commands, such as 0x0b (home position). To be absolutely sure, send as many 0x0b bytes as the ‘worst case scenario’ for the commands that the host uses. The host can verify that command control has returned by issuing a command that generates a response and checking for the response”.

FOUR SUB-SCREENS
Aside from the main screen, theGT800 will allow another four sub-screens of programmable size and location, with the ability to treat each individually. This seems to be quite promising in future development. It seems logical to me that creation of a sub-screen (“window” in the documentation) would be linked to a frame (“box” in the documentation) around the perimeter, to graphically emphasize the window. Creation of a window [3] requires the pixel coordinates of the top left corner and the x and y size of the window. The instruction to draw the box [3], however, uses the pixel coordinates of the top left corner and the pixel coordinates of the bottom right corner. This tripped me up the first time!

Several effects that can be achieved on the display—obviously blanking, another is a “curtain” opening and closing and so forth. Although there is a blinking effect, I found it unusual that this effect only applies to the screen as a whole and cannot be applied to a character, word, line or even a sub-screen.

— ADVERTISMENT—

Advertise Here

Setting a parameter like color or text size is done once and then stays at that setting until changed; thus, you don’t need to set the color of each letter individually. It is not possible to align text, so that positioning within something like a button must be part of the program. One other quibble: It is not possible to have an automatic visible/invisible cursor.

The touch screen has two modes of operation. The first simply identifies the coordinates of the touch. The second seems to be extremely advantageous. It will allow you to create keyboards of differing numbers of buttons of programmable size, and will identify each key as it is touched. Up to four different keyboards can be switched in and out.

There are some basic graphics tools. As I mentioned, it is possible to create any pixel pattern, but I am not going to avail myself of that. Some higher-level functions allow you to draw a line or a rectangle, and I am going to use that to outline the buttons on a keyboard. There is a “gotcha” here, at least for me. When you use the Line/Box Pattern Draw [3], the coordinates used are, like text, relative to the selected User Window.

SCREEN GENERATION
Because it isn’t built into the display, I thought it would be nice to have the ability to flick to scroll it up and down. I will discuss this later in the article.

Now let me take you through MainDisplay1. There is an interrupt routine isrDisplayTouch that is invoked every time a serial message is received from the display. The heart of the display interface is the function UpdateMainDisplay. This function is called periodically as part of the multitasking of the overall program. Every time the procedure is called, it interprets whether a flick has been seen. At this point we can gloss over it. Writing information out to the display is done in different steps determined by a state counter used in the switch (cMainDisplayPhase) statement. This is the mechanism that allows the software to wait for completion of an action concerning the display, while still servicing other functions of the overall program.

The screen is first initialized in cases 0 and 1. Case 2 creates the outlines of the buttons using the box pattern drawing command. I chose to use constant values, rather than calculated values, for the borders of the keys, because the coordinates are used for the button outlines, the buttons themselves and the text within the buttons, and I wanted to economize on execution time.

Cases 3 through 6 configure the touch screen for 6 × 10 keys. The first row actually allows for keys in the text display at the top of the display, but they are not read in the software. This keyboard layout was an early decision, and could be improved on at a later stage. This format automatically sizes the buttons identically. It also sets up the way the number of simultaneous touches to a single touch. Incoming serial messages about the switch activation are received on an interrupt handler, which I will describe later.

Cases 7 to 9 set up the text color and size, and then the actual text into the center of each touch button, excluding the top row.

The message window at the top is set up from case 10 to case 15. I wanted to indicate whether the keyboard was set to uppercase or lowercase (which is toggled by touching the “SHF” key). To indicate the status, I created two different cursor symbols, resembling a “U” for upper case and an inverted “U” for lowercase. Case 16 is where these characters are created.

Once set up, the UpdateMainDisplay procedure moves to the case set to 50 (just a convenient number). In cases 50 and 51, the sub-window is prepared for data.

The display keyboard encoder is configured (Switch Matrix mode, Multi-Touch mode) to generate a 4-byte serial message, both when the switch is touched and when the finger is removed. The serial input interrupt procedure detects which action has been seen and only flags the touch event. There is other code to help with detection of the flick action, but we will deal with that advanced action later.

The code in case 52 requires a key touch. When it is seen, it bumps the state counter cMainDisplayPhase to 53. In case cMainDisplayPhase = 53, the software determines if it is data being keyed on or whether a flick has occurred. If it is data, then it is prepared for the display, and the revised message is rewritten to the display from case cMainDisplayPhase=50. If a flick is detected, cMainDisplayPhase is bumped on to 53. The rest of the flick action is not implemented for this article, and the results are cleared and the input scan restarted.

IMAGES
It’s possible to generate an image, bit by bit, in the software, but that will consume a large part of program memory. It is possible to store several images in the display’s non-volatile memory (called FROM) and then fetch one or more and place it anywhere on the display. I decided to use my employer’s logo. The first step is to get the graphic into a .bmp format.

The GT800 has a USB interface, so you can access the different memory areas with a PC. Noritake provides an interface program called GT Packer. You will need to set it up until you arrive at something like Figure 4. By checking the “on GT” checkbox, you can see how the image looks on the physical display. If the image you choose is too large, you can scale load the file and then Resize it in Microsoft Paint or some other graphic editor. You download from the PC to the FROM address you set up in Start Address, and then click on the Write Display button. It is possible to tweak image presentation and file size by playing with the color and image formatting. Once done, you need to note the start address that you selected, the Image Format and the “Compensated Size” shown in the Preview window, as this information is used in command to transfer the image from FROM to the display.

FIGURE 4 – Noritake’s GT Packer problems enables you to visualize the display, download it onto the display for evaluation, and finally store in on the non-volatile memory on the display over a USB port.

Back in my code, you will notice that I test in cMainDisplayPhase =18 for an external switch (merely a development tool) that simply bypasses the keyboard interface by vectoring to the case=60. Once there, the micro simply fetches the image and plonks it down at the cursor location—no muss, no fuss, but also no help like justification or scaling. You can cut off a piece of it, but you can’t make it bigger or smaller.

One slight inconvenience using GT Packer is that the display must be disconnected from the host MCU and must have a supply of 5 VDC independent of the USB connection. It seems to me that in a production, the first thing would be to solder in the headers. The next step would probably be to test the display using GT packer, and then down load all the images, because that only needs to be done once and is stored in non-volatile memory.

KEYBOARD
As I mentioned before, it is possible to configure the touch aspect of the screen all the way down to the pixel level, but it’s also possible to configure buttons of different sizes and a matrix of switches. It’s also possible to save up to four different switch configurations with different modes. In single-touch mode, the signal is sent continuously while the button is pressed. In multi-touch mode with n=1, data are sent once when pressed and once when released.

The actual switches do not have a built-in outline, so you have to use the box-drawing tool or some other graphic to indicate the presence of the switch to the user. Defining and changing these set ups can take time and consume resources of the host micro. To save some of those, it is possible to save the graphics in the flash memory on the display, but not the button set-up. You can create up to four macros in flash memory as well, so the switch configuration could also be stored.

There is a separate macro language—and compiler for it—and both the macro and the graphics would require additional documentation and procedures to maintain. I’ve opted not to go that route. I also settled for a single keyboard configuration, consisting of a matrix of 6 rows by 10 columns that are permanently enabled. I then use the software to contextually decide if the key is active or not.

FLICK CAPABILITY
Running a finger along the display triggers the activation (and release) of a series of buttons. The software (in MainDisplay1) looks for several changes of readings in the rows to detect vertical motion, and stores the sequence in a period of time. If there are more than two in the period, a flick-detection cycle is initiated and a record of the sequence is stored. If the row number goes from high to low, the finger is moving up, and the converse. The number of rows measured determines how far the finger has moved, and the time taken from the time the flick is detected to the last button seen gives an indication of the speed at which the finger was moving. Combining these two measurements could be used to determine the ballistic response of the list on the display—how fast and how many rows it changes.

The idea of this article began as a means of documenting the early part of the project, because I found the Noritake documentation to be a little short on examples for the GT800. But now it is also a necessary precursor to my next article where I discuss how I created a menu system within the constraints of the Noritake graphical display. That article is scheduled to appear in the January (342) issue of Circuit Cellar. I am grateful to Noritake Technical Support for answering many, many emails with accurate information and great advice. I consider Noritake support to be the gold standard of the industry.

This article is meant to work as a standalone guide to using the Noritake GT800X480A-C903PA. Based on the experience on my original 2003 article, the next article part is going to be rather daunting. Explaining the concepts and software constructs used can be challenging. Come back next month and see how I’ve done!  ( Find the next article here )

RESOURCES

References:
[1] Hierarchical Menus in Embedded Systems, Circuit Cellar, Issue #160, November 2003
[2] gen4 Display Module Series 7.0” Diablo16 Integrated Display Module datasheet
[3] GT-C9xxP series “General Function” Software Specification (requires registration)  GT800X480A-C903PA Hardware Specification (requires registration)

Cypress Semiconductor | www.cypress.com
Noritake | www.noritake-elec.com

Links to more of Aubrey’s publications on/in Circuit Cellar, Planet Analog and Embedded.com at are available at: http://bit.ly/2m26MJB

PUBLISHED IN CIRCUIT CELLAR MAGAZINE • DECEMBER 2018 #341 – Get a PDF of the issue


Don't miss out on upcoming issues of Circuit Cellar. Subscribe today!

 
 
Note: We’ve made the October 2017 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.


Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Become a Sponsor
Engineering Manager at | + posts

Aubrey Kagan has worked in electronics for more years than he cares to remember. He is currently Engineering Manager at Emphatec, an industrial electronics design house in Markham, Ontario. He has written many articles for Circuit Cellar over the past 25 years as well as a book Excel by Example based on three of those articles. Aubrey was one of the “notable contributors” interviewed in Circuit Cellar’s 25th Anniversary issue. He has also published several design ideas as well as numerous blogs covering many aspects of electronic design. You can find a list and links to more of his publications on the Circuit Cellar article materials webpage. He can be contacted at [email protected]