Video image display and touch panel sample for GR-PEACH LCD Shield.

Information

Japanese version is available in lower part of this page.
このページの後半に日本語版が用意されています.

Information

This sample has multiple frame buffers so that the clock source can display different cameras and LCDs cleanly. If you are using the NTSC signal, the following sample is simpler.
このサンプルはクロック元が異なるカメラとLCDを綺麗に表示するために、複数のフレームバッファを持っています。NTSC信号を使用するのであれば、以下のサンプルの方が単純です。

https://developer.mbed.org/users/dkato/code/GR-PEACH_NTSC_in_2ch/

What is this ?

The basic function of the GR-PEACH LCD Shield can be tried by this sample.
Display the information of the touch panel on the camera image.
A character font is shown to the screen lower right every 1 second.

Touch panel information

The touch location of the blue and the 2nd point becomes pink in the 1st touch location in case of a multi-touch.
While touching the screen, a white square in the upper left corner of the screen will be displayed.
Touch information is being output in printf. When doing terminal designation, please set 230400 as a baud rate.

Composition

GR-PEACH, GR-PEACH 4.3 inch LCD Shield or GR-PEACH 7.1 inch LCD Shield, GR-PEACH AUDIO CAMERA Shield and Camera.
/media/uploads/dkato/gr-peach_with_camera_lcd.png

When using analog input for video input, GR-PEACH AUDIO CAMERA Shield and the camera are unnecessary.
(Setting the VIDEO_CVBS to VIDEO_INPUT_METHOD)
Please input analog signal to NTSC1A pin. (Even if there are no input signals, a touch panel can be operated.).
/media/uploads/dkato/video_display_analog.jpg

Video input of default is a digital input (Cmos Camera). When changing the video input, please change the below.

mbed_app.json

{
    "config": {
        "camera":{
            "help": "0:disable 1:enable",
            "value": "1"
        },
        "camera-type":{
            "help": "Please see README.md",
            "value": "CAMERA_CVBS"
        },
        "lcd":{
            "help": "0:disable 1:enable",
            "value": "1"
        },
        "lcd-type":{
            "help": "Please see README.md",
            "value": "GR_PEACH_4_3INCH_SHIELD"
        }
    }
}
camera-type "value"Description
CAMERA_CVBSGR-PEACH NTSC signal
CAMERA_MT9V111GR-PEACH MT9V111
CAMERA_OV7725GR-LYHCEE included camera
CAMERA_OV5642GR-PEACH OV5642
CAMERA_WIRELESS_CAMERAGR-PEACH Wireless/Camera shield (MT9V111)
lcd-type "value"Description
GR_PEACH_4_3INCH_SHIELDGR-PEACH 4.3 inch LCD shield
GR_PEACH_7_1INCH_SHIELDGR-PEACH 7.1 inch LCD shield
GR_PEACH_RSK_TFTGR-PEACH RSK board LCD
GR_PEACH_DISPLAY_SHIELDGR-PEACH Display Shield or DVi-Board
GR_LYCHEE_LCDGR-LYHCEE TF043HV001A0..etc(40pin)




概要

このサンプルではGR-PEACH LCD Shieldの基本的な機能を試せます。
カメラ画像の上にタッチパネルの情報を重ねて表示します。
文字フォントが右下に1秒毎に表示されます。

タッチパネルの情報

マルチタッチの場合は1点目のタッチ位置は青、2点目のタッチ位置はピンクになります。
画面がタッチされている間、画面左上に白い四角が表示されます。
printfでもタッチ情報を出力しています。ターミナル表示を行う際は、ボーレートに230400を設定してください。

構成

GR-PEACH、GR-PEACH 4.3 inch LCD Shield または GR-PEACH 7.1 inch LCD Shield、GR-PEACH AUDIO CAMERA Shield 、カメラ。
/media/uploads/dkato/gr-peach_with_camera_lcd.png

