Using a Hardware Abstraction Layer with EVE
With a Hardware Abstraction Layer (HAL), you can make enhancing your product with a graphical touchscreen even easier.
When creating software for complex peripherals, it is important to ensure that the code is readable and portable. This article illustrates the way in which a Hardware Abstraction Layer (HAL) can offer a solution when using EVE as a graphic peripheral with a variety of MCUs.
The Embedded Video Engine (EVE) devices from Bridgetek are a family of display controllers allowing a touch screen colour LCD to be easily added to existing and new devices. The FT81x series are the latest members of the EVE family, featuring higher resolution and increased memory. The high level of integration allows display, touch and audio capability to be added with minimal external components. Likewise, the object-oriented display generation and innovative touch features make software development fast and easy with minimal overhead on the attached processor. Even devices based on low-cost MCUs with limited resources can now utilise an attractive and user-friendly interface.
The HAL allows the Main Application to focus on creating the screen content itself using the command syntax defined in the EVE Programmers Guide, rather than the associated lower level byte transfers over SPI. An additional advantage is that the lowest level layer can provide all of the MCU-specific operations and so the upper layers can be consistent over a range of MCUs.
Figure 1 illustrates the use of several layers under the main application to provide abstraction from the hardware.
Figure 1 – Example HAL layers for EVE
The EVE Interface
Figure 2 provides a simplified summary of the data paths when interfacing to EVE over SPI. The MCU configures EVE and creates display content via the RAM_DL, RAM_CMD, RAM_G and Registers. Having access to these different blocks over SPI allows the application to be optimised using various graphics techniques. Because each of the blocks has their own ways of being addressed and managed, the HAL can take any complexity and overhead involved away from the main application, making it much more intuitive and maintainable.
Figure 2 – Simplified diagram showing data paths and memory blocks
Display List (via RAM_DL)
One of the features which allow EVE to be capable yet cost-effective is the lack of frame buffer in the device. Instead, the Graphics Processing Unit (GPU) parses a list of instructions in real time from an 8K Display List RAM (RAM_DL) as it generates each line of the display. An example of a short display list is shown in Figure 3, which will result in the screen being cleared to a black colour and a red dot drawn on the screen. Each command is 4 bytes in size including parameters.
Figure 3 – Simple Display List
Internally, the EVE device uses two 8K buffers in a foreground-background fashion to provide clean transitions when updating the display; the GPU parsing one list and the other list mapped into the user memory where the next display can be created. A Swap register controls the swapping of the two lists.
The GPU will continue to refresh the screen until the next Swap occurs and so the attached MCU does not need to continually write data to the EVE device to retain the display.
The Display List can accept GPU commands directly (see the Display List Commands section of the Programmers Guide). It can also refer to raw image data such as bitmaps and fonts stored in RAM_G allowing the GPU to display these. In addition, it can include instructions to assign tag numbers to items on the screen. The user can then determine easily via the touch engine registers which item is being touched instead of having to compare coordinates manually.
Co-Processor (via RAM_CMD)
The Co-Processor can assist with tasks which cannot be directly handled by the GPU. Some examples include:
- Creating display list entries to draw widgets such as gauges and text from GPU primitive
- Inflating compressed (e.g. JPEG and PNG) image data into a raw format to RAM_G, which the GPU can then display
- Writing registers in the EVE memory map. For example, swapping display lists and updating touch transform values after screen calibration
It is an important concept that the RAM_DL defines the final display content. The co-processor can help to write the required GPU shapes to RAM_DL to form a widget but does not directly display the widget. Likewise, the co-processor converts images to a format supported by the GPU and stores them in RAM_G but does not directly display them.
The co-processor can accept both co-processor commands and GPU instructions via its FIFO, with the latter being passed through directly to the display list. The application can, therefore, create a screen consisting of both widgets and basic graphics operations entirely through the co-processor.
Commands and any associated parameters and data are fed to the co-processor via a 4K circular FIFO (RAM_CMD). The circular nature allows more than 4K to be sent. The buffer is managed by two pointers.
- REG_CMD_WRITE is written by the user program to point to the end of the instructions to be executed
- REG_CMD_READ is written by the co-processor and will increment toward REG_CMD_WRITE as commands are executed
- When REG_CMD_WRITE == REG_CMD_READ the buffer is empty
The MCU should treat the RAM_CMD as a true circular FIFO and each new command should be written at the next location after the previous one, rather than beginning at offset 0 each time. The MCU should keep a local count of the size of each command so that the write pointer can be advanced accordingly to point to the end.
Figure 4 illustrates the creation of a very simple display list using the co-processor, showing how the read and write pointers are used.
Figure 4 – Simple Co-Processor example
General Purpose Graphics RAM (RAM_G)
The RAM_G memory is used to hold items such as raw image and custom font data. Instructions in RAM_DL can specify the properties (format, height, width etc.) and assign handles to these items, allowing them to be displayed via the display list.
As the memory is not used for frame buffering, the entire memory (1MByte on FT81x, 256KByte on FT80x) can, therefore, be used for general purpose image and font storage.
The memory can be written and read via standard SPI transfers and a burst of data can be written to sequential addresses by keeping the Chip Select line low, which is useful for streaming image data to the RAM_G.
This part of the memory map includes registers to configure EVE, set timing parameters for the attached display, and for interfacing with the touch and audio features. The registers for the RAM_CMD pointers and RAM_DL swap are also mapped here. They can be written and read via standard SPI transactions and also by the co-processor.
EVE can be interfaced via standard SPI transfers (the FT81x also supports Quad SPI offering higher throughput). EVE uses a specific message format for writing and reading across the entire memory map. This consists of a three-byte address value which specifies the memory location. This is sent after Chip Select is asserted, and also includes two bits used to indicate whether the location will be read or written to (see the bits highlighted in red). The subsequent bytes clocked on the SPI bus will carry the data to be written on MOSI or have the data being read returned over MISO. Burst writes can be carried out to subsequent sequential addresses by keeping Chip Select low.
There are also a small number of special host commands for EVE configuration (for example clock mode and low-power SLEEP mode) which are directly interpreted by EVE. These are encoded within a 3-byte SPI transfer with no addressing required.
Figure 5 illustrates the SPI transfers used for reading, writing and host commands.
Figure 5 – SPI Transfers
Creating the HAL
The HAL library can be written to abstract the user interface designer from the lower level aspects discussed previously. Figure 6 below follows the structure summarised in the Introduction (Figure 1) and provides one example of how the calls from the main application are translated through the HAL layers to low-level hardware operations.
The API and EVE layers will often be consistent across all applications. Once the MCU layer has been created for the desired SPI host platform, the designer can focus on creating their interface within the Main Application layer.
Figure 6 – Example Main Application and HAL code layers
In its simplest form, the high-level APIs provided by the HAL in an EVE application should allow the developer to create display lists, execute co-processor lists, load image and font data, and read/write registers and memory locations from the main application code layer. The layers below avoid the need to worry about the specifics of the MCU’s SPI peripheral and of the EVE SPI protocol.
The main application can then be written using the EVE Programmers Guide as a reference or using a tool such as the EVE Screen Editor which allows graphical positioning of the screen content and outputs the list of co-processor commands. Additional layers could further abstract the main application allowing programming to be carried out entirely using graphical flow tools such as the EVE Screen Designer.
This layer takes the commands and associated parameters and manipulates them into the 8-bit, 16-bit and 32-bit values used by the EVE layer. It also handles the operation of the co-processor FIFO and addressing. This layer uses a header file providing macros for bit-shifting the parameters into the correct positions. For example, VERTEX2F(x,y) produces a 32-bit value combining the command with the X and Y values using the definition in the header file.
This layer provides the functions to implement the EVE SPI protocol. It provides functions for the addressing and data transfer used when writing to and reading from the EVE memory. It also provides functions combining the address and read/write operation for reading and writing 8/16/32-bit registers.
The MCU layer is the only one which deals directly with the SPI and GPIO registers on the MCU, which allows the three layers above to be portable across MCU types. EVE requires a minimum of
- An SPI Master peripheral supporting Mode 0 and MISO, MOSI and SCK lines
- A single chip select line (may be GPIO or part of the SPI module depending on MCU)
- A GPIO line to control the Power Down pin of EVE
The MCU layer can provide configuration functions for setting up the MCU’s SPI and GPIO. In this case, it provides a basic SPI read/write byte function and a set of functions for controlling the Chip Select and Power Down via GPIO.
The EVE family of display controllers are a great solution for adding an attractive and intuitive touchscreen display interface to devices. By taking much of the workload away from the attached MCU, the addition of this functionality can often be achieved with minimal impact on the MCU which is controlling the other parts of the final product.
Creating a Hardware Abstraction Layer when using devices such as EVE brings many advantages including allowing the User Interface designer to focus on the screen content itself, keeping the code well-structured and easily maintainable, and allowing greater portability across MCU platforms.
The HAL can be implemented in a variety of programming styles and languages, and providing different levels of abstraction, depending on the programmer’s preference. Bridgetek has a wide range of example code which provides a good starting point when developing a HAL for EVE.
Founded in 2016, Bridgetek supplies highly advanced ICs and board level products to meet the exacting demands of a constantly evolving global technology landscape. The company’s Embedded Video Engine (EVE) graphic controller ICs each integrate display, audio and touch functionality onto a single chip, thereby dramatically reducing in the time period and bill-of-materials costs associated with developing next-generation Human Machine Interface (HMI) systems. These are complemented by its highly-differentiated, speed-optimised microcontroller units (MCUs) with augmented connectivity features.
CommentsAdd a comment
I've been using FT80x for a while now. Its been working reliably without any issues and indeed has reduced development time drastically. Now I intend on taking a next step i.e. with the FT813 clubbed with PIC32MX470. Since i write my custom drivers, I need clarity as to how complex would it be to migrate my current designs from FT80x to FT81x. I'm also considering trying out the Quad Spi using the PIC32MZ. So could anyone explain the challenges I would have to be prepared for. Also some precautionary suggestions would be a great deal of help. I would soon be sharing my progress on Git, so help is appreciated.