Set up GUIDRV FlexColor

From SEGGER Knowledge Base
Jump to navigation Jump to search

The GUIDRV_FlexColor driver supports a wide range of display controllers and is likely one of the most commonly used emWin drivers for managing indirectly accessible display controllers. This tutorial shows how to set it up.

Objective of this tutorial

This tutorial demonstrates how to set up the GUIDRV_FlexColor display driver. Given that the GUIDRV_FlexColor driver supports over 60 display controllers, this tutorial aims to provide a general overview of the setup process. However, it cannot delve into the specifics for each individual controller.

Although almost any interface can be used, we will use SPI as a reference.

Requirements

Before starting to set up the GUIDRV_FlexColor driver the user should make sure that emWin compiles without any warnings and errors.

It is mandatory to have a working interface to the display controller (e.g. SPI).

The minimum size of the emWin memory pool is approximately 10 KB of RAM. This should be configured in the file GUIConf.c using the function GUI_ALLOC_AssignMemory(). Although emWin can operate with such a small amount of memory, I recommend using at least 64 KB to ensure there is enough memory available for additional emWin features.

A working initialization sequence is required, as this is not provided by SEGGER. Typically, the manufacturer of the display controller provides an initialization sequence.

We recommend to do all display specific configurations within a file named LCDConf.c and keep a structure roughly as shown below.

Display controller initialization

The initialization sequence must be implemented by the user. A good place to call the initialization is within the driver callback function LCD_X_DisplayDriver(). In this function, the user can respond to the command LCD_X_INITCONTROLLER and call the initialization sequence. This might look as follows:

int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * pData) {
  int r;

  GUI_USE_PARA(LayerIndex);
  GUI_USE_PARA(pData);
  r = 0;
  switch (Cmd) {
  case LCD_X_INITCONTROLLER:
    //
    // Call init sequence
    //
    _InitController();
    break;
  default:
    r = -1;
    break;
  }
  return r;
}

After calling the initialization function, the display controller should be fully initialized and ready to receive pixel data from emWin.

Display driver initialization

The display driver initialization is performed within the function LCD_XConfig() and consists of the following steps:

  • Create a driver device
  • Configure screen size
  • Set communication routines
  • Select function set for desired controller, select whether a cache should be used or not, select the bus width and the color depth
  • Config driver

Below is an example on how such a driver initialization can look like below:

void LCD_X_Config(void) {
  GUI_DEVICE * pDevice;
  GUI_PORT_API PortAPI    = {0};
  CONFIG_FLEXCOLOR Config = {0};

  //
  // Set display driver and color conversion for 1st layer
  //
  pDevice = GUI_DEVICE_CreateAndLink(DISPLAY_DRIVER, COLOR_CONVERSION, 0, 0);
  //
  // Screen size
  //
  LCD_SetSizeEx (0, YSIZE_PHYS, XSIZE_PHYS);
  LCD_SetVSizeEx(0, YSIZE_PHYS, XSIZE_PHYS);
  //
  // Hardware routines (PortAPI)
  //
  PortAPI.pfWrite8_A0  = _Write8_A0;
  PortAPI.pfWrite8_A1  = _Write8_A1;
  PortAPI.pfWriteM8_A1 = _WriteM8_A1;
  PortAPI.pfRead8_A1   = _Read8_A1;
  PortAPI.pfReadM8_A1  = _ReadM8_A1;
  PortAPI.pfSetCS      = _SetCS;
  //
  // Function selection and operation mode (bus, bpp and cache)
  //
  GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66709, GUIDRV_FLEXCOLOR_M16C0B8);
  //
  // Orientation
  //
  Config.Orientation   = GUI_MIRROR_X;
  Config.NumDummyReads = 0;
  GUIDRV_FlexColor_Config(pDevice, &Config);
}

Create a driver device

The first step is to create a driver device. This is the very same with every emWin display driver. The function GUI_DEVICE_CreateAndLink() awaits the following parameters.

  • Desired driver: GUIDRV_FLEXCOLOR
  • Desired color format: GUICC_565
  • Flags: Should be zero
  • LayerIndex: Typically 0
  pDevice = GUI_DEVICE_CreateAndLink(GUIDRV_FLEXCOLOR, GUICC_565, 0, 0);

The return value is a pointer to the newly created device. If the return value is NULL something has gone wrong.

Configure screen size

emWin needs to know the size of the screen. So, we use following functions to set the display size. The virtual size should be set to same size as the real size.

  LCD_SetSizeEx (0, YSIZE_PHYS, XSIZE_PHYS);
  LCD_SetVSizeEx(0, YSIZE_PHYS, XSIZE_PHYS);