ビデオ入力にアナログ入力を使用する場合はGR-PEACH AUDIO CAMERA Shield とカメラは不要です。
(VIDEO_INPUT_METHOD にVIDEO_CVBS を設定)
NTSC1Aピンにアナログ信号を入力してください。(入力信号が無くてもタッチパネルは操作できます。)
/media/uploads/dkato/video_display_analog.jpg

デフォルトのビデオ入力はデジタル入力(Cmos Camera)です。ビデオ入力を変更する際は以下を変更してください。

mbed_app.json

{
    "config": {
        "camera":{
            "help": "0:disable 1:enable",
            "value": "1"
        },
        "camera-type":{
            "help": "Please see README.md",
            "value": "CAMERA_CVBS"
        },
        "lcd":{
            "help": "0:disable 1:enable",
            "value": "1"
        },
        "lcd-type":{
            "help": "Please see README.md",
            "value": "GR_PEACH_4_3INCH_SHIELD"
        }
    }
}
camera-type "value"Description
CAMERA_CVBSGR-PEACH NTSC signal
CAMERA_MT9V111GR-PEACH MT9V111
CAMERA_OV7725GR-LYHCEE included camera
CAMERA_OV5642GR-PEACH OV5642
CAMERA_WIRELESS_CAMERAGR-PEACH Wireless/Camera shield (MT9V111)
lcd-type "value"Description
GR_PEACH_4_3INCH_SHIELDGR-PEACH 4.3 inch LCD shield
GR_PEACH_7_1INCH_SHIELDGR-PEACH 7.1 inch LCD shield
GR_PEACH_RSK_TFTGR-PEACH RSK board LCD
GR_PEACH_DISPLAY_SHIELDGR-PEACH Display Shield or DVi-Board
GR_LYCHEE_LCDGR-LYHCEE TF043HV001A0..etc(40pin)

main.cpp

Committer:
dkato
Date:
2016-11-28
Revision:
6:95b92f2035dc
Parent:
5:4e96673f4830
Child:
7:d782dce51ee6

File content as of revision 6:95b92f2035dc:

#include "mbed.h"
#include "DisplayBace.h"
#include "rtos.h"
#include "AsciiFont.h"

#define VIDEO_CVBS             (0)                 /* Analog  Video Signal */
#define VIDEO_CMOS_CAMERA      (1)                 /* Digital Video Signal */
#define VIDEO_YCBCR422         (0)
#define VIDEO_RGB888           (1)
#define VIDEO_RGB565           (2)

/**** User Selection *********/
/** Camera setting **/
#define VIDEO_INPUT_METHOD     (VIDEO_CMOS_CAMERA) /* Select  VIDEO_CVBS or VIDEO_CMOS_CAMERA                       */
#define VIDEO_INPUT_FORMAT     (VIDEO_YCBCR422)    /* Select  VIDEO_YCBCR422 or VIDEO_RGB888 or VIDEO_RGB565        */
#define USE_VIDEO_CH           (0)                 /* Select  0 or 1            If selecting VIDEO_CMOS_CAMERA, should be 0.)               */
#define VIDEO_PAL              (0)                 /* Select  0(NTSC) or 1(PAL) If selecting VIDEO_CVBS, this parameter is not referenced.) */
/** LCD setting **/
#define LCD_TYPE               (0)                 /* Select  0(4.3inch) or 1(7.1inch) */
/*****************************/

/** LCD shield config **/
#if (LCD_TYPE == 0)
  #include "LCD_shield_config_4_3inch.h"
#else
  #include "LCD_shield_config_7_1inch.h"
#endif

/** Video and Grapics (GRAPHICS_LAYER_0) parameter **/
/* video input */
#if USE_VIDEO_CH == (0)
  #define VIDEO_INPUT_CH       (DisplayBase::VIDEO_INPUT_CHANNEL_0)
  #define VIDEO_INT_TYPE       (DisplayBase::INT_TYPE_S0_VFIELD)
#else
  #define VIDEO_INPUT_CH       (DisplayBase::VIDEO_INPUT_CHANNEL_1)
  #define VIDEO_INT_TYPE       (DisplayBase::INT_TYPE_S1_VFIELD)
#endif

/* NTSC or PAL */
#if VIDEO_PAL == 0
  #define COL_SYS              (DisplayBase::COL_SYS_NTSC_358)
#else
  #define COL_SYS              (DisplayBase::COL_SYS_PAL_443)
#endif

/* Video input and LCD layer 0 output */
#if VIDEO_INPUT_FORMAT == VIDEO_YCBCR422
  #define VIDEO_FORMAT         (DisplayBase::VIDEO_FORMAT_YCBCR422)
  #define GRAPHICS_FORMAT      (DisplayBase::GRAPHICS_FORMAT_YCBCR422)
  #define WR_RD_WRSWA          (DisplayBase::WR_RD_WRSWA_NON)
#elif VIDEO_INPUT_FORMAT == VIDEO_RGB565
  #define VIDEO_FORMAT         (DisplayBase::VIDEO_FORMAT_RGB565)
  #define GRAPHICS_FORMAT      (DisplayBase::GRAPHICS_FORMAT_RGB565)
  #define WR_RD_WRSWA          (DisplayBase::WR_RD_WRSWA_32_16BIT)
#else
  #define VIDEO_FORMAT         (DisplayBase::VIDEO_FORMAT_RGB888)
  #define GRAPHICS_FORMAT      (DisplayBase::GRAPHICS_FORMAT_RGB888)
  #define WR_RD_WRSWA          (DisplayBase::WR_RD_WRSWA_32BIT)
#endif

/* The size of the video input is adjusted to the LCD size. */
#define VIDEO_PIXEL_HW                LCD_PIXEL_WIDTH
#define VIDEO_PIXEL_VW                LCD_PIXEL_HEIGHT

/*! Frame buffer stride: Frame buffer stride should be set to a multiple of 32 or 128
    in accordance with the frame buffer burst transfer mode. */
/* FRAME BUFFER Parameter GRAPHICS_LAYER_0 */
#define FRAME_BUFFER_NUM              (3u)
#if ( VIDEO_INPUT_FORMAT == VIDEO_YCBCR422 || VIDEO_INPUT_FORMAT == VIDEO_RGB565 )
  #define FRAME_BUFFER_BYTE_PER_PIXEL (2u)
#else
  #define FRAME_BUFFER_BYTE_PER_PIXEL (4u)
#endif
#define FRAME_BUFFER_STRIDE           (((LCD_PIXEL_WIDTH * FRAME_BUFFER_BYTE_PER_PIXEL) + 31u) & ~31u)

/* TOUCH BUFFER Parameter GRAPHICS_LAYER_1 */
#define TOUCH_BUFFER_BYTE_PER_PIXEL   (2u)
#define TOUCH_BUFFER_STRIDE           (((LCD_PIXEL_WIDTH * TOUCH_BUFFER_BYTE_PER_PIXEL) + 31u) & ~31u)

/* Touch panel parameter */
#define TOUCH_NUM                     (2u)
#define DRAW_POINT                    (5)

/* STRING BUFFER Parameter GRAPHICS_LAYER_2 */
#define STRING_PIXEL_HW               (120)
#define STRING_PIXEL_VM               (24)
#define STRING_BUFFER_BYTE_PER_PIXEL  (2u)
#define STRING_BUFFER_STRIDE          (((LCD_PIXEL_WIDTH * STRING_BUFFER_BYTE_PER_PIXEL) + 31u) & ~31u)

static DisplayBase Display;
static DigitalOut  lcd_pwon(P7_15);
static DigitalOut  lcd_blon(P8_1);
static PwmOut      lcd_cntrst(P8_15);
static Serial      pc(USBTX, USBRX);
static Semaphore   sem_touch_int(0);
static TouckKey_LCD_shield touch(P4_0, P2_13, I2C_SDA, I2C_SCL);
static Thread *    p_VideoLcdTask = NULL;
static DigitalOut  led_blue(LED_BLUE);