Set communication routines

To be able to communicate with the display controller the driver needs to know how. This is done by implementing a couple functions to read and write commands and data via the desired interface. The prototypes of these functions can be found in the GUI_PORT_API description in the emWin user manual.

After implementing these functions simply set the function pointers in the GUI_PORT_API structure. This structure will be used in the next step.

  PortAPI.pfWrite8_A0  = _Write8_A0;
  PortAPI.pfWrite8_A1  = _Write8_A1;
  PortAPI.pfWriteM8_A1 = _WriteM8_A1;
  PortAPI.pfRead8_A1   = _Read8_A1;
  PortAPI.pfReadM8_A1  = _ReadM8_A1;
  PortAPI.pfSetCS      = _SetCS;

Below is an example showing the implementation of the communication routines. Within these routines we simply utilize the SPI module of the MCU. The implementation below is just an example and the missing routines for writing and reading have to implemented by the user. SEGGER does not provide these functions.

/*********************************************************************
*
*       _Write8_A0
*/
static void _Write8_A0(U8 Data) {
  //
  // DC low (command)
  //
  LCD_DCX_PORT->BRR = LCD_DCX_PIN;
  //
  // Set command
  //
  *((__IO uint8_t*)&hspi1.Instance->DR) = Data;
  //
  // Wait for busy flag
  //
  while((hspi1.Instance->SR & SPI_FLAG_BSY) != RESET);
}

/*********************************************************************
*
*       _Write8_A1
*/
static void _Write8_A1(U8 Data) {
  //
  // DC high (data)
  //
  LCD_DCX_PORT->BSRR = LCD_DCX_PIN;
  //
  // Set data
  //
  *((__IO uint8_t*)&hspi1.Instance->DR) = Data;
  //
  // Wait for TX flag
  //
  while((hspi1.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE);
  //
  // Wait for busy flag
  //
  while((hspi1.Instance->SR & SPI_FLAG_BSY) != RESET);
}

Controller selection and other options

After setting the function pointers, the GUI_PORT_API structure can be passed to GUIDRV_FlexColor_SetFunc(). Calling GUIDRV_FlexColor_SetFunc() will also select the function set for communication with the controller, as well as the color depth, bus width, and whether or not to use a cache.

GUIDRV_FlexColor_SetFunc() accepts the following parameters:

  • GUI_DEVICE pointer: A pointer to the previously created driver device
  • GUI_PORT_API point: A pointer to the GUI_PORT_API structure from the step before
  • Function pointer: A macro to select the correct display driver. Please refer to API description of GUIDRV_FlexColor_SetFunc()
  • Function pointer: A macro to define the mode

A complete list with all possible parameters can be found in the API description of GUIDRV_FlexColor_SetFunc().

The code below sets the previously initialized GUI_PORT_API structure and selects the function set GUIDRV_FLEXCOLOR_F66709 to drive an Ilitek ILI9341 display controller. Additional to that a color depth of 16 bpp, no cache and a bus width of 8 bit should be used. This is determined by the last parameter. This is how the macro is read:

GUIDRV_FLEXCOLOR_M16C0B8

  • M16: is the desired color depth
  • C0: Whether a cache or not is used (C0 - no cache, C1 - with cache)
  • B8: 8 bit bus width
GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66709, GUIDRV_FLEXCOLOR_M16C0B8);

Configuration options

In the last step the user a couple of configuration options. For example this can the display orientation, a certain number of dummy reads or an offset in x and y (COM, SEG).

For a complete description of the options, please refer to the API description of GUIDRV_FlexColor_Config().

  Config.Orientation   = GUI_MIRROR_X;
  Config.NumDummyReads = 0;
  GUIDRV_FlexColor_Config(pDevice, &Config);

Example LCDConf.c

After performing all steps mentioned above your LCD configuration should look similar to the one below. Keep in mind that the configuration below is only an example and should give you just an idea of how the GUIDRV_FlexColor driver can be configured.

/*********************************************************************
*                SEGGER Microcontroller GmbH & Co. KG                *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*       (c) 2003 - 2015  SEGGER Microcontroller GmbH & Co. KG        *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
----------------------------------------------------------------------
File        : LCDConf.c
Purpose     : Display controller configuration (single layer)
---------------------------END-OF-HEADER------------------------------
*/

#include <string.h>

#include "GUI.h"
#include "GUIDRV_FlexColor.h"

#include "stm32g0xx_hal.h"