#if defined(__ICCARM__)
/* 32 bytes aligned */
#pragma data_alignment=32
static uint8_t user_frame_buffer0[FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT];
#pragma data_alignment=32
static uint8_t user_frame_buffer1[FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT];
#pragma data_alignment=32
static uint8_t user_frame_buffer2[FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT];
#pragma data_alignment=32
static uint8_t user_frame_buffer_touch[TOUCH_BUFFER_STRIDE * LCD_PIXEL_HEIGHT];
#pragma data_alignment=32
static uint8_t user_frame_buffer_string[STRING_BUFFER_STRIDE * STRING_PIXEL_VM];
#else
/* 32 bytes aligned */
static uint8_t user_frame_buffer0[FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT]__attribute((aligned(32)));
static uint8_t user_frame_buffer1[FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT]__attribute((aligned(32)));
static uint8_t user_frame_buffer2[FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT]__attribute((aligned(32)));
static uint8_t user_frame_buffer_touch[TOUCH_BUFFER_STRIDE * LCD_PIXEL_HEIGHT]__attribute((aligned(32)));
static uint8_t user_frame_buffer_string[STRING_BUFFER_STRIDE * STRING_PIXEL_VM]__attribute((aligned(32)));
#endif
static uint8_t * FrameBufferTbl[FRAME_BUFFER_NUM] = {user_frame_buffer0, user_frame_buffer1, user_frame_buffer2};
#if VIDEO_INPUT_METHOD == VIDEO_CVBS
static volatile int32_t vfield_count = 0;
#endif
static int write_buff_num = 0;
static int read_buff_num = 0;
static bool graphics_init_end = false;

/****** cache control ******/
static void dcache_clean(void * p_buf, uint32_t size) {
    uint32_t start_addr = (uint32_t)p_buf & 0xFFFFFFE0;
    uint32_t end_addr   = (uint32_t)p_buf + size;
    uint32_t addr;

    /* Data cache clean */
    for (addr = start_addr; addr < end_addr; addr += 0x20) {
        __v7_clean_dcache_mva((void *)addr);
    }
}

/****** LCD ******/
#if(0) /* When needing LCD Vsync interrupt, please make it effective. */
static void IntCallbackFunc_LoVsync(DisplayBase::int_type_t int_type) {
    /* Interrupt callback function for Vsync interruption */
}
#endif

static void Init_LCD_Display(void) {
    DisplayBase::graphics_error_t error;
    DisplayBase::lcd_config_t lcd_config;
    PinName lvds_pin[8] = {
        /* data pin */
        P5_7, P5_6, P5_5, P5_4, P5_3, P5_2, P5_1, P5_0
    };

    lcd_pwon = 0;
    lcd_blon = 0;
    Thread::wait(100);
    lcd_pwon = 1;
    lcd_blon = 1;

    Display.Graphics_Lvds_Port_Init(lvds_pin, 8);

    /* Graphics initialization process */
    lcd_config = LcdCfgTbl_LCD_shield;
    error = Display.Graphics_init(&lcd_config);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
    graphics_init_end = true;

#if(0) /* When needing LCD Vsync interrupt, please make it effective. */
    /* Interrupt callback function setting (Vsync signal output from scaler 0) */
    error = Display.Graphics_Irq_Handler_Set(DisplayBase::INT_TYPE_S0_LO_VSYNC, 0, IntCallbackFunc_LoVsync);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
#endif
}

static void Start_LCD_Display(uint8_t * p_buf) {
    DisplayBase::rect_t rect;

    rect.vs = 0;
    rect.vw = LCD_PIXEL_HEIGHT;
    rect.hs = 0;
    rect.hw = LCD_PIXEL_WIDTH;
    Display.Graphics_Read_Setting(
        DisplayBase::GRAPHICS_LAYER_0,
        (void *)p_buf,
        FRAME_BUFFER_STRIDE,
        GRAPHICS_FORMAT,
        WR_RD_WRSWA,
        &rect
    );
    Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0);
}

/****** Video ******/
#if(0) /* When needing video Vsync interrupt, please make it effective. */
static void IntCallbackFunc_ViVsync(DisplayBase::int_type_t int_type) {
    /* Interrupt callback function for Vsync interruption */
}
#endif

static void IntCallbackFunc_Vfield(DisplayBase::int_type_t int_type) {
    /* Interrupt callback function */
#if VIDEO_INPUT_METHOD == VIDEO_CVBS
    if (vfield_count == 0) {
        vfield_count = 1;
    } else {
        vfield_count = 0;
#else
    {
#endif
        if (p_VideoLcdTask != NULL) {
            p_VideoLcdTask->signal_set(1);
        }
    }
}

static void Init_Video(void) {
    DisplayBase::graphics_error_t error;

    /* Graphics initialization process */
    if (graphics_init_end == false) {
        /* When not initializing LCD, this processing is needed. */
        error = Display.Graphics_init(NULL);
        if (error != DisplayBase::GRAPHICS_OK) {
            printf("Line %d, error %d\n", __LINE__, error);
            mbed_die();
        }
        graphics_init_end = true;
    }

#if VIDEO_INPUT_METHOD == VIDEO_CVBS
    error = Display.Graphics_Video_init( DisplayBase::INPUT_SEL_VDEC, NULL);
    if( error != DisplayBase::GRAPHICS_OK ) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
#elif VIDEO_INPUT_METHOD == VIDEO_CMOS_CAMERA
    DisplayBase::video_ext_in_config_t ext_in_config;
    PinName cmos_camera_pin[11] = {
        /* data pin */
        P2_7, P2_6, P2_5, P2_4, P2_3, P2_2, P2_1, P2_0,
        /* control pin */
        P10_0,      /* DV0_CLK   */
        P1_0,       /* DV0_Vsync */
        P1_1        /* DV0_Hsync */
    };

    /* MT9V111 camera input config */
    ext_in_config.inp_format     = DisplayBase::VIDEO_EXTIN_FORMAT_BT601; /* BT601 8bit YCbCr format */
    ext_in_config.inp_pxd_edge   = DisplayBase::EDGE_RISING;              /* Clock edge select for capturing data          */
    ext_in_config.inp_vs_edge    = DisplayBase::EDGE_RISING;              /* Clock edge select for capturing Vsync signals */
    ext_in_config.inp_hs_edge    = DisplayBase::EDGE_RISING;              /* Clock edge select for capturing Hsync signals */
    ext_in_config.inp_endian_on  = DisplayBase::OFF;                      /* External input bit endian change on/off       */
    ext_in_config.inp_swap_on    = DisplayBase::OFF;                      /* External input B/R signal swap on/off         */
    ext_in_config.inp_vs_inv     = DisplayBase::SIG_POL_NOT_INVERTED;     /* External input DV_VSYNC inversion control     */
    ext_in_config.inp_hs_inv     = DisplayBase::SIG_POL_INVERTED;         /* External input DV_HSYNC inversion control     */
    ext_in_config.inp_f525_625   = DisplayBase::EXTIN_LINE_525;           /* Number of lines for BT.656 external input */
    ext_in_config.inp_h_pos      = DisplayBase::EXTIN_H_POS_CRYCBY;       /* Y/Cb/Y/Cr data string start timing to Hsync reference */
    ext_in_config.cap_vs_pos     = 6;                                     /* Capture start position from Vsync */
    ext_in_config.cap_hs_pos     = 150;                                   /* Capture start position form Hsync */
#if (LCD_TYPE == 0)
    /* The same screen ratio as the screen ratio of the LCD. */
    ext_in_config.cap_width      = 640;                                   /* Capture width */
    ext_in_config.cap_height     = 363;                                   /* Capture height Max 468[line]
                                                                             Due to CMOS(MT9V111) output signal timing and VDC5 specification */
#else
    ext_in_config.cap_width      = 640;                                   /* Capture width  */
    ext_in_config.cap_height     = 468;                                   /* Capture height Max 468[line]
                                                                             Due to CMOS(MT9V111) output signal timing and VDC5 specification */
#endif
    error = Display.Graphics_Video_init( DisplayBase::INPUT_SEL_EXT, &ext_in_config);
    if( error != DisplayBase::GRAPHICS_OK ) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Camera input port setting */
    error = Display.Graphics_Dvinput_Port_Init(cmos_camera_pin, 11);
    if( error != DisplayBase::GRAPHICS_OK ) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
#endif

#if(0) /* When needing video Vsync interrupt, please make it effective. */
    /* Interrupt callback function setting (Vsync signal input to scaler 0) */
    error = Display.Graphics_Irq_Handler_Set(DisplayBase::INT_TYPE_S0_VI_VSYNC, 0, IntCallbackFunc_ViVsync);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
#endif

    /* Interrupt callback function setting (Field end signal for recording function in scaler 0) */
    error = Display.Graphics_Irq_Handler_Set(VIDEO_INT_TYPE, 0, IntCallbackFunc_Vfield);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
}

static void Start_Video(uint8_t * p_buf) {
    DisplayBase::graphics_error_t error;

    /* Video capture setting (progressive form fixed) */
    error = Display.Video_Write_Setting(
                VIDEO_INPUT_CH,
                COL_SYS,
                p_buf,
                FRAME_BUFFER_STRIDE,
                VIDEO_FORMAT,
                WR_RD_WRSWA,
                VIDEO_PIXEL_VW,
                VIDEO_PIXEL_HW
            );
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Video write process start */
    error = Display.Video_Start(VIDEO_INPUT_CH);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Video write process stop */
    error = Display.Video_Stop(VIDEO_INPUT_CH);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Video write process start */
    error = Display.Video_Start(VIDEO_INPUT_CH);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
}

/****** Video input is output to LCD ******/
static void video_lcd_task(void) {
    DisplayBase::graphics_error_t error;
    int wk_num;
    int i;

    /* Initialization memory */
    for (i = 0; i < FRAME_BUFFER_NUM; i++) {
        memset(FrameBufferTbl[i], 0, (FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT));
        dcache_clean(FrameBufferTbl[i], (FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT));
    }

    /* Start of Video */
    Start_Video(FrameBufferTbl[write_buff_num]);

    /* Wait for first video drawing */
    Thread::signal_wait(1);
    write_buff_num++;
    if (write_buff_num >= FRAME_BUFFER_NUM) {
        write_buff_num = 0;
    }
    error = Display.Video_Write_Change(VIDEO_INPUT_CH, FrameBufferTbl[write_buff_num], FRAME_BUFFER_STRIDE);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Start of LCD */
    Start_LCD_Display(FrameBufferTbl[read_buff_num]);

    /* Backlight on */
    Thread::wait(200);
    lcd_cntrst.write(1.0);

    while (1) {
        Thread::signal_wait(1);
        wk_num = write_buff_num + 1;
        if (wk_num >= FRAME_BUFFER_NUM) {
            wk_num = 0;
        }
        /* If the next buffer is empty, it's changed. */
        if (wk_num != read_buff_num) {
            read_buff_num  = write_buff_num;
            write_buff_num = wk_num;
            /* Change video buffer */
            error = Display.Video_Write_Change(VIDEO_INPUT_CH, FrameBufferTbl[write_buff_num], FRAME_BUFFER_STRIDE);
            if (error != DisplayBase::GRAPHICS_OK) {
                printf("Line %d, error %d\n", __LINE__, error);
                mbed_die();
            }
            /* Change LCD buffer */
            Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_0, (void *)FrameBufferTbl[read_buff_num]);
        }
    }
}