/*********************************************************************
*
*       Layer configuration
*
**********************************************************************
*/
//
// Physical display size
//
#define XSIZE_PHYS 320
#define YSIZE_PHYS 240

//
// Color conversion
//
#define COLOR_CONVERSION GUICC_565

//
// Display driver
//
#define DISPLAY_DRIVER GUIDRV_FLEXCOLOR

//
// Display orientation
//
#define DISPLAY_ORIENTATION (GUI_MIRROR_X)                               // Default
//#define DISPLAY_ORIENTATION (GUI_MIRROR_Y)                               // 180
//#define DISPLAY_ORIENTATION (GUI_SWAP_XY)                                // CW
//#define DISPLAY_ORIENTATION (GUI_SWAP_XY | GUI_MIRROR_X | GUI_MIRROR_Y)  // CCW

/*********************************************************************
*
*       Configuration checking
*
**********************************************************************
*/
#ifndef   XSIZE_PHYS
  #error Physical X size of display is not defined!
#endif
#ifndef   YSIZE_PHYS
  #error Physical Y size of display is not defined!
#endif
#ifndef   COLOR_CONVERSION
  #error Color conversion not defined!
#endif
#ifndef   DISPLAY_DRIVER
  #error No display driver defined!
#endif

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/
#define LCD_DCX_PIN          GPIO_PIN_14
#define LCD_DCX_PORT         GPIOB
#define LCD_TE_PIN           GPIO_PIN_0
#define LCD_TE_PORT          GPIOA
#define LCD_TE_EXTI_IRQn     EXTI0_1_IRQn
#define LCD_RESET_PIN        GPIO_PIN_1
#define LCD_RESET_PORT       GPIOA
#define LCD_CSX_PIN          GPIO_PIN_9
#define LCD_CS_PORT          GPIOA

//
// ILI9341
//
#define ILI9341_EXIT_SLEEP_MODE       0x11
#define ILI9341_ENTER_NORMAL_MODE     0x13
#define ILI9341_SET_DISPLAY_OFF       0x28
#define ILI9341_SET_DISPLAY_ON        0x29
#define ILI9341_SET_TEAR_ON           0x35
#define ILI9341_SET_ADDRESS_MODE      0x36
#define ILI9341_SET_PIXEL_FORMAT      0x3A
#define ILI9341_SET_TEAR_SCANLINE     0x44

#define ILI9341_RAM_ACCESS_W          0x2C
#define ILI9341_RAM_ACCESS_R          0x2E

/*********************************************************************
*
*       Prototype
*
**********************************************************************
*/
SPI_HandleTypeDef hspi1;
DMA_HandleTypeDef hdma_spi1_tx;

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/
/*********************************************************************
*
*       Error_Handler
*/
static void Error_Handler(void) {
  while (1);
}

/*********************************************************************
*
*       _SPI1_Init
*/
static void _SPI1_Init(void) {
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  //
  // SPI clock enable
  //
  __HAL_RCC_SPI1_CLK_ENABLE();

  __HAL_RCC_GPIOA_CLK_ENABLE();
  //
  // Configure PA5 (SCK) and PA7 (MOSI)
  //
  GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  //
  // Configure PA6 (MISO)
  //
  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  //
  // SPI1 parameter configuration
  //
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK) {
    Error_Handler();
  }
}

/*********************************************************************
*
*       _DMA_Init
*/
static void _DMA_Init(void) {
  //
  // Enable DMA clock
  //
  __HAL_RCC_DMA1_CLK_ENABLE();
  //
  // Configure DMA1 CH1 interrupt
  //
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
  //
  // Configure DMA1 CH2_3 interrupt
  //
  HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
}

/*********************************************************************
*
*       _GPIO_Init
*/
static void _GPIO_Init(void) {
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  //
  // Enable GPIO clocks
  //
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  //
  //  Reset low
  //
  HAL_GPIO_WritePin(LCD_RESET_PORT, LCD_RESET_PIN, GPIO_PIN_RESET);
  //
  // DC high
  //
  HAL_GPIO_WritePin(LCD_DCX_PORT, LCD_DCX_PIN, GPIO_PIN_SET);
  //
  // CS high
  //
  HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CSX_PIN, GPIO_PIN_SET);
  //
  // TE pin
  //
  GPIO_InitStruct.Pin = LCD_TE_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(LCD_TE_PORT, &GPIO_InitStruct);
  //
  // Reset pin
  //
  GPIO_InitStruct.Pin = LCD_RESET_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LCD_RESET_PORT, &GPIO_InitStruct);
  //
  // DC pin
  //
  GPIO_InitStruct.Pin = LCD_DCX_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(LCD_DCX_PORT, &GPIO_InitStruct);
  //
  // CS display pin
  //
  GPIO_InitStruct.Pin = LCD_CSX_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(LCD_CS_PORT, &GPIO_InitStruct);
  //
  // Configure EXTI interrupt
  //
  HAL_NVIC_SetPriority(EXTI0_1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI0_1_IRQn);
}