/****** Touch panel ******/
static void draw_touch_pos(uint8_t * p_buf, int id, int x, int y) {
    int idx_base;
    int wk_idx;
    int i;
    int j;
    uint8_t coller_pix[TOUCH_BUFFER_BYTE_PER_PIXEL];  /* ARGB4444 */

    /* A coordinate in the upper left is calculated from a central coordinate. */
    if ((x - (DRAW_POINT / 2)) >= 0) {
        x -= (DRAW_POINT / 2);
    }
    if (x > (LCD_PIXEL_WIDTH - DRAW_POINT)) {
        x = (LCD_PIXEL_WIDTH - DRAW_POINT);
    }
    if ((y - (DRAW_POINT / 2)) >= 0) {
        y -= (DRAW_POINT / 2);
    }
    if (y > (LCD_PIXEL_HEIGHT - DRAW_POINT)) {
        y = (LCD_PIXEL_HEIGHT - DRAW_POINT);
    }
    idx_base = (x + (LCD_PIXEL_WIDTH * y)) * TOUCH_BUFFER_BYTE_PER_PIXEL;

    /* Select color */
    if (id == 0) {
        /* Blue */
        coller_pix[0] = 0x0F;  /* 4:Green 4:Blue */
        coller_pix[1] = 0xF0;  /* 4:Alpha 4:Red  */
    } else {
        /* Pink */
        coller_pix[0] = 0x07;  /* 4:Green 4:Blue */
        coller_pix[1] = 0xFF;  /* 4:Alpha 4:Red  */
    }

    /* Drawing */
    for (i = 0; i < DRAW_POINT; i++) {
        wk_idx = idx_base + (LCD_PIXEL_WIDTH * TOUCH_BUFFER_BYTE_PER_PIXEL * i);
        for (j = 0; j < DRAW_POINT; j++) {
            p_buf[wk_idx++] = coller_pix[0];
            p_buf[wk_idx++] = coller_pix[1];
        }
    }
}

static void draw_touch_keyonoff(uint8_t * p_buf, int id, bool onoff) {
    int idx_base;
    int wk_idx;
    int i;
    int j;
    uint8_t coller_pix[TOUCH_BUFFER_BYTE_PER_PIXEL];  /* ARGB4444 */

    /* Display position */
    if (id == 0) {
        idx_base = 0;
    } else {
        idx_base = DRAW_POINT * TOUCH_BUFFER_BYTE_PER_PIXEL;
    }

    /* Select color */
    if (onoff == false) {
        /* Transparency */
        coller_pix[0] = 0x00;  /* 4:Green 4:Blue */
        coller_pix[1] = 0x00;  /* 4:Alpha 4:Red  */
    } else {
        /* White */
        coller_pix[0] = 0xff;  /* 4:Green 4:Blue */
        coller_pix[1] = 0xff;  /* 4:Alpha 4:Red  */
    }

    /* Drawing */
    for (i = 0; i < DRAW_POINT; i++) {
        wk_idx = idx_base + (LCD_PIXEL_WIDTH * TOUCH_BUFFER_BYTE_PER_PIXEL * i);
        for (j = 0; j < DRAW_POINT; j++) {
            p_buf[wk_idx++] = coller_pix[0];
            p_buf[wk_idx++] = coller_pix[1];
        }
    }
}

static void touch_int_callback(void) {
    sem_touch_int.release();
}