/*********************************************************************
*
*       _SetCS
*/
static void _SetCS(U8 NotActive) {
  if (NotActive) {
    //
    // Not active, set CS high
    //
    LCD_CS_PORT->BSRR = LCD_CSX_PIN;
  } else {
    //
    // Active, set CS low
    //
    LCD_CS_PORT->BRR = LCD_CSX_PIN;
  }
}

/*********************************************************************
*
*       _Write8_A0
*/
static void _Write8_A0(U8 Data) {
  //
  // CS low
  //
  LCD_CS_PORT->BRR = LCD_CSX_PIN;
  //
  // DC low (command)
  //
  LCD_DCX_PORT->BRR = LCD_DCX_PIN;
  //
  // Set command
  //
  *((__IO uint8_t*)&hspi1.Instance->DR) = Data;
  //
  // Wait for busy flag
  //
  while((hspi1.Instance->SR & SPI_FLAG_BSY) != RESET);
}

/*********************************************************************
*
*       _Write8_A1
*/
static void _Write8_A1(U8 Data) {
  //
  // CS low
  //
  LCD_CS_PORT->BRR = LCD_CSX_PIN;
  //
  // DC high (data)
  //
  LCD_DCX_PORT->BSRR = LCD_DCX_PIN;
  //
  // Set data
  //
  *((__IO uint8_t*)&hspi1.Instance->DR) = Data;
  //
  // Wait for TX flag
  //
  while((hspi1.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE);
  //
  // Wait for busy flag
  //
  while((hspi1.Instance->SR & SPI_FLAG_BSY) != RESET);
}

/*********************************************************************
*
*       _WriteM8_A1
*/
static void _WriteM8_A1(U8 * pData, int NumItems) {
  //
  // CS low
  //
  LCD_CS_PORT->BRR = LCD_CSX_PIN;
  //
  // DC high (data)
  //
  LCD_DCX_PORT->BSRR = LCD_DCX_PIN;
  //
  // Send data
  //
  do {
    *((__IO uint8_t*)&hspi1.Instance->DR) = *pData;
    pData++;
    //
    // wait for TX flaag
    //
    while((hspi1.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE);
  } while (--NumItems);
  //
  // Wait for busy flag
  //
  while(((hspi1.Instance->SR) & SPI_FLAG_BSY) != RESET);
}

/*********************************************************************
*
*       _Read8_A1
*/
static U8 _Read8_A1(void) {
  U8 Data;
  //
  // DC high (data)
  //
  LCD_DCX_PORT->BSRR = LCD_DCX_PIN;
  //
  // Cear RX buffer
  //
  while (hspi1.Instance->SR & SPI_FLAG_RXNE) { *((__IO uint8_t*)&hspi1.Instance->DR); }
  //
  // Receive data
  //
  *((__IO uint8_t*)&hspi1.Instance->DR) = 0;
  //
  // Wait for busy flag
  //
  while((hspi1.Instance->SR & SPI_FLAG_BSY));
  //
  // Get data
  //
  Data = *((__IO uint8_t*)&hspi1.Instance->DR);
  return Data;
}

/*********************************************************************
*
*       _ReadM8_A1
*/
static void _ReadM8_A1(U8 * pData, int NumItems) {
  //
  // DC high (data)
  //
  LCD_DCX_PORT->BSRR = LCD_DCX_PIN;
  //
  // Clear RX buffer
  //
  while (hspi1.Instance->SR & SPI_FLAG_RXNE) { *((volatile uint8_t*)&hspi1.Instance->DR); }
  //
  // Get data
  //
  while (NumItems) {
    //
    // Wait for busy flag
    //
    *((__IO uint8_t*)&hspi1.Instance->DR) = 0;
    while(((hspi1.Instance->SR) & SPI_FLAG_BSY));
    *pData = *((__IO uint8_t*)&hspi1.Instance->DR);
    pData++;
    NumItems--;
  }
}

/*********************************************************************
*
*       _InitController
*
* Purpose:
*   Init ILI9341
*/
static void _InitController(void) {
  static U8 Done;
  U8 aData[4];
 
  if (Done) {  // Init only once
    return;
  }  
  Done = 1;
  _GPIO_Init();
  _DMA_Init();
  _SPI1_Init();

  __HAL_SPI_ENABLE(&hspi1);

  HAL_GPIO_WritePin(LCD_RESET_PORT, LCD_RESET_PIN, GPIO_PIN_RESET);
  GUI_Delay(100);
  HAL_GPIO_WritePin(LCD_RESET_PORT, LCD_RESET_PIN, GPIO_PIN_SET);
  GUI_Delay(100);
  //
  // Leave sleep mode
  //
  _Write8_A0(ILI9341_EXIT_SLEEP_MODE);
  GUI_Delay(100);
  //
  // Enter normal mode
  //
  _Write8_A0(ILI9341_ENTER_NORMAL_MODE);
  GUI_Delay(100);
  //
  // Pixel Format, 16bpp
  //
  aData[0] = 0x05; // RGB565
  _Write8_A0(ILI9341_SET_PIXEL_FORMAT);
  _WriteM8_A1(aData, 1);
  GUI_Delay(100);

  //
  // Generate TE signal
  //
  aData[0] = 0; //0x00;
  _Write8_A0(ILI9341_SET_TEAR_ON);
  _WriteM8_A1(aData, 1);
  GUI_Delay(100);
  //
  // TE scan line
  //
  aData[0] = 0;
  aData[1] = 0;
  _Write8_A0(ILI9341_SET_TEAR_SCANLINE);
  _WriteM8_A1(aData, 2);
  GUI_Delay(100);
}

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/
void EXTI0_1_IRQHandler(void);
void EXTI0_1_IRQHandler(void) {
  HAL_GPIO_EXTI_IRQHandler(LCD_TE_PIN);
}

/*********************************************************************
*
*       LCD_X_Config
*
* Purpose:
*   Called during the initialization process in order to set up the
*   display driver configuration.
*
*/
void LCD_X_Config(void) {
  GUI_DEVICE * pDevice;
  GUI_PORT_API PortAPI    = {0};
  CONFIG_FLEXCOLOR Config = {0};

  //
  // Set display driver and color conversion for 1st layer
  //
  pDevice = GUI_DEVICE_CreateAndLink(DISPLAY_DRIVER, COLOR_CONVERSION, 0, 0);
  //
  // Screen size
  //
  LCD_SetSizeEx (0, YSIZE_PHYS, XSIZE_PHYS);
  LCD_SetVSizeEx(0, YSIZE_PHYS, XSIZE_PHYS);
  //
  // Hardware routines (PortAPI)
  //
  PortAPI.pfWrite8_A0  = _Write8_A0;
  PortAPI.pfWrite8_A1  = _Write8_A1;
  PortAPI.pfWriteM8_A1 = _WriteM8_A1;
  PortAPI.pfRead8_A1   = _Read8_A1;
  PortAPI.pfReadM8_A1  = _ReadM8_A1;
  PortAPI.pfSetCS      = _SetCS;
  //
  // Function selection and operation mode (bus, bpp and cache)
  //
  GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66709, GUIDRV_FLEXCOLOR_M16C0B8);
  //
  // Orientation
  //
  Config.Orientation   = GUI_MIRROR_X;
  Config.NumDummyReads = 0;
  GUIDRV_FlexColor_Config(pDevice, &Config);
}

/*********************************************************************
*
*       LCD_X_DisplayDriver
*
* Purpose:
*   This function is called by the display driver for several purposes.
*   To support the according task the routine needs to be adapted to
*   the display controller. Please note that the commands marked with
*   'optional' are not cogently required and should only be adapted if
*   the display controller supports these features.
*
* Parameter:
*   LayerIndex - Index of layer to be configured
*   Cmd        - Please refer to the details in the switch statement below
*   pData      - Pointer to a LCD_X_DATA structure
*/
int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * pData) {
  int r;

  GUI_USE_PARA(LayerIndex);
  GUI_USE_PARA(pData);
  r = 0;
  switch (Cmd) {
  case LCD_X_INITCONTROLLER:
    _InitController();
    break;
  case LCD_X_ON:
    //
    // Turn display on
    //
    _Write8_A0(ILI9341_SET_DISPLAY_ON);
    break;
  case LCD_X_OFF:
    //
    // Turn display on
    //
    _Write8_A0(ILI9341_SET_DISPLAY_OFF);
    break;
  default:
    r = -1;
    break;
  }
  return r;
}

/*************************** End of file ****************************/