static void touch_task(void) {
    DisplayBase::rect_t rect;
    TouchKey::touch_pos_t touch_pos[TOUCH_NUM];
    int touch_num = 0;
    int touch_num_last = 0;
    int i;

    /* The layer by which the touch panel location is drawn */
    memset(user_frame_buffer_touch, 0, sizeof(user_frame_buffer_touch));
    dcache_clean(user_frame_buffer_touch, sizeof(user_frame_buffer_touch));
    rect.vs = 0;
    rect.vw = LCD_PIXEL_HEIGHT;
    rect.hs = 0;
    rect.hw = LCD_PIXEL_WIDTH;
    Display.Graphics_Read_Setting(
        DisplayBase::GRAPHICS_LAYER_1,
        (void *)user_frame_buffer_touch,
        TOUCH_BUFFER_STRIDE,
        DisplayBase::GRAPHICS_FORMAT_ARGB4444,
        DisplayBase::WR_RD_WRSWA_32_16BIT,
        &rect
    );
    Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_1);

    /* Callback setting */
    touch.SetCallback(&touch_int_callback);

    /* Reset touch IC */
    touch.Reset();

    while (1) {
        /* Wait touch event */
        sem_touch_int.wait();

        /* Get touch coordinates */
        touch_num = touch.GetCoordinates(TOUCH_NUM, touch_pos);

        /* When it's a new touch, touch frame buffer is initialized */
        if ((touch_num != 0) && (touch_num_last == 0)) {
            memset(user_frame_buffer_touch, 0, sizeof(user_frame_buffer_touch));
        }
        touch_num_last = touch_num;

        /* Drawing of a touch coordinate */
        for (i = 0; i < TOUCH_NUM; i ++) {
            printf("{valid=%d,x=%d,y=%d} ", touch_pos[i].valid, touch_pos[i].x, touch_pos[i].y);
            draw_touch_keyonoff(user_frame_buffer_touch, i, touch_pos[i].valid);
            if (touch_pos[i].valid) {
                draw_touch_pos(user_frame_buffer_touch, i, touch_pos[i].x, touch_pos[i].y);
            }
        }
        printf("\n");

        /* Data cache clean */
        dcache_clean(user_frame_buffer_touch, sizeof(user_frame_buffer_touch));
    }
}

/****** main ******/
int main(void) {
    DisplayBase::rect_t rect;
    char test_cnt = 0x20;

    /* Change the baud rate of the printf() */
    pc.baud(921600);

    /* Initialization of LCD */
    Init_LCD_Display();    /* When using LCD, please call before than Init_Video(). */

    /* Initialization of Video */
    Init_Video();

    /* Start Video and Lcd processing */
    p_VideoLcdTask = new Thread;
    p_VideoLcdTask->start(video_lcd_task);

    /* Start touch panel processing */
    Thread touchTask;
    touchTask.start(touch_task);

    /* The layer by which the character string is drawn */
    memset(user_frame_buffer_string, 0, sizeof(user_frame_buffer_string));
    dcache_clean(user_frame_buffer_string, sizeof(user_frame_buffer_string));
    rect.vs = LCD_PIXEL_HEIGHT - STRING_PIXEL_VM - 10;
    rect.vw = STRING_PIXEL_VM;
    rect.hs = LCD_PIXEL_WIDTH - STRING_PIXEL_HW - 10;
    rect.hw = STRING_PIXEL_HW;
    Display.Graphics_Read_Setting(
        DisplayBase::GRAPHICS_LAYER_2,
        (void *)user_frame_buffer_string,
        STRING_BUFFER_STRIDE,
        DisplayBase::GRAPHICS_FORMAT_ARGB4444,
        DisplayBase::WR_RD_WRSWA_32_16BIT,
        &rect
    );
    Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_2);

    /* String */
    AsciiFont ascii_font(user_frame_buffer_string, STRING_PIXEL_HW, STRING_PIXEL_VM,
                         STRING_BUFFER_STRIDE, STRING_BUFFER_BYTE_PER_PIXEL);
    ascii_font.DrawStr("Font:", 0, 8, 0x0000ffff, 2);
    while (1) {
        led_blue = !led_blue;
        //colour: rrrrGBAR (r:Reserve G:Green B:Blue A:Alpha R:Red
        ascii_font.DrawChar(test_cnt, 84, 0, 0x0000aa9f, 3);
        if (test_cnt < 0x7e) {
            test_cnt++;
        } else {
            test_cnt = 0x20;
        }
        dcache_clean(user_frame_buffer_string, sizeof(user_frame_buffer_string));
        Thread::wait(1000);
    }
}