野火4.3寸触摸屏幕移植LVGL–HAL库

野火4.3寸触摸屏幕移植LVGL--HAL库

工具:

野火 电容触摸屏—触摸画板_4.3寸ILI9806G(MCU接口800*480 8080时序)

keil5

与开发板连接

这里不在赘述,详情参考下面文章

野火stm32f407霸天虎V2开发板使用cubemx驱动4.3寸LCD屏_stm32cube nt35510-CSDN博客

LVGL移植

网络上文章遍地,不在赘述,这里主要针对个别产品

显示移植

主要修改 lv_port_disp_template.c文件中disp_flush函数

官方标准库驱动

硬件初始化删除:

在官方库里,包含了各种端口外设初始化,但是在HAL库移植时,这些本应该在cubemx中就配置好,所以应该删掉(cubemx配置参照上面文章连接)

宏定义的更改:

将标准库函数替换为HAL库函数,宏定义转换为HAL库所需要的

官方绘图函数删减:

在官方给的驱动中,有各种绘图函数,但是在移植LVGL时,这些是多余的,因为LVGL已经内置各种窗口绘制,文字的打印

#ifndef      __BSP_ILI9806G_LCD_H
#define      __BSP_ILI9806G_LCD_H

#include "stm32f4xx.h"
#include "./font/fonts.h"

/***************************************************************************************
2^26 =0X0400 0000 = 64MB,每个 BANK 有4*64MB = 256MB
64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF
64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF
64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF
64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF

选择BANK1-BORSRAM3 连接 TFT,地址范围为0X6800 0000 ~ 0X6BFF FFFF
FSMC_A0 接LCD的DC(寄存器/数据选择)脚
寄存器基地址 = 0X6C00 0000
RAM基地址 = 0X6D00 0000 = 0X6C00 0000+2^0*2 = 0X6800 0000 + 0X2 = 0X6800 0002
当选择不同的地址线时,地址要重新计算  
****************************************************************************************/

/******************************* ILI9806G 显示屏的 FSMC 参数定义 ***************************/
//FSMC_Bank1_NORSRAM用于LCD命令操作的地址
#define      FSMC_Addr_ILI9806G_CMD         ( ( uint32_t ) 0x68000000 )

//FSMC_Bank1_NORSRAM用于LCD数据操作的地址      
#define      FSMC_Addr_ILI9806G_DATA        ( ( uint32_t ) 0x68000002 )

//由片选引脚决定的NOR/SRAM块
#define      FSMC_Bank1_NORSRAMx           FSMC_Bank1_NORSRAM3

/******************************* ILI9806G 显示屏8080通讯引脚定义 ***************************/
/******控制信号线******/
#define      FSMC_AF                       GPIO_AF_FSMC
//片选,选择NOR/SRAM块
#define      ILI9806G_CS_CLK                RCC_AHB1Periph_GPIOG  
#define      ILI9806G_CS_PORT               GPIOG
#define      ILI9806G_CS_PIN                GPIO_Pin_10
#define      ILI9806G_CS_PinSource          GPIO_PinSource10

//DC引脚,使用FSMC的地址信号控制,本引脚决定了访问LCD时使用的地址
//PF0为FSMC_A0
#define      ILI9806G_DC_CLK                RCC_AHB1Periph_GPIOF  
#define      ILI9806G_DC_PORT               GPIOF
#define      ILI9806G_DC_PIN                GPIO_Pin_0
#define      ILI9806G_DC_PinSource          GPIO_PinSource0

//写使能
#define      ILI9806G_WR_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_WR_PORT               GPIOD
#define      ILI9806G_WR_PIN                GPIO_Pin_5
#define      ILI9806G_WR_PinSource          GPIO_PinSource5

//读使能
#define      ILI9806G_RD_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_RD_PORT               GPIOD
#define      ILI9806G_RD_PIN                GPIO_Pin_4
#define      ILI9806G_RD_PinSource          GPIO_PinSource4

//复位引脚
#define      ILI9806G_RST_CLK               RCC_AHB1Periph_GPIOF 
#define      ILI9806G_RST_PORT              GPIOF
#define      ILI9806G_RST_PIN               GPIO_Pin_11

//背光引脚
#define      ILI9806G_BK_CLK                RCC_AHB1Periph_GPIOF   
#define      ILI9806G_BK_PORT               GPIOF
#define      ILI9806G_BK_PIN                GPIO_Pin_9

/********数据信号线***************/
#define      ILI9806G_D0_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D0_PORT               GPIOD
#define      ILI9806G_D0_PIN                GPIO_Pin_14
#define      ILI9806G_D0_PinSource          GPIO_PinSource14

#define      ILI9806G_D1_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D1_PORT               GPIOD
#define      ILI9806G_D1_PIN                GPIO_Pin_15
#define      ILI9806G_D1_PinSource          GPIO_PinSource15

#define      ILI9806G_D2_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D2_PORT               GPIOD
#define      ILI9806G_D2_PIN                GPIO_Pin_0
#define      ILI9806G_D2_PinSource          GPIO_PinSource0

#define      ILI9806G_D3_CLK                RCC_AHB1Periph_GPIOD  
#define      ILI9806G_D3_PORT               GPIOD
#define      ILI9806G_D3_PIN                GPIO_Pin_1
#define      ILI9806G_D3_PinSource          GPIO_PinSource1

#define      ILI9806G_D4_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D4_PORT               GPIOE
#define      ILI9806G_D4_PIN                GPIO_Pin_7
#define      ILI9806G_D4_PinSource          GPIO_PinSource7

#define      ILI9806G_D5_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D5_PORT               GPIOE
#define      ILI9806G_D5_PIN                GPIO_Pin_8
#define      ILI9806G_D5_PinSource          GPIO_PinSource8

#define      ILI9806G_D6_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D6_PORT               GPIOE
#define      ILI9806G_D6_PIN                GPIO_Pin_9
#define      ILI9806G_D6_PinSource          GPIO_PinSource9

#define      ILI9806G_D7_CLK                RCC_AHB1Periph_GPIOE  
#define      ILI9806G_D7_PORT               GPIOE
#define      ILI9806G_D7_PIN                GPIO_Pin_10
#define      ILI9806G_D7_PinSource          GPIO_PinSource10

#define      ILI9806G_D8_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D8_PORT               GPIOE
#define      ILI9806G_D8_PIN                GPIO_Pin_11
#define      ILI9806G_D8_PinSource          GPIO_PinSource11

#define      ILI9806G_D9_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D9_PORT               GPIOE
#define      ILI9806G_D9_PIN                GPIO_Pin_12
#define      ILI9806G_D9_PinSource          GPIO_PinSource12

#define      ILI9806G_D10_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D10_PORT               GPIOE
#define      ILI9806G_D10_PIN                GPIO_Pin_13
#define      ILI9806G_D10_PinSource          GPIO_PinSource13

#define      ILI9806G_D11_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D11_PORT               GPIOE
#define      ILI9806G_D11_PIN                GPIO_Pin_14
#define      ILI9806G_D11_PinSource          GPIO_PinSource14

#define      ILI9806G_D12_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D12_PORT               GPIOE
#define      ILI9806G_D12_PIN                GPIO_Pin_15
#define      ILI9806G_D12_PinSource          GPIO_PinSource15

#define      ILI9806G_D13_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D13_PORT               GPIOD
#define      ILI9806G_D13_PIN                GPIO_Pin_8
#define      ILI9806G_D13_PinSource          GPIO_PinSource8

#define      ILI9806G_D14_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D14_PORT               GPIOD
#define      ILI9806G_D14_PIN                GPIO_Pin_9
#define      ILI9806G_D14_PinSource          GPIO_PinSource9

#define      ILI9806G_D15_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D15_PORT               GPIOD
#define      ILI9806G_D15_PIN                GPIO_Pin_10
#define      ILI9806G_D15_PinSource          GPIO_PinSource10

/*************************************** 调试预用 ******************************************/
#define      DEBUG_DELAY()               Delay(0x5000)

/***************************** ILI934 显示区域的起始坐标和总行列数 ***************************/
#define      ILI9806G_DispWindow_X_Star         0     //起始点的X坐标
#define      ILI9806G_DispWindow_Y_Star         0     //起始点的Y坐标

#define             ILI9806G_LESS_PIXEL         480         //液晶屏较短方向的像素宽度
#define             ILI9806G_MORE_PIXEL         800         //液晶屏较长方向的像素宽度

//根据液晶扫描方向而变化的XY像素宽度
//调用ILI9806G_GramScan函数设置方向时会自动更改
extern uint16_t LCD_X_LENGTH,LCD_Y_LENGTH; 

//液晶屏扫描模式
//参数可选值为0-7
extern uint8_t LCD_SCAN_MODE;

/******************************* 定义 ILI934 显示屏常用颜色 ********************************/
#define      BACKGROUND                     BLACK   //默认背景颜色

#define      WHITE                                0xFFFF       //白色
#define      BLACK                         0x0000      //黑色 
#define      GREY                          0xF7DE      //灰色 
#define      BLUE                          0x001F      //蓝色 
#define      BLUE2                         0x051F      //浅蓝色 
#define      RED                           0xF800      //红色 
#define      MAGENTA                       0xF81F      //红紫色,洋红色 
#define      GREEN                         0x07E0      //绿色 
#define      CYAN                          0x7FFF      //蓝绿色,青色 
#define      YELLOW                        0xFFE0      //黄色 
#define      BRED                          0xF81F
#define      GRED                          0xFFE0
#define      GBLUE                         0x07FF

/******************************* 定义 ILI934 常用命令 ********************************/
#define      CMD_SetCoordinateX                 0x2A         //设置X坐标
#define      CMD_SetCoordinateY                 0x2B         //设置Y坐标
#define      CMD_SetPixel                         0x2C       //填充像素

/********************************** 声明 ILI934 函数 ***************************************/
void                     ILI9806G_Init                    ( void );
void                     ILI9806G_Rst                     ( void );
void                     ILI9806G_BackLed_Control         ( FunctionalState enumState );
void                     ILI9806G_GramScan                ( uint8_t ucOtion );
void                     ILI9806G_OpenWindow              ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight );
void                     ILI9806G_Clear                   ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight );
void                     ILI9806G_SetPointPixel           ( uint16_t usX, uint16_t usY );
uint16_t                 ILI9806G_GetPointPixel           ( uint16_t usX , uint16_t usY );
void                     ILI9806G_DrawLine                ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 );
void                     ILI9806G_DrawRectangle           ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight,uint8_t ucFilled );
void                     ILI9806G_DrawCircle              ( uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled );
void                     ILI9806G_DispChar_EN             ( uint16_t usX, uint16_t usY, const char cChar );
void                     ILI9806G_DispStringLine_EN      ( uint16_t line, char * pStr );
void                     ILI9806G_DispString_EN                 ( uint16_t usX, uint16_t usY, char * pStr );
void                                            ILI9806G_DispString_EN_YDir         (   uint16_t usX,uint16_t usY ,  char * pStr );
void                     ILI9806G_DispChar_CH             ( uint16_t usX, uint16_t usY, uint16_t usChar );
void                     ILI9806G_DispString_CH           ( uint16_t usX, uint16_t usY,  char * pStr );
void                     ILI9806G_DispString_EN_CH        ( uint16_t usX, uint16_t usY,  char * pStr );
void                                            ILI9806G_DispStringLine_EN_CH   (  uint16_t line, char * pStr );
void                                            ILI9806G_DispString_EN_YDir         (   uint16_t usX,uint16_t usY ,  char * pStr );
void                                            ILI9806G_DispString_EN_CH_YDir  (   uint16_t usX,uint16_t usY , char * pStr );

void                                            LCD_SetFont                                         (sFONT *fonts);
sFONT                                       *LCD_GetFont                                            (void);
void                                            ILI9806G_ClearLine                                      (uint16_t Line);
void                                            LCD_SetBackColor                                (uint16_t Color);
void                                            LCD_SetTextColor                                (uint16_t Color)    ;
void                                            LCD_SetColors                                       (uint16_t TextColor, uint16_t BackColor);
void                                            LCD_GetColors                                       (uint16_t *TextColor, uint16_t *BackColor);

#define                                     LCD_ClearLine                       ILI9806G_ClearLine

void ILI9806G_DisplayStringEx(uint16_t x,       //字符显示位置x
                                                                 uint16_t y,                //字符显示位置y
                                                                 uint16_t Font_width,   //要显示的字体宽度,英文字符在此基础上/2。注意为偶数
                                                                 uint16_t Font_Height,  //要显示的字体高度,注意为偶数
                                                                 uint8_t *ptr,                  //显示的字符内容
                                                                 uint16_t DrawModel);  //是否反色显示

void ILI9806G_DisplayStringEx_YDir(uint16_t x,      //字符显示位置x
                                                                             uint16_t y,                //字符显示位置y
                                                                             uint16_t Font_width,   //要显示的字体宽度,英文字符在此基础上/2。注意为偶数
                                                                             uint16_t Font_Height,  //要显示的字体高度,注意为偶数
                                                                             uint8_t *ptr,                  //显示的字符内容
                                                                             uint16_t DrawModel);  //是否反色显示

#endif /* __BSP_ILI9806G_ILI9806G_H */
/**
  ******************************************************************************
  * @file    bsp_ili9806g_lcd.c
  * @version V1.0
  * @date    2013-xx-xx
  * @brief   ILI9806G液晶屏驱动
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火  STM32 F407 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 

#include "./lcd/bsp_ili9806g_lcd.h"
#include "./font/fonts.h" 

//根据液晶扫描方向而变化的XY像素宽度
//调用ILI9806G_GramScan函数设置方向时会自动更改
uint16_t LCD_X_LENGTH = ILI9806G_MORE_PIXEL;
uint16_t LCD_Y_LENGTH = ILI9806G_LESS_PIXEL;

//液晶屏扫描模式,本变量主要用于方便选择触摸屏的计算参数
//参数可选值为0-7
//调用ILI9806G_GramScan函数设置方向时会自动更改
//LCD刚初始化完成时会使用本默认值
uint8_t LCD_SCAN_MODE =3;

static sFONT *LCD_Currentfonts = &Font16x32;  //英文字体
static uint16_t CurrentTextColor   = WHITE;//前景色
static uint16_t CurrentBackColor   = BLACK;//背景色

//缓存读取回来的字模数据
static uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ]; 

static void                 ILI9806G_Write_Cmd           ( uint16_t usCmd );
static void                 ILI9806G_Write_Data          ( uint16_t usData );
static uint16_t             ILI9806G_Read_Data           ( void );
static void                   ILI9806G_Delay               ( __IO uint32_t nCount );
static void                   ILI9806G_GPIO_Config         ( void );
static void                   ILI9806G_FSMC_Config         ( void );
static void                   ILI9806G_REG_Config          ( void );
static void                   ILI9806G_SetCursor           ( uint16_t usX, uint16_t usY );
static __inline void          ILI9806G_FillColor           ( uint32_t ulAmout_Point, uint16_t usColor );
static uint16_t               ILI9806G_Read_PixelData      ( void );

/**
  * @brief  简单延时函数
  * @param  nCount :延时计数值
  * @retval 无
  */    
static void Delay ( __IO uint32_t nCount )
{
  for ( ; nCount != 0; nCount -- );

}

/**
  * @brief  向ILI9806G写入命令
  * @param  usCmd :要写入的命令(表寄存器地址)
  * @retval 无
  */    
static void ILI9806G_Write_Cmd ( uint16_t usCmd )
{
    * ( __IO uint16_t * ) ( FSMC_Addr_ILI9806G_CMD ) = usCmd;

}

/**
  * @brief  向ILI9806G写入数据
  * @param  usData :要写入的数据
  * @retval 无
  */    
static void ILI9806G_Write_Data ( uint16_t usData )
{
    * ( __IO uint16_t * ) ( FSMC_Addr_ILI9806G_DATA ) = usData;

}

/**
  * @brief  从ILI9806G读取数据
  * @param  无
  * @retval 读取到的数据
  */    
static uint16_t ILI9806G_Read_Data ( void )
{
    return ( * ( __IO uint16_t * ) ( FSMC_Addr_ILI9806G_DATA ) );

}

/**
  * @brief  用于 ILI9806G 简单延时函数
  * @param  nCount :延时计数值
  * @retval 无
  */    
static void ILI9806G_Delay ( __IO uint32_t nCount )
{
  for ( ; nCount != 0; nCount -- );

}

/**
  * @brief  初始化ILI9806G的IO引脚
  * @param  无
  * @retval 无
  */
static void ILI9806G_GPIO_Config ( void )
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /* 使能FSMC对应相应管脚时钟*/
    RCC_AHB1PeriphClockCmd (    
                                                    /*控制信号*/
                                                    ILI9806G_CS_CLK|ILI9806G_DC_CLK|ILI9806G_WR_CLK|
                                                    ILI9806G_RD_CLK |ILI9806G_BK_CLK|ILI9806G_RST_CLK|
                                                    /*数据信号*/
                                                    ILI9806G_D0_CLK|ILI9806G_D1_CLK|    ILI9806G_D2_CLK | 
                                                    ILI9806G_D3_CLK | ILI9806G_D4_CLK|ILI9806G_D5_CLK|
                                                    ILI9806G_D6_CLK | ILI9806G_D7_CLK|ILI9806G_D8_CLK|
                                                    ILI9806G_D9_CLK | ILI9806G_D10_CLK|ILI9806G_D11_CLK|
                                                    ILI9806G_D12_CLK | ILI9806G_D13_CLK|ILI9806G_D14_CLK|
                                                    ILI9806G_D15_CLK    , ENABLE );

    /* 配置FSMC相对应的数据线,FSMC-D0~D15 */ 
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D0_PIN; 
    GPIO_Init(ILI9806G_D0_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D0_PORT,ILI9806G_D0_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D1_PIN; 
    GPIO_Init(ILI9806G_D1_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D1_PORT,ILI9806G_D1_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D2_PIN; 
    GPIO_Init(ILI9806G_D2_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D2_PORT,ILI9806G_D2_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D3_PIN; 
    GPIO_Init(ILI9806G_D3_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D3_PORT,ILI9806G_D3_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D4_PIN; 
    GPIO_Init(ILI9806G_D4_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D4_PORT,ILI9806G_D4_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D5_PIN; 
    GPIO_Init(ILI9806G_D5_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D5_PORT,ILI9806G_D5_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D6_PIN; 
    GPIO_Init(ILI9806G_D6_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D6_PORT,ILI9806G_D6_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D7_PIN; 
    GPIO_Init(ILI9806G_D7_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D7_PORT,ILI9806G_D7_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D8_PIN; 
    GPIO_Init(ILI9806G_D8_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D8_PORT,ILI9806G_D8_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D9_PIN; 
    GPIO_Init(ILI9806G_D9_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D9_PORT,ILI9806G_D9_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D10_PIN; 
    GPIO_Init(ILI9806G_D10_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D10_PORT,ILI9806G_D10_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D11_PIN; 
    GPIO_Init(ILI9806G_D11_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D11_PORT,ILI9806G_D11_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D12_PIN; 
    GPIO_Init(ILI9806G_D12_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D12_PORT,ILI9806G_D12_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D13_PIN; 
    GPIO_Init(ILI9806G_D13_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D13_PORT,ILI9806G_D13_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D14_PIN; 
    GPIO_Init(ILI9806G_D14_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D14_PORT,ILI9806G_D14_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_D15_PIN; 
    GPIO_Init(ILI9806G_D15_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_D15_PORT,ILI9806G_D15_PinSource,FSMC_AF);

    /* 配置FSMC相对应的控制线
     * FSMC_NOE   :LCD-RD
     * FSMC_NWE   :LCD-WR
     * FSMC_NE1   :LCD-CS
     * FSMC_A0    :LCD-DC
     */
    GPIO_InitStructure.GPIO_Pin = ILI9806G_RD_PIN; 
    GPIO_Init(ILI9806G_RD_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_RD_PORT,ILI9806G_RD_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_WR_PIN; 
    GPIO_Init(ILI9806G_WR_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_WR_PORT,ILI9806G_WR_PinSource,FSMC_AF);

    GPIO_InitStructure.GPIO_Pin = ILI9806G_CS_PIN; 
    GPIO_Init(ILI9806G_CS_PORT, &GPIO_InitStructure);   
    GPIO_PinAFConfig(ILI9806G_CS_PORT,ILI9806G_CS_PinSource,FSMC_AF);  

    GPIO_InitStructure.GPIO_Pin = ILI9806G_DC_PIN; 
    GPIO_Init(ILI9806G_DC_PORT, &GPIO_InitStructure);
    GPIO_PinAFConfig(ILI9806G_DC_PORT,ILI9806G_DC_PinSource,FSMC_AF);

  /* 配置LCD复位RST控制管脚*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Pin = ILI9806G_RST_PIN; 
    GPIO_Init ( ILI9806G_RST_PORT, & GPIO_InitStructure );

    /* 配置LCD背光控制管脚BK*/
    GPIO_InitStructure.GPIO_Pin = ILI9806G_BK_PIN; 
    GPIO_Init ( ILI9806G_BK_PORT, & GPIO_InitStructure );

}

 /**
  * @brief  LCD  FSMC 模式配置
  * @param  无
  * @retval 无
  */
static void ILI9806G_FSMC_Config ( void )
{
    FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
    FSMC_NORSRAMTimingInitTypeDef  readWriteTiming;     

    /* 使能FSMC时钟*/
    RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);

    //地址建立时间(ADDSET)为1个HCLK 5/168M=30ns
    readWriteTiming.FSMC_AddressSetupTime      = 0x04;   //地址建立时间
    //数据保持时间(DATAST)+ 1个HCLK = 12/168M=72ns 
    readWriteTiming.FSMC_DataSetupTime         = 0x0b;   //数据建立时间
    //选择控制的模式
    //模式B,异步NOR FLASH模式,与ILI9806G的8080时序匹配
    readWriteTiming.FSMC_AccessMode            = FSMC_AccessMode_B; 

    /*以下配置与模式B无关*/
    //地址保持时间(ADDHLD)模式A未用到
    readWriteTiming.FSMC_AddressHoldTime       = 0x00;   //地址保持时间
    //设置总线转换周期,仅用于复用模式的NOR操作
    readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
    //设置时钟分频,仅用于同步类型的存储器
    readWriteTiming.FSMC_CLKDivision           = 0x00;
    //数据保持时间,仅用于同步型的NOR 
    readWriteTiming.FSMC_DataLatency           = 0x00;  

    FSMC_NORSRAMInitStructure.FSMC_Bank                  = FSMC_Bank1_NORSRAMx;
    FSMC_NORSRAMInitStructure.FSMC_DataAddressMux        = FSMC_DataAddressMux_Disable;
    FSMC_NORSRAMInitStructure.FSMC_MemoryType            = FSMC_MemoryType_NOR;
    FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth       = FSMC_MemoryDataWidth_16b;
    FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode       = FSMC_BurstAccessMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity    = FSMC_WaitSignalPolarity_Low;
    FSMC_NORSRAMInitStructure.FSMC_WrapMode              = FSMC_WrapMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive      = FSMC_WaitSignalActive_BeforeWaitState;
    FSMC_NORSRAMInitStructure.FSMC_WriteOperation        = FSMC_WriteOperation_Enable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignal            = FSMC_WaitSignal_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ExtendedMode          = FSMC_ExtendedMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WriteBurst            = FSMC_WriteBurst_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;
    FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct     = &readWriteTiming;  

    FSMC_NORSRAMInit ( & FSMC_NORSRAMInitStructure ); 

    /* 使能 FSMC_Bank1_NORSRAM3 */
    FSMC_NORSRAMCmd ( FSMC_Bank1_NORSRAMx, ENABLE );  

}

/**
 * @brief  初始化ILI9806G寄存器
 * @param  无
 * @retval 无
 */
/**
 * @brief  初始化ILI9806G寄存器
 * @param  无
 * @retval 无
 */
static void ILI9806G_REG_Config ( void )
{   

    //************* Start Initial Sequence **********//
    ILI9806G_Write_Cmd(0xFF); // EXTC Command Set enable register 
    ILI9806G_Write_Data(0xFF); 
    ILI9806G_Write_Data(0x98); 
    ILI9806G_Write_Data(0x06); 

    ILI9806G_Write_Cmd(0xBA); // SPI Interface Setting 
    ILI9806G_Write_Data(0x60); 

    ILI9806G_Write_Cmd(0xBC); // GIP 1 
    ILI9806G_Write_Data(0x01); 
    ILI9806G_Write_Data(0x0E); 
    ILI9806G_Write_Data(0x61); 
    ILI9806G_Write_Data(0xFB); 
    ILI9806G_Write_Data(0x10); 
    ILI9806G_Write_Data(0x10); 
    ILI9806G_Write_Data(0x0B); 
    ILI9806G_Write_Data(0x0F); 
    ILI9806G_Write_Data(0x2E); 
    ILI9806G_Write_Data(0x73); 
    ILI9806G_Write_Data(0xFF); 
    ILI9806G_Write_Data(0xFF); 
    ILI9806G_Write_Data(0x0E); 
    ILI9806G_Write_Data(0x0E); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x03); 
    ILI9806G_Write_Data(0x66); 
    ILI9806G_Write_Data(0x63); 
    ILI9806G_Write_Data(0x01); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 

    ILI9806G_Write_Cmd(0xBD); // GIP 2 
    ILI9806G_Write_Data(0x01); 
    ILI9806G_Write_Data(0x23); 
    ILI9806G_Write_Data(0x45); 
    ILI9806G_Write_Data(0x67); 
    ILI9806G_Write_Data(0x01); 
    ILI9806G_Write_Data(0x23); 
    ILI9806G_Write_Data(0x45); 
    ILI9806G_Write_Data(0x67); 

    ILI9806G_Write_Cmd(0xBE); // GIP 3 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x21); 
    ILI9806G_Write_Data(0xAB); 
    ILI9806G_Write_Data(0x60); 
    ILI9806G_Write_Data(0x22); 
    ILI9806G_Write_Data(0x22); 
    ILI9806G_Write_Data(0x22); 
    ILI9806G_Write_Data(0x22); 
    ILI9806G_Write_Data(0x22); 

    ILI9806G_Write_Cmd(0xC7); // Vcom 
    ILI9806G_Write_Data(0x6F); 

    ILI9806G_Write_Cmd(0xED); // EN_volt_reg 
    ILI9806G_Write_Data(0x7F); 
    ILI9806G_Write_Data(0x0F); 
    ILI9806G_Write_Data(0x00); 

    ILI9806G_Write_Cmd(0xC0); // Power Control 1
    ILI9806G_Write_Data(0x37); 
    ILI9806G_Write_Data(0x0B); 
    ILI9806G_Write_Data(0x0A); 

    ILI9806G_Write_Cmd(0xFC); // LVGL 
    ILI9806G_Write_Data(0x0A); 

    ILI9806G_Write_Cmd(0xDF); // Engineering Setting 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x20); 

    ILI9806G_Write_Cmd(0xF3); // DVDD Voltage Setting 
    ILI9806G_Write_Data(0x74); 

    ILI9806G_Write_Cmd(0xB4); // Display Inversion Control 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 

    ILI9806G_Write_Cmd(0xF7); // 480x800
    ILI9806G_Write_Data(0x8A); 

    ILI9806G_Write_Cmd(0xB1); // Frame Rate 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x12); 
    ILI9806G_Write_Data(0x13); 

    ILI9806G_Write_Cmd(0xF2); //Panel Timing Control 
    ILI9806G_Write_Data(0x80); 
    ILI9806G_Write_Data(0x5B); 
    ILI9806G_Write_Data(0x40); 
    ILI9806G_Write_Data(0x28); 

    ILI9806G_Write_Cmd(0xC1); // Power Control 2 
    ILI9806G_Write_Data(0x17); 
    ILI9806G_Write_Data(0x7D); 
    ILI9806G_Write_Data(0x7A); 
    ILI9806G_Write_Data(0x20); 

    ILI9806G_Write_Cmd(0xE0); 
    ILI9806G_Write_Data(0x00); //P1 
    ILI9806G_Write_Data(0x11); //P2 
    ILI9806G_Write_Data(0x1C); //P3 
    ILI9806G_Write_Data(0x0E); //P4 
    ILI9806G_Write_Data(0x0F); //P5 
    ILI9806G_Write_Data(0x0C); //P6 
    ILI9806G_Write_Data(0xC7); //P7 
    ILI9806G_Write_Data(0x06); //P8 
    ILI9806G_Write_Data(0x06); //P9 
    ILI9806G_Write_Data(0x0A); //P10 
    ILI9806G_Write_Data(0x10); //P11 
    ILI9806G_Write_Data(0x12); //P12 
    ILI9806G_Write_Data(0x0A); //P13 
    ILI9806G_Write_Data(0x10); //P14 
    ILI9806G_Write_Data(0x02); //P15 
    ILI9806G_Write_Data(0x00); //P16 

    ILI9806G_Write_Cmd(0xE1); 
    ILI9806G_Write_Data(0x00); //P1 
    ILI9806G_Write_Data(0x12); //P2 
    ILI9806G_Write_Data(0x18); //P3 
    ILI9806G_Write_Data(0x0C); //P4 
    ILI9806G_Write_Data(0x0F); //P5 
    ILI9806G_Write_Data(0x0A); //P6 
    ILI9806G_Write_Data(0x77); //P7 
    ILI9806G_Write_Data(0x06); //P8 
    ILI9806G_Write_Data(0x07); //P9 
    ILI9806G_Write_Data(0x0A); //P10 
    ILI9806G_Write_Data(0x0E); //P11 
    ILI9806G_Write_Data(0x0B); //P12 
    ILI9806G_Write_Data(0x10); //P13 
    ILI9806G_Write_Data(0x1D); //P14 
    ILI9806G_Write_Data(0x17); //P15 
    ILI9806G_Write_Data(0x00); //P16 

    ILI9806G_Write_Cmd(0x35); //Tearing Effect ON 
    ILI9806G_Write_Data(0x00); 

    ILI9806G_Write_Cmd(0x3A);
    ILI9806G_Write_Data(0x55);

    ILI9806G_Write_Cmd(0x11); //Exit Sleep 
    DEBUG_DELAY ();
    ILI9806G_Write_Cmd(0x29); // Display On 

}

/**
 * @brief  ILI9806G初始化函数,如果要用到lcd,一定要调用这个函数
 * @param  无
 * @retval 无
 */
void ILI9806G_Init ( void )
{
    ILI9806G_GPIO_Config ();
    ILI9806G_FSMC_Config ();

    ILI9806G_Rst ();
    ILI9806G_REG_Config ();

    //设置默认扫描方向,其中 6 模式为大部分液晶例程的默认显示方向  
    ILI9806G_GramScan(LCD_SCAN_MODE);

    ILI9806G_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);  /* 清屏,显示全黑 */
    ILI9806G_BackLed_Control ( ENABLE );      //点亮LCD背光灯
}

/**
 * @brief  ILI9806G背光LED控制
 * @param  enumState :决定是否使能背光LED
  *   该参数为以下值之一:
  *     @arg ENABLE :使能背光LED
  *     @arg DISABLE :禁用背光LED
 * @retval 无
 */
void ILI9806G_BackLed_Control ( FunctionalState enumState )
{
    if ( enumState )
         GPIO_SetBits( ILI9806G_BK_PORT, ILI9806G_BK_PIN ); 
    else
         GPIO_ResetBits( ILI9806G_BK_PORT, ILI9806G_BK_PIN );

}

/**
 * @brief  ILI9806G 软件复位
 * @param  无
 * @retval 无
 */
void ILI9806G_Rst ( void )
{           
    GPIO_ResetBits ( ILI9806G_RST_PORT, ILI9806G_RST_PIN );  //低电平复位

    ILI9806G_Delay ( 30000 );                      

    GPIO_SetBits ( ILI9806G_RST_PORT, ILI9806G_RST_PIN );            

    ILI9806G_Delay ( 30000 );   

}

/**
 * @brief  设置ILI9806G的GRAM的扫描方向 
 * @param  ucOption :选择GRAM的扫描方向 
 *     @arg 0-7 :参数可选值为0-7这八个方向
 *
 *  !!!其中0、3、5、6 模式适合从左至右显示文字,
 *              不推荐使用其它模式显示文字   其它模式显示文字会有镜像效果          
 *      
 *  其中0、2、4、6 模式的X方向像素为480,Y方向像素为854
 *  其中1、3、5、7 模式下X方向像素为854,Y方向像素为480
 *
 *  其中 6 模式为大部分液晶例程的默认显示方向
 *  其中 3 模式为摄像头例程使用的方向
 *  其中 0 模式为BMP图片显示例程使用的方向
 *
 * @retval 无
 * @note  坐标图例:A表示向上,V表示向下,<表示向左,>表示向右
                    X表示X轴,Y表示Y轴

------------------------------------------------------------
模式0:                .       模式1:        .   模式2:            .   模式3:                    
                    A       .                   A       .       A                   .       A                                   
                    |       .                   |       .       |                   .       |                           
                    Y       .                   X       .       Y                   .       X                   
                    0       .                   1       .       2                   .       3                   
    <--- X0 o        .   <----Y1  o       .       o 2X--->  .      o 3Y---> 
------------------------------------------------------------    
模式4:                .   模式5:            .   模式6:            .   模式7:                    
    <--- X4 o        .   <--- Y5 o        .       o 6X--->  .      o 7Y---> 
                    4       .                   5       .       6                   .       7   
                    Y       .                   X       .       Y                   .       X                       
                    |       .                   |       .       |                   .       |                           
                    V       .                   V       .       V                   .       V       
---------------------------------------------------------               
                                             LCD屏示例
                                |-----------------|
                                |           野火Logo      |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |-----------------|
                                屏幕正面(宽480,高854)

 *******************************************************/
void ILI9806G_GramScan ( uint8_t ucOption )
{   
    //参数检查,只可输入0-7
    if(ucOption >7 )
        return;

    //根据模式更新LCD_SCAN_MODE的值,主要用于触摸屏选择计算参数
    LCD_SCAN_MODE = ucOption;

    //根据模式更新XY方向的像素宽度
    if(ucOption%2 == 0) 
    {
        //0 2 4 6模式下X方向像素宽度为480,Y方向为854
        LCD_X_LENGTH = ILI9806G_LESS_PIXEL;
        LCD_Y_LENGTH =  ILI9806G_MORE_PIXEL;
    }
    else                
    {
        //1 3 5 7模式下X方向像素宽度为854,Y方向为480
        LCD_X_LENGTH = ILI9806G_MORE_PIXEL;
        LCD_Y_LENGTH =  ILI9806G_LESS_PIXEL; 
    }

    //0x36命令参数的高3位可用于设置GRAM扫描方向 
    ILI9806G_Write_Cmd ( 0x36 ); 
    ILI9806G_Write_Data (0x00 | (ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
    ILI9806G_Write_Cmd ( CMD_SetCoordinateX ); 
    ILI9806G_Write_Data ( 0x00 );       /* x 起始坐标高8位 */
    ILI9806G_Write_Data ( 0x00 );       /* x 起始坐标低8位 */
    ILI9806G_Write_Data ( ((LCD_X_LENGTH-1)>>8)&0xFF ); /* x 结束坐标高8位 */   
    ILI9806G_Write_Data ( (LCD_X_LENGTH-1)&0xFF );              /* x 结束坐标低8位 */

    ILI9806G_Write_Cmd ( CMD_SetCoordinateY ); 
    ILI9806G_Write_Data ( 0x00 );       /* y 起始坐标高8位 */
    ILI9806G_Write_Data ( 0x00 );       /* y 起始坐标低8位 */
    ILI9806G_Write_Data ( ((LCD_Y_LENGTH-1)>>8)&0xFF );   /* y 结束坐标高8位 */  
    ILI9806G_Write_Data ( (LCD_Y_LENGTH-1)&0xFF );              /* y 结束坐标低8位 */

    /* write gram start */
    ILI9806G_Write_Cmd ( CMD_SetPixel );    
}

/**
 * @brief  在ILI9806G显示器上开辟一个窗口
 * @param  usX :在特定扫描方向下窗口的起点X坐标
 * @param  usY :在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth :窗口的宽度
 * @param  usHeight :窗口的高度
 * @retval 无
 */
void ILI9806G_OpenWindow ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{   
    ILI9806G_Write_Cmd ( CMD_SetCoordinateX );               /* 设置X坐标 */
    ILI9806G_Write_Data ( usX >> 8  );     /* 先高8位,然后低8位 */
    ILI9806G_Write_Data ( usX & 0xff  );     /* 设置起始点和结束点*/
    ILI9806G_Write_Data ( ( usX + usWidth - 1 ) >> 8  );
    ILI9806G_Write_Data ( ( usX + usWidth - 1 ) & 0xff  );

    ILI9806G_Write_Cmd ( CMD_SetCoordinateY );               /* 设置Y坐标*/
    ILI9806G_Write_Data ( usY >> 8  );
    ILI9806G_Write_Data ( usY & 0xff  );
    ILI9806G_Write_Data ( ( usY + usHeight - 1 ) >> 8 );
    ILI9806G_Write_Data ( ( usY + usHeight - 1) & 0xff );

}

/**
 * @brief  设定ILI9806G的光标坐标
 * @param  usX :在特定扫描方向下光标的X坐标
 * @param  usY :在特定扫描方向下光标的Y坐标
 * @retval 无
 */
static void ILI9806G_SetCursor ( uint16_t usX, uint16_t usY )   
{
    ILI9806G_OpenWindow ( usX, usY, 1, 1 );
}

/**
 * @brief  在ILI9806G显示器上以某一颜色填充像素点
 * @param  ulAmout_Point :要填充颜色的像素点的总数目
 * @param  usColor :颜色
 * @retval 无
 */
static __inline void ILI9806G_FillColor ( uint32_t ulAmout_Point, uint16_t usColor )
{
    uint32_t i = 0;

    /* memory write */
    ILI9806G_Write_Cmd ( CMD_SetPixel );    

    for ( i = 0; i < ulAmout_Point; i ++ )
        ILI9806G_Write_Data ( usColor );

}

/**
 * @brief  对ILI9806G显示器的某一窗口以某种颜色进行清屏
 * @param  usX :在特定扫描方向下窗口的起点X坐标
 * @param  usY :在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth :窗口的宽度
 * @param  usHeight :窗口的高度
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_Clear ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{
    ILI9806G_OpenWindow ( usX, usY, usWidth, usHeight );

    ILI9806G_FillColor ( usWidth * usHeight, CurrentBackColor );        

}

/**
 * @brief  对ILI9806G显示器的某一点以某种颜色进行填充
 * @param  usX :在特定扫描方向下该点的X坐标
 * @param  usY :在特定扫描方向下该点的Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_SetPointPixel ( uint16_t usX, uint16_t usY )  
{   
    if ( ( usX < LCD_X_LENGTH ) && ( usY < LCD_Y_LENGTH ) )
  {
        ILI9806G_SetCursor ( usX, usY );

        ILI9806G_FillColor ( 1, CurrentTextColor );
    }

}

/**
 * @brief  读取ILI9806G GRAN 的一个像素数据
 * @param  无
 * @retval 像素数据
 */
static uint16_t ILI9806G_Read_PixelData ( void )    
{   
    uint16_t usR=0, usG=0, usB=0 ;

    ILI9806G_Write_Cmd ( 0x2E );   /* 读数据 */

    usR = ILI9806G_Read_Data ();    /*FIRST READ OUT DUMMY DATA*/

    usR = ILI9806G_Read_Data ();    /*READ OUT RED DATA  */
    usB = ILI9806G_Read_Data ();    /*READ OUT BLUE DATA*/
    usG = ILI9806G_Read_Data ();    /*READ OUT GREEN DATA*/ 

  return ( ( ( usR >> 11 ) << 11 ) | ( ( usG >> 10 ) << 5 ) | ( usB >> 11 ) );

}

/**
 * @brief  获取 ILI9806G 显示器上某一个坐标点的像素数据
 * @param  usX :在特定扫描方向下该点的X坐标
 * @param  usY :在特定扫描方向下该点的Y坐标
 * @retval 像素数据
 */
uint16_t ILI9806G_GetPointPixel ( uint16_t usX, uint16_t usY )
{ 
    uint16_t usPixelData;

    ILI9806G_SetCursor ( usX, usY );

    usPixelData = ILI9806G_Read_PixelData ();

    return usPixelData;

}

/**
 * @brief  在 ILI9806G 显示器上使用 Bresenham 算法画线段 
 * @param  usX1 :在特定扫描方向下线段的一个端点X坐标
 * @param  usY1 :在特定扫描方向下线段的一个端点Y坐标
 * @param  usX2 :在特定扫描方向下线段的另一个端点X坐标
 * @param  usY2 :在特定扫描方向下线段的另一个端点Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DrawLine ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 )
{
    uint16_t us; 
    uint16_t usX_Current, usY_Current;

    int32_t lError_X = 0, lError_Y = 0, lDelta_X, lDelta_Y, lDistance; 
    int32_t lIncrease_X, lIncrease_Y;   

    lDelta_X = usX2 - usX1; //计算坐标增量 
    lDelta_Y = usY2 - usY1; 

    usX_Current = usX1; 
    usY_Current = usY1; 

    if ( lDelta_X > 0 ) 
        lIncrease_X = 1; //设置单步方向 

    else if ( lDelta_X == 0 ) 
        lIncrease_X = 0;//垂直线 

    else 
  { 
    lIncrease_X = -1;
    lDelta_X = - lDelta_X;
  } 

    if ( lDelta_Y > 0 )
        lIncrease_Y = 1; 

    else if ( lDelta_Y == 0 )
        lIncrease_Y = 0;//水平线 

    else 
  {
    lIncrease_Y = -1;
    lDelta_Y = - lDelta_Y;
  } 

    if (  lDelta_X > lDelta_Y )
        lDistance = lDelta_X; //选取基本增量坐标轴 

    else 
        lDistance = lDelta_Y; 

    for ( us = 0; us <= lDistance + 1; us ++ )//画线输出 
    {  
        ILI9806G_SetPointPixel ( usX_Current, usY_Current );//画点 

        lError_X += lDelta_X ; 
        lError_Y += lDelta_Y ; 

        if ( lError_X > lDistance ) 
        { 
            lError_X -= lDistance; 
            usX_Current += lIncrease_X; 
        }  

        if ( lError_Y > lDistance ) 
        { 
            lError_Y -= lDistance; 
            usY_Current += lIncrease_Y; 
        } 

    }  

}   

/**
 * @brief  在 ILI9806G 显示器上画一个矩形
 * @param  usX_Start :在特定扫描方向下矩形的起始点X坐标
 * @param  usY_Start :在特定扫描方向下矩形的起始点Y坐标
 * @param  usWidth:矩形的宽度(单位:像素)
 * @param  usHeight:矩形的高度(单位:像素)
 * @param  ucFilled :选择是否填充该矩形
  *   该参数为以下值之一:
  *     @arg 0 :空心矩形
  *     @arg 1 :实心矩形 
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DrawRectangle ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint8_t ucFilled )
{
    if ( ucFilled )
    {
        ILI9806G_OpenWindow ( usX_Start, usY_Start, usWidth, usHeight );
        ILI9806G_FillColor ( usWidth * usHeight ,CurrentTextColor); 
    }
    else
    {
        ILI9806G_DrawLine ( usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start );
        ILI9806G_DrawLine ( usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );
        ILI9806G_DrawLine ( usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1 );
        ILI9806G_DrawLine ( usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );        
    }

}

/**
 * @brief  在 ILI9806G 显示器上使用 Bresenham 算法画圆
 * @param  usX_Center :在特定扫描方向下圆心的X坐标
 * @param  usY_Center :在特定扫描方向下圆心的Y坐标
 * @param  usRadius:圆的半径(单位:像素)
 * @param  ucFilled :选择是否填充该圆
  *   该参数为以下值之一:
  *     @arg 0 :空心圆
  *     @arg 1 :实心圆
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DrawCircle ( uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled )
{
    int16_t sCurrentX, sCurrentY;
    int16_t sError;

    sCurrentX = 0; sCurrentY = usRadius;      

    sError = 3 - ( usRadius << 1 );     //判断下个点位置的标志

    while ( sCurrentX <= sCurrentY )
    {
        int16_t sCountY;

        if ( ucFilled )             
            for ( sCountY = sCurrentX; sCountY <= sCurrentY; sCountY ++ ) 
            {                      
                ILI9806G_SetPointPixel ( usX_Center + sCurrentX, usY_Center + sCountY );           //1,研究对象 
                ILI9806G_SetPointPixel ( usX_Center - sCurrentX, usY_Center + sCountY );           //2       
                ILI9806G_SetPointPixel ( usX_Center - sCountY,   usY_Center + sCurrentX );           //3
                ILI9806G_SetPointPixel ( usX_Center - sCountY,   usY_Center - sCurrentX );           //4
                ILI9806G_SetPointPixel ( usX_Center - sCurrentX, usY_Center - sCountY );           //5    
        ILI9806G_SetPointPixel ( usX_Center + sCurrentX, usY_Center - sCountY );           //6
                ILI9806G_SetPointPixel ( usX_Center + sCountY,   usY_Center - sCurrentX );           //7    
        ILI9806G_SetPointPixel ( usX_Center + sCountY,   usY_Center + sCurrentX );           //0                
            }

        else
        {          
            ILI9806G_SetPointPixel ( usX_Center + sCurrentX, usY_Center + sCurrentY );             //1,研究对象
            ILI9806G_SetPointPixel ( usX_Center - sCurrentX, usY_Center + sCurrentY );             //2      
            ILI9806G_SetPointPixel ( usX_Center - sCurrentY, usY_Center + sCurrentX );             //3
            ILI9806G_SetPointPixel ( usX_Center - sCurrentY, usY_Center - sCurrentX );             //4
            ILI9806G_SetPointPixel ( usX_Center - sCurrentX, usY_Center - sCurrentY );             //5       
            ILI9806G_SetPointPixel ( usX_Center + sCurrentX, usY_Center - sCurrentY );             //6
            ILI9806G_SetPointPixel ( usX_Center + sCurrentY, usY_Center - sCurrentX );             //7 
            ILI9806G_SetPointPixel ( usX_Center + sCurrentY, usY_Center + sCurrentX );             //0
    }           

        sCurrentX ++;

        if ( sError < 0 ) 
            sError += 4 * sCurrentX + 6;      

        else
        {
            sError += 10 + 4 * ( sCurrentX - sCurrentY );   
            sCurrentY --;
        }   

    }

}

/**
 * @brief  在 ILI9806G 显示器上显示一个英文字符
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下该点的起始Y坐标
 * @param  cChar :要显示的英文字符
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DispChar_EN ( uint16_t usX, uint16_t usY, const char cChar )
{
    uint8_t  byteCount, bitCount,fontLength;    
    uint16_t ucRelativePositon;
    uint8_t *Pfont;

    //对ascii码表偏移(字模表不包含ASCII表的前32个非图形符号)
    ucRelativePositon = cChar - ' ';

    //每个字模的字节数
    fontLength = (LCD_Currentfonts->Width*LCD_Currentfonts->Height)/8;

    //字模首地址
    /*ascii码表偏移值乘以每个字模的字节数,求出字模的偏移位置*/
    Pfont = (uint8_t *)&LCD_Currentfonts->table[ucRelativePositon * fontLength];

    //设置显示窗口
    ILI9806G_OpenWindow ( usX, usY, LCD_Currentfonts->Width, LCD_Currentfonts->Height);

    ILI9806G_Write_Cmd ( CMD_SetPixel );            

    //按字节读取字模数据
    //由于前面直接设置了显示窗口,显示数据会自动换行
    for ( byteCount = 0; byteCount < fontLength; byteCount++ )
    {
            //一位一位处理要显示的颜色
            for ( bitCount = 0; bitCount < 8; bitCount++ )
            {
                    if ( Pfont[byteCount] & (0x80>>bitCount) )
                        ILI9806G_Write_Data ( CurrentTextColor );           
                    else
                        ILI9806G_Write_Data ( CurrentBackColor );
            }   
    }   
}

/**
 * @brief  在 ILI9806G 显示器上显示英文字符串
 * @param  line :在特定扫描方向下字符串的起始Y坐标
  *   本参数可使用宏LINE(0)、LINE(1)等方式指定文字坐标,
  *   宏LINE(x)会根据当前选择的字体来计算Y坐标值。
    *       显示中文且使用LINE宏时,需要把英文字体设置成Font8x16
 * @param  pStr :要显示的英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DispStringLine_EN (  uint16_t line,  char * pStr )
{
    uint16_t usX = 0;

    while ( * pStr != '\0' )
    {
        if ( ( usX - ILI9806G_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
        {
            usX = ILI9806G_DispWindow_X_Star;
            line += LCD_Currentfonts->Height;
        }

        if ( ( line - ILI9806G_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
        {
            usX = ILI9806G_DispWindow_X_Star;
            line = ILI9806G_DispWindow_Y_Star;
        }

        ILI9806G_DispChar_EN ( usX, line, * pStr);

        pStr ++;

        usX += LCD_Currentfonts->Width;

    }

}

/**
 * @brief  在 ILI9806G 显示器上显示英文字符串
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  pStr :要显示的英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DispString_EN (   uint16_t usX ,uint16_t usY,  char * pStr )
{
    while ( * pStr != '\0' )
    {
        if ( ( usX - ILI9806G_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
        {
            usX = ILI9806G_DispWindow_X_Star;
            usY += LCD_Currentfonts->Height;
        }

        if ( ( usY - ILI9806G_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
        {
            usX = ILI9806G_DispWindow_X_Star;
            usY = ILI9806G_DispWindow_Y_Star;
        }

        ILI9806G_DispChar_EN ( usX, usY, * pStr);

        pStr ++;

        usX += LCD_Currentfonts->Width;

    }

}

/**
 * @brief  在 ILI9806G 显示器上显示英文字符串(沿Y轴方向)
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  pStr :要显示的英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DispString_EN_YDir (   uint16_t usX,uint16_t usY ,  char * pStr )
{   
    while ( * pStr != '\0' )
    {
        if ( ( usY - ILI9806G_DispWindow_Y_Star + LCD_Currentfonts->Height ) >LCD_Y_LENGTH  )
        {
            usY = ILI9806G_DispWindow_Y_Star;
            usX += LCD_Currentfonts->Width;
        }

        if ( ( usX - ILI9806G_DispWindow_X_Star + LCD_Currentfonts->Width ) >  LCD_X_LENGTH)
        {
            usX = ILI9806G_DispWindow_X_Star;
            usY = ILI9806G_DispWindow_Y_Star;
        }

        ILI9806G_DispChar_EN ( usX, usY, * pStr);

        pStr ++;

        usY += LCD_Currentfonts->Height;     
    }   
}

/**
 * @brief  在 ILI9806G 显示器上显示一个中文字符
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  usChar :要显示的中文字符(国标码)
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */ 
void ILI9806G_DispChar_CH ( uint16_t usX, uint16_t usY, uint16_t usChar )
{
    uint8_t rowCount, bitCount;
  uint32_t usTemp; 

//  占用空间太大,改成全局变量 
//  uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ];    

    //设置显示窗口
    ILI9806G_OpenWindow ( usX, usY, WIDTH_CH_CHAR, HEIGHT_CH_CHAR );

    ILI9806G_Write_Cmd ( CMD_SetPixel );

    //取字模数据  
  GetGBKCode ( ucBuffer, usChar );  

    for ( rowCount = 0; rowCount < HEIGHT_CH_CHAR; rowCount++ )
    {
    /* 取出四个字节的数据,在lcd上即是一个汉字的一行 */
        usTemp = ucBuffer [ rowCount * 4 ];
        usTemp = ( usTemp << 8 );
        usTemp |= ucBuffer [ rowCount * 4 + 1 ];
        usTemp = ( usTemp << 8 );
        usTemp |= ucBuffer [ rowCount * 4 + 2 ];
        usTemp = ( usTemp << 8 );
        usTemp |= ucBuffer [ rowCount * 4 + 3 ];

        for ( bitCount = 0; bitCount < WIDTH_CH_CHAR; bitCount ++ )
        {           
            if ( usTemp & ( 0x80000000 >> bitCount ) )  //高位在前 
              ILI9806G_Write_Data ( CurrentTextColor );             
            else
                ILI9806G_Write_Data ( CurrentBackColor );           
        }       
    }

}

/**
 * @brief  在 ILI9806G 显示器上显示中文字符串
 * @param  line :在特定扫描方向下字符串的起始Y坐标
  *   本参数可使用宏LINE(0)、LINE(1)等方式指定文字坐标,
  *   宏LINE(x)会根据当前选择的字体来计算Y坐标值。
    *       显示中文且使用LINE宏时,需要把英文字体设置成Font8x16
 * @param  pStr :要显示的英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DispString_CH (   uint16_t usX , uint16_t usY, char * pStr )
{   
    uint16_t usCh;

    while( * pStr != '\0' )
    {       
        if ( ( usX - ILI9806G_DispWindow_X_Star + WIDTH_CH_CHAR ) > LCD_X_LENGTH )
        {
            usX = ILI9806G_DispWindow_X_Star;
            usY += HEIGHT_CH_CHAR;
        }

        if ( ( usY - ILI9806G_DispWindow_Y_Star + HEIGHT_CH_CHAR ) > LCD_Y_LENGTH )
        {
            usX = ILI9806G_DispWindow_X_Star;
            usY = ILI9806G_DispWindow_Y_Star;
        }   

        usCh = * ( uint16_t * ) pStr;   
      usCh = ( usCh << 8 ) + ( usCh >> 8 );

        ILI9806G_DispChar_CH ( usX, usY, usCh );

        usX += WIDTH_CH_CHAR;

        pStr += 2;           //一个汉字两个字节 

    }      

}

/**
 * @brief  在 ILI9806G 显示器上显示中英文字符串
 * @param  line :在特定扫描方向下字符串的起始Y坐标
  *   本参数可使用宏LINE(0)、LINE(1)等方式指定文字坐标,
  *   宏LINE(x)会根据当前选择的字体来计算Y坐标值。
    *       显示中文且使用LINE宏时,需要把英文字体设置成Font8x16
 * @param  pStr :要显示的字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DispStringLine_EN_CH (  uint16_t line, char * pStr )
{
    uint16_t usCh;
    uint16_t usX = 0;

    while( * pStr != '\0' )
    {
        if ( * pStr <= 126 )             //英文字符
        {
            if ( ( usX - ILI9806G_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
            {
                usX = ILI9806G_DispWindow_X_Star;
                line += LCD_Currentfonts->Height;
            }

            if ( ( line - ILI9806G_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
            {
                usX = ILI9806G_DispWindow_X_Star;
                line = ILI9806G_DispWindow_Y_Star;
            }           

          ILI9806G_DispChar_EN ( usX, line, * pStr );

            usX +=  LCD_Currentfonts->Width;

          pStr ++;

        }

        else                                //汉字字符
        {
            if ( ( usX - ILI9806G_DispWindow_X_Star + WIDTH_CH_CHAR ) > LCD_X_LENGTH )
            {
                usX = ILI9806G_DispWindow_X_Star;
                line += HEIGHT_CH_CHAR;
            }

            if ( ( line - ILI9806G_DispWindow_Y_Star + HEIGHT_CH_CHAR ) > LCD_Y_LENGTH )
            {
                usX = ILI9806G_DispWindow_X_Star;
                line = ILI9806G_DispWindow_Y_Star;
            }   

            usCh = * ( uint16_t * ) pStr;   

            usCh = ( usCh << 8 ) + ( usCh >> 8 );       

            ILI9806G_DispChar_CH ( usX, line, usCh );

            usX += WIDTH_CH_CHAR;

            pStr += 2;           //一个汉字两个字节 

    }

  } 
} 

/**
 * @brief  在 ILI9806G 显示器上显示中英文字符串
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  pStr :要显示的字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DispString_EN_CH (    uint16_t usX , uint16_t usY, char * pStr )
{
    uint16_t usCh;

    while( * pStr != '\0' )
    {
        if ( * pStr <= 126 )             //英文字符
        {
            if ( ( usX - ILI9806G_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
            {
                usX = ILI9806G_DispWindow_X_Star;
                usY += LCD_Currentfonts->Height;
            }

            if ( ( usY - ILI9806G_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
            {
                usX = ILI9806G_DispWindow_X_Star;
                usY = ILI9806G_DispWindow_Y_Star;
            }           

          ILI9806G_DispChar_EN ( usX, usY, * pStr );

            usX +=  LCD_Currentfonts->Width;

          pStr ++;

        }

        else                                //汉字字符
        {
            if ( ( usX - ILI9806G_DispWindow_X_Star + WIDTH_CH_CHAR ) > LCD_X_LENGTH )
            {
                usX = ILI9806G_DispWindow_X_Star;
                usY += HEIGHT_CH_CHAR;
            }

            if ( ( usY - ILI9806G_DispWindow_Y_Star + HEIGHT_CH_CHAR ) > LCD_Y_LENGTH )
            {
                usX = ILI9806G_DispWindow_X_Star;
                usY = ILI9806G_DispWindow_Y_Star;
            }   

            usCh = * ( uint16_t * ) pStr;   

            usCh = ( usCh << 8 ) + ( usCh >> 8 );       

            ILI9806G_DispChar_CH ( usX, usY, usCh );

            usX += WIDTH_CH_CHAR;

            pStr += 2;           //一个汉字两个字节 

    }

  } 
} 

/**
 * @brief  在 ILI9806G 显示器上显示中英文字符串(沿Y轴方向)
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  pStr :要显示的中英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DispString_EN_CH_YDir (  uint16_t usX,uint16_t usY , char * pStr )
{
    uint16_t usCh;

    while( * pStr != '\0' )
    {           
            //统一使用汉字的宽高来计算换行
            if ( ( usY - ILI9806G_DispWindow_Y_Star + HEIGHT_CH_CHAR ) >LCD_Y_LENGTH  )
            {
                usY = ILI9806G_DispWindow_Y_Star;
                usX += WIDTH_CH_CHAR;
            }           
            if ( ( usX - ILI9806G_DispWindow_X_Star + WIDTH_CH_CHAR ) >  LCD_X_LENGTH)
            {
                usX = ILI9806G_DispWindow_X_Star;
                usY = ILI9806G_DispWindow_Y_Star;
            }

        //显示    
        if ( * pStr <= 126 )             //英文字符
        {           
            ILI9806G_DispChar_EN ( usX, usY, * pStr);

            pStr ++;

            usY += HEIGHT_CH_CHAR;      
        }
        else                                //汉字字符
        {           
            usCh = * ( uint16_t * ) pStr;   

            usCh = ( usCh << 8 ) + ( usCh >> 8 );       

            ILI9806G_DispChar_CH ( usX,usY , usCh );

            usY += HEIGHT_CH_CHAR;

            pStr += 2;           //一个汉字两个字节 

    }

  } 
} 

/***********************缩放字体****************************/
#define ZOOMMAXBUFF 16384
uint8_t zoomBuff[ZOOMMAXBUFF] = {0};    //用于缩放的缓存,最大支持到128*128
uint8_t zoomTempBuff[1024] = {0};

/**
 * @brief  缩放字模,缩放后的字模由1个像素点由8个数据位来表示
                                        0x01表示笔迹,0x00表示空白区
 * @param  in_width :原始字符宽度
 * @param  in_heig :原始字符高度
 * @param  out_width :缩放后的字符宽度
 * @param  out_heig:缩放后的字符高度
 * @param  in_ptr :字库输入指针   注意:1pixel 1bit
 * @param  out_ptr :缩放后的字符输出指针 注意: 1pixel 8bit
 *      out_ptr实际上没有正常输出,改成了直接输出到全局指针zoomBuff中
 * @param  en_cn :0为英文,1为中文
 * @retval 无
 */
void ILI9806G_zoomChar(uint16_t in_width,   //原始字符宽度
                                    uint16_t in_heig,       //原始字符高度
                                    uint16_t out_width, //缩放后的字符宽度
                                    uint16_t out_heig,  //缩放后的字符高度
                                    uint8_t *in_ptr,    //字库输入指针    注意:1pixel 1bit
                                    uint8_t *out_ptr, //缩放后的字符输出指针 注意: 1pixel 8bit
                                    uint8_t en_cn)      //0为英文,1为中文 
{
    uint8_t *pts,*ots;
    //根据源字模及目标字模大小,设定运算比例因子,左移16是为了把浮点运算转成定点运算
    unsigned int xrIntFloat_16=(in_width<<16)/out_width+1; 
  unsigned int yrIntFloat_16=(in_heig<<16)/out_heig+1;

    unsigned int srcy_16=0;
    unsigned int y,x;
    uint8_t *pSrcLine;

    uint16_t byteCount,bitCount;

    //检查参数是否合法
    if(in_width > 48) return;                                                //字库不允许超过48像素
    if(in_width * in_heig == 0) return; 
    if(in_width * in_heig > 48*48 ) return;                  //限制输入最大 48*48

    if(out_width * out_heig == 0) return;   
    if(out_width * out_heig >= ZOOMMAXBUFF ) return; //限制最大缩放 128*128
    pts = (uint8_t*)&zoomTempBuff;

    //为方便运算,字库的数据由1 pixel/1bit 映射到1pixel/8bit
    //0x01表示笔迹,0x00表示空白区
    if(en_cn == 0x00)//英文
    {
        //英文和中文字库上下边界不对,可在此处调整。需要注意tempBuff防止溢出
            for(byteCount=0;byteCount<in_heig*in_width/8;byteCount++)    
            {
                for(bitCount=0;bitCount<8;bitCount++)
                    {                       
                        //把源字模数据由位映射到字节
                        //in_ptr里bitX为1,则pts里整个字节值为1
                        //in_ptr里bitX为0,则pts里整个字节值为0
                        *pts++ = (in_ptr[byteCount] & (0x80>>bitCount))?1:0; 
                    }
            }               
    }
    else //中文
    {           
            for(byteCount=0;byteCount<in_heig*in_width/8;byteCount++)    
            {
                for(bitCount=0;bitCount<8;bitCount++)
                    {                       
                        //把源字模数据由位映射到字节
                        //in_ptr里bitX为1,则pts里整个字节值为1
                        //in_ptr里bitX为0,则pts里整个字节值为0
                        *pts++ = (in_ptr[byteCount] & (0x80>>bitCount))?1:0; 
                    }
            }       
    }

    //zoom过程
    pts = (uint8_t*)&zoomTempBuff;  //映射后的源数据指针
    ots = (uint8_t*)&zoomBuff;  //输出数据的指针
    for (y=0;y<out_heig;y++) /*行遍历*/
    {
                unsigned int srcx_16=0;
        pSrcLine=pts+in_width*(srcy_16>>16);              
        for (x=0;x<out_width;x++) /*行内像素遍历*/
        {
            ots[x]=pSrcLine[srcx_16>>16]; //把源字模数据复制到目标指针中
            srcx_16+=xrIntFloat_16;         //按比例偏移源像素点
        }
        srcy_16+=yrIntFloat_16;               //按比例偏移源像素点
        ots+=out_width;                     
    }
    /*!!!缩放后的字模数据直接存储到全局指针zoomBuff里了*/
    out_ptr = (uint8_t*)&zoomBuff;  //out_ptr没有正确传出,后面调用直接改成了全局变量指针!

    /*实际中如果使用out_ptr不需要下面这一句!!!
        只是因为out_ptr没有使用,会导致warning。强迫症*/
    out_ptr++; 
}           

/**
 * @brief  利用缩放后的字模显示字符
 * @param  Xpos :字符显示位置x
 * @param  Ypos :字符显示位置y
 * @param  Font_width :字符宽度
 * @param  Font_Heig:字符高度
 * @param  c :要显示的字模数据
 * @param  DrawModel :是否反色显示 
 * @retval 无
 */
void ILI9806G_DrawChar_Ex(uint16_t usX, //字符显示位置x
                                                uint16_t usY, //字符显示位置y
                                                uint16_t Font_width, //字符宽度
                                                uint16_t Font_Height,  //字符高度 
                                                uint8_t *c,                     //字模数据
                                                uint16_t DrawModel)     //是否反色显示
{
  uint32_t index = 0, counter = 0;

    //设置显示窗口
    ILI9806G_OpenWindow ( usX, usY, Font_width, Font_Height);

    ILI9806G_Write_Cmd ( CMD_SetPixel );        

    //按字节读取字模数据
    //由于前面直接设置了显示窗口,显示数据会自动换行
    for ( index = 0; index < Font_Height; index++ )
    {
            //一位一位处理要显示的颜色
            for ( counter = 0; counter < Font_width; counter++ )
            {
                    //缩放后的字模数据,以一个字节表示一个像素位
                    //整个字节值为1表示该像素为笔迹
                    //整个字节值为0表示该像素为背景
                    if ( *c++ == DrawModel )
                        ILI9806G_Write_Data ( CurrentBackColor );           
                    else
                        ILI9806G_Write_Data ( CurrentTextColor );
            }   
    }   
}

/**
 * @brief  利用缩放后的字模显示字符串
 * @param  Xpos :字符显示位置x
 * @param  Ypos :字符显示位置y
 * @param  Font_width :字符宽度,英文字符在此基础上/2。注意为偶数
 * @param  Font_Heig:字符高度,注意为偶数
 * @param  c :要显示的字符串
 * @param  DrawModel :是否反色显示 
 * @retval 无
 */
void ILI9806G_DisplayStringEx(uint16_t x,       //字符显示位置x
                                                         uint16_t y,                //字符显示位置y
                                                         uint16_t Font_width,   //要显示的字体宽度,英文字符在此基础上/2。注意为偶数
                                                         uint16_t Font_Height,  //要显示的字体高度,注意为偶数
                                                         uint8_t *ptr,                  //显示的字符内容
                                                         uint16_t DrawModel)  //是否反色显示

{
    uint16_t Charwidth = Font_width; //默认为Font_width,英文宽度为中文宽度的一半
    uint8_t *psr;
    uint8_t Ascii;  //英文
    uint16_t usCh;  //中文

    //占用空间太大,改成全局变量 
    //  uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ];    

    while ( *ptr != '\0')
    {
            /****处理换行*****/
            if ( ( x - ILI9806G_DispWindow_X_Star + Charwidth ) > LCD_X_LENGTH )
            {
                x = ILI9806G_DispWindow_X_Star;
                y += Font_Height;
            }

            if ( ( y - ILI9806G_DispWindow_Y_Star + Font_Height ) > LCD_Y_LENGTH )
            {
                x = ILI9806G_DispWindow_X_Star;
                y = ILI9806G_DispWindow_Y_Star;
            }   

        if(*ptr > 0x80) //如果是中文
        {           
            Charwidth = Font_width;
            usCh = * ( uint16_t * ) ptr;                
            usCh = ( usCh << 8 ) + ( usCh >> 8 );
            GetGBKCode ( ucBuffer, usCh );  //取字模数据
            //缩放字模数据,源字模为32*32
            ILI9806G_zoomChar(WIDTH_CH_CHAR,HEIGHT_CH_CHAR,Charwidth,Font_Height,(uint8_t *)&ucBuffer,psr,1); 
            //显示单个字符
            ILI9806G_DrawChar_Ex(x,y,Charwidth,Font_Height,(uint8_t*)&zoomBuff,DrawModel);
            x+=Charwidth;
            ptr+=2;
        }
        else
        {
                Charwidth = Font_width / 2;
                Ascii = *ptr - 32;
                //使用16*32字体缩放字模数据
                ILI9806G_zoomChar(16,32,Charwidth,Font_Height,(uint8_t *)&Font16x32.table[Ascii * Font16x32.Height*Font16x32.Width/8],psr,0);
              //显示单个字符
                ILI9806G_DrawChar_Ex(x,y,Charwidth,Font_Height,(uint8_t*)&zoomBuff,DrawModel);
                x+=Charwidth;
                ptr++;
        }
    }
}

/**
 * @brief  利用缩放后的字模显示字符串(沿Y轴方向)
 * @param  Xpos :字符显示位置x
 * @param  Ypos :字符显示位置y
 * @param  Font_width :字符宽度,英文字符在此基础上/2。注意为偶数
 * @param  Font_Heig:字符高度,注意为偶数
 * @param  c :要显示的字符串
 * @param  DrawModel :是否反色显示 
 * @retval 无
 */
void ILI9806G_DisplayStringEx_YDir(uint16_t x,      //字符显示位置x
                                                                         uint16_t y,                //字符显示位置y
                                                                         uint16_t Font_width,   //要显示的字体宽度,英文字符在此基础上/2。注意为偶数
                                                                         uint16_t Font_Height,  //要显示的字体高度,注意为偶数
                                                                         uint8_t *ptr,                  //显示的字符内容
                                                                         uint16_t DrawModel)  //是否反色显示
{
    uint16_t Charwidth = Font_width; //默认为Font_width,英文宽度为中文宽度的一半
    uint8_t *psr;
    uint8_t Ascii;  //英文
    uint16_t usCh;  //中文
    uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ];    

    while ( *ptr != '\0')
    {           
            //统一使用汉字的宽高来计算换行
            if ( ( y - ILI9806G_DispWindow_X_Star + Font_width ) > LCD_X_LENGTH )
            {
                y = ILI9806G_DispWindow_X_Star;
                x += Font_width;
            }

            if ( ( x - ILI9806G_DispWindow_Y_Star + Font_Height ) > LCD_Y_LENGTH )
            {
                y = ILI9806G_DispWindow_X_Star;
                x = ILI9806G_DispWindow_Y_Star;
            }   

        if(*ptr > 0x80) //如果是中文
        {           
            Charwidth = Font_width;
            usCh = * ( uint16_t * ) ptr;                
            usCh = ( usCh << 8 ) + ( usCh >> 8 );
            GetGBKCode ( ucBuffer, usCh );  //取字模数据
            //缩放字模数据,源字模为16*16
            ILI9806G_zoomChar(WIDTH_CH_CHAR,HEIGHT_CH_CHAR,Charwidth,Font_Height,(uint8_t *)&ucBuffer,psr,1); 
            //显示单个字符
            ILI9806G_DrawChar_Ex(x,y,Charwidth,Font_Height,(uint8_t*)&zoomBuff,DrawModel);
            y+=Font_Height;
            ptr+=2;
        }
        else
        {
                Charwidth = Font_width / 2;
                Ascii = *ptr - 32;
                //使用16*24字体缩放字模数据
                ILI9806G_zoomChar(16,24,Charwidth,Font_Height,(uint8_t *)&Font16x32.table[Ascii * Font16x32.Height*Font16x32.Width/8],psr,0);
              //显示单个字符
                ILI9806G_DrawChar_Ex(x,y,Charwidth,Font_Height,(uint8_t*)&zoomBuff,DrawModel);
                y+=Font_Height;
                ptr++;
        }
    }
}

/**
  * @brief  设置英文字体类型
  * @param  fonts: 指定要选择的字体
    *       参数为以下值之一
  *     @arg:Font24x32;
  *     @arg:Font16x24;
  *     @arg:Font8x16;
  * @retval None
  */
void LCD_SetFont(sFONT *fonts)
{
  LCD_Currentfonts = fonts;
}

/**
  * @brief  获取当前字体类型
  * @param  None.
  * @retval 返回当前字体类型
  */
sFONT *LCD_GetFont(void)
{
  return LCD_Currentfonts;
}

/**
  * @brief  设置LCD的前景(字体)及背景颜色,RGB565
  * @param  TextColor: 指定前景(字体)颜色
  * @param  BackColor: 指定背景颜色
  * @retval None
  */
void LCD_SetColors(uint16_t TextColor, uint16_t BackColor) 
{
  CurrentTextColor = TextColor; 
  CurrentBackColor = BackColor;
}

/**
  * @brief  获取LCD的前景(字体)及背景颜色,RGB565
  * @param  TextColor: 用来存储前景(字体)颜色的指针变量
  * @param  BackColor: 用来存储背景颜色的指针变量
  * @retval None
  */
void LCD_GetColors(uint16_t *TextColor, uint16_t *BackColor)
{
  *TextColor = CurrentTextColor;
  *BackColor = CurrentBackColor;
}

/**
  * @brief  设置LCD的前景(字体)颜色,RGB565
  * @param  Color: 指定前景(字体)颜色 
  * @retval None
  */
void LCD_SetTextColor(uint16_t Color)
{
  CurrentTextColor = Color;
}

/**
  * @brief  设置LCD的背景颜色,RGB565
  * @param  Color: 指定背景颜色 
  * @retval None
  */
void LCD_SetBackColor(uint16_t Color)
{
  CurrentBackColor = Color;
}

/**
  * @brief  清除某行文字
  * @param  Line: 指定要删除的行
  *   本参数可使用宏LINE(0)、LINE(1)等方式指定要删除的行,
  *   宏LINE(x)会根据当前选择的字体来计算Y坐标值,并删除当前字体高度的第x行。
  * @retval None
  */
void ILI9806G_ClearLine(uint16_t Line)
{
  ILI9806G_Clear(0,Line,LCD_X_LENGTH,((sFONT *)LCD_GetFont())->Height);  /* 清屏,显示全黑 */

}
/*********************end of file*************************/

修改至HAL库驱动

#ifndef      __BSP_ILI9806G_LCD_H
#define      __BSP_ILI9806G_LCD_H

#include "stm32f4xx.h"
#include "stm32f4xx_hal.h"

/***************************************************************************************
2^26 =0X0400 0000 = 64MB,每个 BANK 有4*64MB = 256MB  bsp_ili9806g_lcd.h
64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF
64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF
64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF
64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF

选择BANK1-BORSRAM3 连接 TFT,地址范围为0X6800 0000 ~ 0X6BFF FFFF
FSMC_A0 接LCD的DC(寄存器/数据选择)脚
寄存器基地址 = 0X6C00 0000
RAM基地址 = 0X6D00 0000 = 0X6C00 0000+2^0*2 = 0X6800 0000 + 0X2 = 0X6800 0002
当选择不同的地址线时,地址要重新计算  
****************************************************************************************/

/******************************* ILI9806G 显示屏的 FSMC 参数定义 ***************************/
//FSMC_Bank1_NORSRAM用于LCD命令操作的地址
#define      FSMC_Addr_ILI9806G_CMD         ( ( uint32_t ) 0x68000000 )

//FSMC_Bank1_NORSRAM用于LCD数据操作的地址      
#define      FSMC_Addr_ILI9806G_DATA        ( ( uint32_t ) 0x68000002 )

//由片选引脚决定的NOR/SRAM块
#define      FSMC_Bank1_NORSRAMx           FSMC_Bank1_NORSRAM3

/******************************* ILI9806G 显示屏8080通讯引脚定义 ***************************/
/******控制信号线******/
#define      FSMC_AF                       GPIO_AF_FSMC
//片选,选择NOR/SRAM块
#define      ILI9806G_CS_CLK                RCC_AHB1Periph_GPIOG  
#define      ILI9806G_CS_PORT               GPIOG
#define      ILI9806G_CS_PIN                GPIO_PIN_10
#define      ILI9806G_CS_PinSource          GPIO_PinSource10

//DC引脚,使用FSMC的地址信号控制,本引脚决定了访问LCD时使用的地址
//PF0为FSMC_A0
#define      ILI9806G_DC_CLK                RCC_AHB1Periph_GPIOF  
#define      ILI9806G_DC_PORT               GPIOF
#define      ILI9806G_DC_PIN                GPIO_PIN_0
#define      ILI9806G_DC_PinSource          GPIO_PinSource0

//写使能
#define      ILI9806G_WR_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_WR_PORT               GPIOD
#define      ILI9806G_WR_PIN                GPIO_PIN_5
#define      ILI9806G_WR_PinSource          GPIO_PinSource5

//读使能
#define      ILI9806G_RD_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_RD_PORT               GPIOD
#define      ILI9806G_RD_PIN                GPIO_PIN_4
#define      ILI9806G_RD_PinSource          GPIO_PinSource4

//复位引脚
#define      ILI9806G_RST_CLK               RCC_AHB1Periph_GPIOF 
#define      ILI9806G_RST_PORT              GPIOF
#define      ILI9806G_RST_PIN               GPIO_PIN_11

//背光引脚
#define      ILI9806G_BK_CLK                RCC_AHB1Periph_GPIOF   
#define      ILI9806G_BK_PORT               GPIOF
#define      ILI9806G_BK_PIN                GPIO_PIN_9

/********数据信号线***************/
#define      ILI9806G_D0_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D0_PORT               GPIOD
#define      ILI9806G_D0_PIN                GPIO_PIN_14
#define      ILI9806G_D0_PinSource          GPIO_PinSource14

#define      ILI9806G_D1_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D1_PORT               GPIOD
#define      ILI9806G_D1_PIN                GPIO_PIN_15
#define      ILI9806G_D1_PinSource          GPIO_PinSource15

#define      ILI9806G_D2_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D2_PORT               GPIOD
#define      ILI9806G_D2_PIN                GPIO_PIN_0
#define      ILI9806G_D2_PinSource          GPIO_PinSource0

#define      ILI9806G_D3_CLK                RCC_AHB1Periph_GPIOD  
#define      ILI9806G_D3_PORT               GPIOD
#define      ILI9806G_D3_PIN                GPIO_PIN_1
#define      ILI9806G_D3_PinSource          GPIO_PinSource1

#define      ILI9806G_D4_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D4_PORT               GPIOE
#define      ILI9806G_D4_PIN                GPIO_PIN_7
#define      ILI9806G_D4_PinSource          GPIO_PinSource7

#define      ILI9806G_D5_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D5_PORT               GPIOE
#define      ILI9806G_D5_PIN                GPIO_PIN_8
#define      ILI9806G_D5_PinSource          GPIO_PinSource8

#define      ILI9806G_D6_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D6_PORT               GPIOE
#define      ILI9806G_D6_PIN                GPIO_PIN_9
#define      ILI9806G_D6_PinSource          GPIO_PinSource9

#define      ILI9806G_D7_CLK                RCC_AHB1Periph_GPIOE  
#define      ILI9806G_D7_PORT               GPIOE
#define      ILI9806G_D7_PIN                GPIO_PIN_10
#define      ILI9806G_D7_PinSource          GPIO_PinSource10

#define      ILI9806G_D8_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D8_PORT               GPIOE
#define      ILI9806G_D8_PIN                GPIO_PIN_11
#define      ILI9806G_D8_PinSource          GPIO_PinSource11

#define      ILI9806G_D9_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D9_PORT               GPIOE
#define      ILI9806G_D9_PIN                GPIO_PIN_12
#define      ILI9806G_D9_PinSource          GPIO_PinSource12

#define      ILI9806G_D10_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D10_PORT               GPIOE
#define      ILI9806G_D10_PIN                GPIO_PIN_13
#define      ILI9806G_D10_PinSource          GPIO_PinSource13

#define      ILI9806G_D11_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D11_PORT               GPIOE
#define      ILI9806G_D11_PIN                GPIO_PIN_14
#define      ILI9806G_D11_PinSource          GPIO_PinSource14

#define      ILI9806G_D12_CLK                RCC_AHB1Periph_GPIOE   
#define      ILI9806G_D12_PORT               GPIOE
#define      ILI9806G_D12_PIN                GPIO_PIN_15
#define      ILI9806G_D12_PinSource          GPIO_PinSource15

#define      ILI9806G_D13_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D13_PORT               GPIOD
#define      ILI9806G_D13_PIN                GPIO_PIN_8
#define      ILI9806G_D13_PinSource          GPIO_PinSource8

#define      ILI9806G_D14_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D14_PORT               GPIOD
#define      ILI9806G_D14_PIN                GPIO_PIN_9
#define      ILI9806G_D14_PinSource          GPIO_PinSource9

#define      ILI9806G_D15_CLK                RCC_AHB1Periph_GPIOD   
#define      ILI9806G_D15_PORT               GPIOD
#define      ILI9806G_D15_PIN                GPIO_PIN_10
#define      ILI9806G_D15_PinSource          GPIO_PinSource10

/*************************************** 调试预用 ******************************************/
#define      DEBUG_DELAY()               Delay(0x5000)

/***************************** ILI934 显示区域的起始坐标和总行列数 ***************************/
#define      ILI9806G_DispWindow_X_Star         0     //起始点的X坐标
#define      ILI9806G_DispWindow_Y_Star         0     //起始点的Y坐标

#define             ILI9806G_LESS_PIXEL         480         //液晶屏较短方向的像素宽度
#define             ILI9806G_MORE_PIXEL         800         //液晶屏较长方向的像素宽度

//根据液晶扫描方向而变化的XY像素宽度
//调用ILI9806G_GramScan函数设置方向时会自动更改
extern uint16_t LCD_X_LENGTH,LCD_Y_LENGTH; 

//液晶屏扫描模式
//参数可选值为0-7
extern uint8_t LCD_SCAN_MODE;

/******************************* 定义 ILI934 显示屏常用颜色 ********************************/
#define      BACKGROUND                     BLACK   //默认背景颜色

#define      WHITE                                0xFFFF       //白色
#define      BLACK                         0x0000      //黑色 
#define      GREY                          0xF7DE      //灰色 
#define      BLUE                          0x001F      //蓝色 
#define      BLUE2                         0x051F      //浅蓝色 
#define      RED                           0xF800      //红色 
#define      MAGENTA                       0xF81F      //红紫色,洋红色 
#define      GREEN                         0x07E0      //绿色 
#define      CYAN                          0x7FFF      //蓝绿色,青色 
#define      YELLOW                        0xFFE0      //黄色 
#define      BRED                          0xF81F
#define      GRED                          0xFFE0
#define      GBLUE                         0x07FF

/******************************* 定义 ILI934 常用命令 ********************************/
#define      CMD_SetCoordinateX                 0x2A         //设置X坐标
#define      CMD_SetCoordinateY                 0x2B         //设置Y坐标
#define      CMD_SetPixel                         0x2C       //填充像素

/********************************** 声明 ILI934 函数 ***************************************/
void ILI9806G_Write_Cmd ( uint16_t usCmd );
void ILI9806G_Write_Data ( uint16_t usData );
void                     ILI9806G_Init                    ( void );
void                     ILI9806G_Rst                     ( void );
void                     ILI9806G_BackLed_Control         ( FunctionalState enumState );
void                     ILI9806G_GramScan                ( uint8_t ucOtion );
void                     ILI9806G_OpenWindow              ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight );
void                     ILI9806G_Clear                   ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight );
void                     ILI9806G_SetPointPixel           ( uint16_t usX, uint16_t usY );
uint16_t                 ILI9806G_GetPointPixel           ( uint16_t usX , uint16_t usY );
void                     ILI9806G_DrawLine                ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 );
void                     ILI9806G_DrawRectangle           ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight,uint8_t ucFilled );
void                     ILI9806G_DrawCircle              ( uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled );
void                     ILI9806G_DispChar_EN             ( uint16_t usX, uint16_t usY, const char cChar );
void                     ILI9806G_DispStringLine_EN      ( uint16_t line, char * pStr );
void                     ILI9806G_DispString_EN                 ( uint16_t usX, uint16_t usY, char * pStr );
void                                            ILI9806G_DispString_EN_YDir         (   uint16_t usX,uint16_t usY ,  char * pStr );
void                     ILI9806G_DispChar_CH             ( uint16_t usX, uint16_t usY, uint16_t usChar );
void                     ILI9806G_DispString_CH           ( uint16_t usX, uint16_t usY,  char * pStr );
void                     ILI9806G_DispString_EN_CH        ( uint16_t usX, uint16_t usY,  char * pStr );
void                                            ILI9806G_DispStringLine_EN_CH   (  uint16_t line, char * pStr );
void                                            ILI9806G_DispString_EN_YDir         (   uint16_t usX,uint16_t usY ,  char * pStr );
void                                            ILI9806G_DispString_EN_CH_YDir  (   uint16_t usX,uint16_t usY , char * pStr );
void                                            ILI9806G_ClearLine                                      (uint16_t Line);
void                                            LCD_SetBackColor                                (uint16_t Color);
void                                            LCD_SetTextColor                                (uint16_t Color)    ;
void                                            LCD_SetColors                                       (uint16_t TextColor, uint16_t BackColor);
void                                            LCD_GetColors                                       (uint16_t *TextColor, uint16_t *BackColor);

#define                                     LCD_ClearLine                       ILI9806G_ClearLine

void ILI9806G_DisplayStringEx(uint16_t x,       //字符显示位置x
                                                                 uint16_t y,                //字符显示位置y
                                                                 uint16_t Font_width,   //要显示的字体宽度,英文字符在此基础上/2。注意为偶数
                                                                 uint16_t Font_Height,  //要显示的字体高度,注意为偶数
                                                                 uint8_t *ptr,                  //显示的字符内容
                                                                 uint16_t DrawModel);  //是否反色显示

void ILI9806G_DisplayStringEx_YDir(uint16_t x,      //字符显示位置x
                                                                             uint16_t y,                //字符显示位置y
                                                                             uint16_t Font_width,   //要显示的字体宽度,英文字符在此基础上/2。注意为偶数
                                                                             uint16_t Font_Height,  //要显示的字体高度,注意为偶数
                                                                             uint8_t *ptr,                  //显示的字符内容
                                                                             uint16_t DrawModel);  //是否反色显示

#endif /* __BSP_ILI9806G_ILI9806G_H */
/**
  ******************************************************************************
  * @file    bsp_ili9806g_lcd.c
  * @version V1.0
  * @date    2013-xx-xx
  * @brief   ILI9806G液晶屏驱动
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火  STM32 F407 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 
#include "bsp_ili9806g_lcd.h"

//#include "./font/fonts.h"    
//根据液晶扫描方向而变化的XY像素宽度
//调用ILI9806G_GramScan函数设置方向时会自动更改
uint16_t LCD_X_LENGTH = ILI9806G_MORE_PIXEL;
uint16_t LCD_Y_LENGTH = ILI9806G_LESS_PIXEL;
//液晶屏扫描模式,本变量主要用于方便选择触摸屏的计算参数
//参数可选值为0-7
//调用ILI9806G_GramScan函数设置方向时会自动更改
//LCD刚初始化完成时会使用本默认值
uint8_t LCD_SCAN_MODE =3;

static uint16_t CurrentTextColor   = WHITE;//前景色
static uint16_t CurrentBackColor   = BLACK;//背景色

static uint16_t             ILI9806G_Read_Data           ( void );
static void                   ILI9806G_Delay               ( __IO uint32_t nCount );
static void                   ILI9806G_REG_Config          ( void );
static void                   ILI9806G_SetCursor           ( uint16_t usX, uint16_t usY );
static __inline void          ILI9806G_FillColor           ( uint32_t ulAmout_Point, uint16_t usColor );
static uint16_t               ILI9806G_Read_PixelData      ( void );

/**
  * @brief  简单延时函数
  * @param  nCount :延时计数值
  * @retval 无
  */    
static void Delay ( __IO uint32_t nCount )
{
  for ( ; nCount != 0; nCount -- );

}

/**
  * @brief  向ILI9806G写入命令
  * @param  usCmd :要写入的命令(表寄存器地址)
  * @retval 无
  */    
void ILI9806G_Write_Cmd ( uint16_t usCmd )
{
    * ( __IO uint16_t * ) ( FSMC_Addr_ILI9806G_CMD ) = usCmd;

}

/**
  * @brief  向ILI9806G写入数据
  * @param  usData :要写入的数据
  * @retval 无
  */    
void ILI9806G_Write_Data ( uint16_t usData )
{
    * ( __IO uint16_t * ) ( FSMC_Addr_ILI9806G_DATA ) = usData;

}

/**
  * @brief  从ILI9806G读取数据
  * @param  无
  * @retval 读取到的数据
  */    
static uint16_t ILI9806G_Read_Data ( void )
{
    return ( * ( __IO uint16_t * ) ( FSMC_Addr_ILI9806G_DATA ) );

}

/**
  * @brief  用于 ILI9806G 简单延时函数
  * @param  nCount :延时计数值
  * @retval 无
  */    
static void ILI9806G_Delay ( __IO uint32_t nCount )
{
  for ( ; nCount != 0; nCount -- );

}

/**
 * @brief  初始化ILI9806G寄存器
 * @param  无
 * @retval 无
 */
static void ILI9806G_REG_Config ( void )
{   

    //************* Start Initial Sequence **********//
    ILI9806G_Write_Cmd(0xFF); // EXTC Command Set enable register 
    ILI9806G_Write_Data(0xFF); 
    ILI9806G_Write_Data(0x98); 
    ILI9806G_Write_Data(0x06); 

    ILI9806G_Write_Cmd(0xBA); // SPI Interface Setting 
    ILI9806G_Write_Data(0x60); 

    ILI9806G_Write_Cmd(0xBC); // GIP 1 
    ILI9806G_Write_Data(0x01); 
    ILI9806G_Write_Data(0x0E); 
    ILI9806G_Write_Data(0x61); 
    ILI9806G_Write_Data(0xFB); 
    ILI9806G_Write_Data(0x10); 
    ILI9806G_Write_Data(0x10); 
    ILI9806G_Write_Data(0x0B); 
    ILI9806G_Write_Data(0x0F); 
    ILI9806G_Write_Data(0x2E); 
    ILI9806G_Write_Data(0x73); 
    ILI9806G_Write_Data(0xFF); 
    ILI9806G_Write_Data(0xFF); 
    ILI9806G_Write_Data(0x0E); 
    ILI9806G_Write_Data(0x0E); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x03); 
    ILI9806G_Write_Data(0x66); 
    ILI9806G_Write_Data(0x63); 
    ILI9806G_Write_Data(0x01); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 

    ILI9806G_Write_Cmd(0xBD); // GIP 2 
    ILI9806G_Write_Data(0x01); 
    ILI9806G_Write_Data(0x23); 
    ILI9806G_Write_Data(0x45); 
    ILI9806G_Write_Data(0x67); 
    ILI9806G_Write_Data(0x01); 
    ILI9806G_Write_Data(0x23); 
    ILI9806G_Write_Data(0x45); 
    ILI9806G_Write_Data(0x67); 

    ILI9806G_Write_Cmd(0xBE); // GIP 3 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x21); 
    ILI9806G_Write_Data(0xAB); 
    ILI9806G_Write_Data(0x60); 
    ILI9806G_Write_Data(0x22); 
    ILI9806G_Write_Data(0x22); 
    ILI9806G_Write_Data(0x22); 
    ILI9806G_Write_Data(0x22); 
    ILI9806G_Write_Data(0x22); 

    ILI9806G_Write_Cmd(0xC7); // Vcom 
    ILI9806G_Write_Data(0x6F); 

    ILI9806G_Write_Cmd(0xED); // EN_volt_reg 
    ILI9806G_Write_Data(0x7F); 
    ILI9806G_Write_Data(0x0F); 
    ILI9806G_Write_Data(0x00); 

    ILI9806G_Write_Cmd(0xC0); // Power Control 1
    ILI9806G_Write_Data(0x37); 
    ILI9806G_Write_Data(0x0B); 
    ILI9806G_Write_Data(0x0A); 

    ILI9806G_Write_Cmd(0xFC); // LVGL 
    ILI9806G_Write_Data(0x0A); 

    ILI9806G_Write_Cmd(0xDF); // Engineering Setting 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x20); 

    ILI9806G_Write_Cmd(0xF3); // DVDD Voltage Setting 
    ILI9806G_Write_Data(0x74); 

    ILI9806G_Write_Cmd(0xB4); // Display Inversion Control 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x00); 

    ILI9806G_Write_Cmd(0xF7); // 480x800
    ILI9806G_Write_Data(0x8A); 

    ILI9806G_Write_Cmd(0xB1); // Frame Rate 
    ILI9806G_Write_Data(0x00); 
    ILI9806G_Write_Data(0x12); 
    ILI9806G_Write_Data(0x13); 

    ILI9806G_Write_Cmd(0xF2); //Panel Timing Control 
    ILI9806G_Write_Data(0x80); 
    ILI9806G_Write_Data(0x5B); 
    ILI9806G_Write_Data(0x40); 
    ILI9806G_Write_Data(0x28); 

    ILI9806G_Write_Cmd(0xC1); // Power Control 2 
    ILI9806G_Write_Data(0x17); 
    ILI9806G_Write_Data(0x7D); 
    ILI9806G_Write_Data(0x7A); 
    ILI9806G_Write_Data(0x20); 

    ILI9806G_Write_Cmd(0xE0); 
    ILI9806G_Write_Data(0x00); //P1 
    ILI9806G_Write_Data(0x11); //P2 
    ILI9806G_Write_Data(0x1C); //P3 
    ILI9806G_Write_Data(0x0E); //P4 
    ILI9806G_Write_Data(0x0F); //P5 
    ILI9806G_Write_Data(0x0C); //P6 
    ILI9806G_Write_Data(0xC7); //P7 
    ILI9806G_Write_Data(0x06); //P8 
    ILI9806G_Write_Data(0x06); //P9 
    ILI9806G_Write_Data(0x0A); //P10 
    ILI9806G_Write_Data(0x10); //P11 
    ILI9806G_Write_Data(0x12); //P12 
    ILI9806G_Write_Data(0x0A); //P13 
    ILI9806G_Write_Data(0x10); //P14 
    ILI9806G_Write_Data(0x02); //P15 
    ILI9806G_Write_Data(0x00); //P16 

    ILI9806G_Write_Cmd(0xE1); 
    ILI9806G_Write_Data(0x00); //P1 
    ILI9806G_Write_Data(0x12); //P2 
    ILI9806G_Write_Data(0x18); //P3 
    ILI9806G_Write_Data(0x0C); //P4 
    ILI9806G_Write_Data(0x0F); //P5 
    ILI9806G_Write_Data(0x0A); //P6 
    ILI9806G_Write_Data(0x77); //P7 
    ILI9806G_Write_Data(0x06); //P8 
    ILI9806G_Write_Data(0x07); //P9 
    ILI9806G_Write_Data(0x0A); //P10 
    ILI9806G_Write_Data(0x0E); //P11 
    ILI9806G_Write_Data(0x0B); //P12 
    ILI9806G_Write_Data(0x10); //P13 
    ILI9806G_Write_Data(0x1D); //P14 
    ILI9806G_Write_Data(0x17); //P15 
    ILI9806G_Write_Data(0x00); //P16 

    ILI9806G_Write_Cmd(0x35); //Tearing Effect ON 
    ILI9806G_Write_Data(0x00); 

    ILI9806G_Write_Cmd(0x3A);
    ILI9806G_Write_Data(0x55);

    ILI9806G_Write_Cmd(0x11); //Exit Sleep 
    DEBUG_DELAY ();
    ILI9806G_Write_Cmd(0x29); // Display On 

}

/**
 * @brief  ILI9806G初始化函数,如果要用到lcd,一定要调用这个函数
 * @param  无
 * @retval 无
 */
void ILI9806G_Init ( void )
{

    ILI9806G_Rst ();
    ILI9806G_REG_Config ();

    //设置默认扫描方向,其中 6 模式为大部分液晶例程的默认显示方向  
    ILI9806G_GramScan(LCD_SCAN_MODE);

    ILI9806G_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);  /* 清屏,显示全黑 */
    ILI9806G_BackLed_Control ( ENABLE );      //点亮LCD背光灯
}

/**
 * @brief  ILI9806G背光LED控制
 * @param  enumState :决定是否使能背光LED
  *   该参数为以下值之一:
  *     @arg ENABLE :使能背光LED
  *     @arg DISABLE :禁用背光LED
 * @retval 无
 */
void ILI9806G_BackLed_Control ( FunctionalState enumState )
{
    if ( enumState )
        //  GPIO_SetBits( ILI9806G_BK_PORT, ILI9806G_BK_PIN );  
        HAL_GPIO_WritePin(ILI9806G_BK_PORT,ILI9806G_BK_PIN,GPIO_PIN_SET);
    else
        //  GPIO_ResetBits( ILI9806G_BK_PORT, ILI9806G_BK_PIN );
         HAL_GPIO_WritePin(ILI9806G_BK_PORT,ILI9806G_BK_PIN,GPIO_PIN_RESET);

}

/**
 * @brief  ILI9806G 软件复位
 * @param  无
 * @retval 无
 */
void ILI9806G_Rst ( void )
{           
    // GPIO_ResetBits ( ILI9806G_RST_PORT, ILI9806G_RST_PIN );   //低电平复位
    HAL_GPIO_WritePin(ILI9806G_RST_PORT,ILI9806G_RST_PIN,GPIO_PIN_RESET);

    ILI9806G_Delay ( 30000 );                      

    // GPIO_SetBits ( ILI9806G_RST_PORT, ILI9806G_RST_PIN );
    HAL_GPIO_WritePin(ILI9806G_RST_PORT,ILI9806G_RST_PIN,GPIO_PIN_SET);          

    ILI9806G_Delay ( 30000 );   

}

/**
 * @brief  设置ILI9806G的GRAM的扫描方向 
 * @param  ucOption :选择GRAM的扫描方向 
 *     @arg 0-7 :参数可选值为0-7这八个方向
 *
 *  !!!其中0、3、5、6 模式适合从左至右显示文字,
 *              不推荐使用其它模式显示文字   其它模式显示文字会有镜像效果          
 *      
 *  其中0、2、4、6 模式的X方向像素为480,Y方向像素为854
 *  其中1、3、5、7 模式下X方向像素为854,Y方向像素为480
 *
 *  其中 6 模式为大部分液晶例程的默认显示方向
 *  其中 3 模式为摄像头例程使用的方向
 *  其中 0 模式为BMP图片显示例程使用的方向
 *
 * @retval 无
 * @note  坐标图例:A表示向上,V表示向下,<表示向左,>表示向右
                    X表示X轴,Y表示Y轴

------------------------------------------------------------
模式0:                .       模式1:        .   模式2:            .   模式3:                    
                    A       .                   A       .       A                   .       A                                   
                    |       .                   |       .       |                   .       |                           
                    Y       .                   X       .       Y                   .       X                   
                    0       .                   1       .       2                   .       3                   
    <--- X0 o        .   <----Y1  o       .       o 2X--->  .      o 3Y---> 
------------------------------------------------------------    
模式4:                .   模式5:            .   模式6:            .   模式7:                    
    <--- X4 o        .   <--- Y5 o        .       o 6X--->  .      o 7Y---> 
                    4       .                   5       .       6                   .       7   
                    Y       .                   X       .       Y                   .       X                       
                    |       .                   |       .       |                   .       |                           
                    V       .                   V       .       V                   .       V       
---------------------------------------------------------               
                                             LCD屏示例
                                |-----------------|
                                |           野火Logo      |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |                                   |
                                |-----------------|
                                屏幕正面(宽480,高854)

 *******************************************************/
void ILI9806G_GramScan ( uint8_t ucOption )
{   
    //参数检查,只可输入0-7
    if(ucOption >7 )
        return;

    //根据模式更新LCD_SCAN_MODE的值,主要用于触摸屏选择计算参数
    LCD_SCAN_MODE = ucOption;

    //根据模式更新XY方向的像素宽度
    if(ucOption%2 == 0) 
    {
        //0 2 4 6模式下X方向像素宽度为480,Y方向为854
        LCD_X_LENGTH = ILI9806G_LESS_PIXEL;
        LCD_Y_LENGTH =  ILI9806G_MORE_PIXEL;
    }
    else                
    {
        //1 3 5 7模式下X方向像素宽度为854,Y方向为480
        LCD_X_LENGTH = ILI9806G_MORE_PIXEL;
        LCD_Y_LENGTH =  ILI9806G_LESS_PIXEL; 
    }

    //0x36命令参数的高3位可用于设置GRAM扫描方向 
    ILI9806G_Write_Cmd ( 0x36 ); 
    ILI9806G_Write_Data (0x00 | (ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
    ILI9806G_Write_Cmd ( CMD_SetCoordinateX ); 
    ILI9806G_Write_Data ( 0x00 );       /* x 起始坐标高8位 */
    ILI9806G_Write_Data ( 0x00 );       /* x 起始坐标低8位 */
    ILI9806G_Write_Data ( ((LCD_X_LENGTH-1)>>8)&0xFF ); /* x 结束坐标高8位 */   
    ILI9806G_Write_Data ( (LCD_X_LENGTH-1)&0xFF );              /* x 结束坐标低8位 */

    ILI9806G_Write_Cmd ( CMD_SetCoordinateY ); 
    ILI9806G_Write_Data ( 0x00 );       /* y 起始坐标高8位 */
    ILI9806G_Write_Data ( 0x00 );       /* y 起始坐标低8位 */
    ILI9806G_Write_Data ( ((LCD_Y_LENGTH-1)>>8)&0xFF );   /* y 结束坐标高8位 */  
    ILI9806G_Write_Data ( (LCD_Y_LENGTH-1)&0xFF );              /* y 结束坐标低8位 */

    /* write gram start */
    ILI9806G_Write_Cmd ( CMD_SetPixel );    
}

/**
 * @brief  在ILI9806G显示器上开辟一个窗口
 * @param  usX :在特定扫描方向下窗口的起点X坐标
 * @param  usY :在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth :窗口的宽度
 * @param  usHeight :窗口的高度
 * @retval 无
 */
void ILI9806G_OpenWindow ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{   
    ILI9806G_Write_Cmd ( CMD_SetCoordinateX );               /* 设置X坐标 */
    ILI9806G_Write_Data ( usX >> 8  );     /* 先高8位,然后低8位 */
    ILI9806G_Write_Data ( usX & 0xff  );     /* 设置起始点和结束点*/
    ILI9806G_Write_Data ( ( usX + usWidth - 1 ) >> 8  );
    ILI9806G_Write_Data ( ( usX + usWidth - 1 ) & 0xff  );

    ILI9806G_Write_Cmd ( CMD_SetCoordinateY );               /* 设置Y坐标*/
    ILI9806G_Write_Data ( usY >> 8  );
    ILI9806G_Write_Data ( usY & 0xff  );
    ILI9806G_Write_Data ( ( usY + usHeight - 1 ) >> 8 );
    ILI9806G_Write_Data ( ( usY + usHeight - 1) & 0xff );

}

/**
 * @brief  设定ILI9806G的光标坐标
 * @param  usX :在特定扫描方向下光标的X坐标
 * @param  usY :在特定扫描方向下光标的Y坐标
 * @retval 无
 */
static void ILI9806G_SetCursor ( uint16_t usX, uint16_t usY )   
{
    ILI9806G_OpenWindow ( usX, usY, 1, 1 );
}

/**
 * @brief  在ILI9806G显示器上以某一颜色填充像素点
 * @param  ulAmout_Point :要填充颜色的像素点的总数目
 * @param  usColor :颜色
 * @retval 无
 */
static __inline void ILI9806G_FillColor ( uint32_t ulAmout_Point, uint16_t usColor )
{
    uint32_t i = 0;

    /* memory write */
    ILI9806G_Write_Cmd ( CMD_SetPixel );    

    for ( i = 0; i < ulAmout_Point; i ++ )
        ILI9806G_Write_Data ( usColor );

}

/**
 * @brief  对ILI9806G显示器的某一窗口以某种颜色进行清屏
 * @param  usX :在特定扫描方向下窗口的起点X坐标
 * @param  usY :在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth :窗口的宽度
 * @param  usHeight :窗口的高度
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_Clear ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{
    ILI9806G_OpenWindow ( usX, usY, usWidth, usHeight );

    ILI9806G_FillColor ( usWidth * usHeight, CurrentBackColor );        

}

/**
 * @brief  对ILI9806G显示器的某一点以某种颜色进行填充
 * @param  usX :在特定扫描方向下该点的X坐标
 * @param  usY :在特定扫描方向下该点的Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_SetPointPixel ( uint16_t usX, uint16_t usY )  
{   
    if ( ( usX < LCD_X_LENGTH ) && ( usY < LCD_Y_LENGTH ) )
  {
        ILI9806G_SetCursor ( usX, usY );

        ILI9806G_FillColor ( 1, CurrentBackColor );
    }

}

/**
 * @brief  读取ILI9806G GRAN 的一个像素数据
 * @param  无
 * @retval 像素数据
 */
static uint16_t ILI9806G_Read_PixelData ( void )    
{   
    uint16_t usR=0, usG=0, usB=0 ;

    ILI9806G_Write_Cmd ( 0x2E );   /* 读数据 */

    usR = ILI9806G_Read_Data ();    /*FIRST READ OUT DUMMY DATA*/

    usR = ILI9806G_Read_Data ();    /*READ OUT RED DATA  */
    usB = ILI9806G_Read_Data ();    /*READ OUT BLUE DATA*/
    usG = ILI9806G_Read_Data ();    /*READ OUT GREEN DATA*/ 

  return ( ( ( usR >> 11 ) << 11 ) | ( ( usG >> 10 ) << 5 ) | ( usB >> 11 ) );

}

/**
 * @brief  获取 ILI9806G 显示器上某一个坐标点的像素数据
 * @param  usX :在特定扫描方向下该点的X坐标
 * @param  usY :在特定扫描方向下该点的Y坐标
 * @retval 像素数据
 */
uint16_t ILI9806G_GetPointPixel ( uint16_t usX, uint16_t usY )
{ 
    uint16_t usPixelData;

    ILI9806G_SetCursor ( usX, usY );

    usPixelData = ILI9806G_Read_PixelData ();

    return usPixelData;

}

/**
 * @brief  在 ILI9806G 显示器上使用 Bresenham 算法画线段 
 * @param  usX1 :在特定扫描方向下线段的一个端点X坐标
 * @param  usY1 :在特定扫描方向下线段的一个端点Y坐标
 * @param  usX2 :在特定扫描方向下线段的另一个端点X坐标
 * @param  usY2 :在特定扫描方向下线段的另一个端点Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DrawLine ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 )
{
    uint16_t us; 
    uint16_t usX_Current, usY_Current;

    int32_t lError_X = 0, lError_Y = 0, lDelta_X, lDelta_Y, lDistance; 
    int32_t lIncrease_X, lIncrease_Y;   

    lDelta_X = usX2 - usX1; //计算坐标增量 
    lDelta_Y = usY2 - usY1; 

    usX_Current = usX1; 
    usY_Current = usY1; 

    if ( lDelta_X > 0 ) 
        lIncrease_X = 1; //设置单步方向 

    else if ( lDelta_X == 0 ) 
        lIncrease_X = 0;//垂直线 

    else 
  { 
    lIncrease_X = -1;
    lDelta_X = - lDelta_X;
  } 

    if ( lDelta_Y > 0 )
        lIncrease_Y = 1; 

    else if ( lDelta_Y == 0 )
        lIncrease_Y = 0;//水平线 

    else 
  {
    lIncrease_Y = -1;
    lDelta_Y = - lDelta_Y;
  } 

    if (  lDelta_X > lDelta_Y )
        lDistance = lDelta_X; //选取基本增量坐标轴 

    else 
        lDistance = lDelta_Y; 

    for ( us = 0; us <= lDistance + 1; us ++ )//画线输出 
    {  
        ILI9806G_SetPointPixel ( usX_Current, usY_Current );//画点 

        lError_X += lDelta_X ; 
        lError_Y += lDelta_Y ; 

        if ( lError_X > lDistance ) 
        { 
            lError_X -= lDistance; 
            usX_Current += lIncrease_X; 
        }  

        if ( lError_Y > lDistance ) 
        { 
            lError_Y -= lDistance; 
            usY_Current += lIncrease_Y; 
        } 

    }  

}   

/**
 * @brief  在 ILI9806G 显示器上画一个矩形
 * @param  usX_Start :在特定扫描方向下矩形的起始点X坐标
 * @param  usY_Start :在特定扫描方向下矩形的起始点Y坐标
 * @param  usWidth:矩形的宽度(单位:像素)
 * @param  usHeight:矩形的高度(单位:像素)
 * @param  ucFilled :选择是否填充该矩形
  *   该参数为以下值之一:
  *     @arg 0 :空心矩形
  *     @arg 1 :实心矩形 
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DrawRectangle ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint8_t ucFilled )
{
    if ( ucFilled )
    {
        ILI9806G_OpenWindow ( usX_Start, usY_Start, usWidth, usHeight );
        ILI9806G_FillColor ( usWidth * usHeight ,CurrentTextColor); 
    }
    else
    {
        ILI9806G_DrawLine ( usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start );
        ILI9806G_DrawLine ( usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );
        ILI9806G_DrawLine ( usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1 );
        ILI9806G_DrawLine ( usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );        
    }

}

/**
  * @brief  设置LCD的背景颜色,RGB565
  * @param  Color: 指定背景颜色 
  * @retval None
  */
void LCD_SetBackColor(uint16_t Color)
{
  CurrentBackColor = Color;
}

/**
 * @brief  在 ILI9806G 显示器上使用 Bresenham 算法画圆
 * @param  usX_Center :在特定扫描方向下圆心的X坐标
 * @param  usY_Center :在特定扫描方向下圆心的Y坐标
 * @param  usRadius:圆的半径(单位:像素)
 * @param  ucFilled :选择是否填充该圆
  *   该参数为以下值之一:
  *     @arg 0 :空心圆
  *     @arg 1 :实心圆
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9806G_DrawCircle ( uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled )
{
    int16_t sCurrentX, sCurrentY;
    int16_t sError;

    sCurrentX = 0; sCurrentY = usRadius;      

    sError = 3 - ( usRadius << 1 );     //判断下个点位置的标志

    while ( sCurrentX <= sCurrentY )
    {
        int16_t sCountY;

        if ( ucFilled )             
            for ( sCountY = sCurrentX; sCountY <= sCurrentY; sCountY ++ ) 
            {                      
                ILI9806G_SetPointPixel ( usX_Center + sCurrentX, usY_Center + sCountY );           //1,研究对象 
                ILI9806G_SetPointPixel ( usX_Center - sCurrentX, usY_Center + sCountY );           //2       
                ILI9806G_SetPointPixel ( usX_Center - sCountY,   usY_Center + sCurrentX );           //3
                ILI9806G_SetPointPixel ( usX_Center - sCountY,   usY_Center - sCurrentX );           //4
                ILI9806G_SetPointPixel ( usX_Center - sCurrentX, usY_Center - sCountY );           //5    
        ILI9806G_SetPointPixel ( usX_Center + sCurrentX, usY_Center - sCountY );           //6
                ILI9806G_SetPointPixel ( usX_Center + sCountY,   usY_Center - sCurrentX );           //7    
        ILI9806G_SetPointPixel ( usX_Center + sCountY,   usY_Center + sCurrentX );           //0                
            }

        else
        {          
            ILI9806G_SetPointPixel ( usX_Center + sCurrentX, usY_Center + sCurrentY );             //1,研究对象
            ILI9806G_SetPointPixel ( usX_Center - sCurrentX, usY_Center + sCurrentY );             //2      
            ILI9806G_SetPointPixel ( usX_Center - sCurrentY, usY_Center + sCurrentX );             //3
            ILI9806G_SetPointPixel ( usX_Center - sCurrentY, usY_Center - sCurrentX );             //4
            ILI9806G_SetPointPixel ( usX_Center - sCurrentX, usY_Center - sCurrentY );             //5       
            ILI9806G_SetPointPixel ( usX_Center + sCurrentX, usY_Center - sCurrentY );             //6
            ILI9806G_SetPointPixel ( usX_Center + sCurrentY, usY_Center - sCurrentX );             //7 
            ILI9806G_SetPointPixel ( usX_Center + sCurrentY, usY_Center + sCurrentX );             //0
    }           

        sCurrentX ++;

        if ( sError < 0 ) 
            sError += 4 * sCurrentX + 6;      

        else
        {
            sError += 10 + 4 * ( sCurrentX - sCurrentY );   
            sCurrentY --;
        }   

    }
}

修改LVGL接口

接口主要作用就是对特定坐标填充特定数据

这里采用了 DMA 方法进行填充,即HAL_DMA_Start_IT

在cubemx中配置如下,注意:

1 配置 MemToMem,即内存到内存DMA

2 底部Src Memory 勾选,另一个不勾选 原因:此屏幕在填充像素时,是向固定一个地址写入像素,因此目标地址不自增

3 Steam 自选 ,注册回调函数以及开启DMA时需要填写对应通道

image-20241115163920122

注意,每次填充完要调用lv_disp_flush_ready函数来告诉LVGL绘制完成,

所以在要主函数中注册DMA转运完成的回调函数

即HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream1, HAL_DMA_XFER_CPLT_CB_ID, LVGL_LCD_FSMC_DMA_pCallback);

这个函数的意思是每次DMA转运完成后,调用一次我们自定义的LVGL_LCD_FSMC_DMA_pCallback()

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
   // 1. 计算绘制区域
    uint16_t usX = area->x1;         // 起始X坐标
    uint16_t usY = area->y1;         // 起始Y坐标
    uint16_t usWidth = area->x2 - area->x1 + 1;  // 区域宽度
    uint16_t usHeight = area->y2 - area->y1 + 1; // 区域高度
    // 2. 设置窗口
    ILI9806G_OpenWindow(usX, usY, usWidth, usHeight);
    // 3. 逐个像素填充颜色
    uint32_t total_pixels = usWidth * usHeight;  // 要刷新的像素点总数
    ILI9806G_Write_Cmd ( CMD_SetPixel );    // 发送指令,表示之后的数据为像素
    // 开启DMA,向固定地址写像素
    HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream1,      (uint32_t)color_p,FSMC_Addr_ILI9806G_DATA,total_pixels); 
}

void LVGL_LCD_FSMC_DMA_pCallback(DMA_HandleTypeDef *hdma) {
    // 在此处理传输完成后的逻辑
    // 注意直接这样写&disp_drv可能会报错
    //原因是这是上面初始化函数void lv_port_disp_init(void)中的定义的
    //只需要将原来的删掉,然后在上面定义为全局变量即可
    // static lv_disp_drv_t disp_drv; 
    lv_disp_flush_ready(&disp_drv);
}

至此,显示部分完成

触摸移植

官方标准库中,触摸为软件模拟I2C外加一根外部中断信号线

中断信号线作用为告诉处理器接收到I2C信号,触发中断进行模拟接收和处理

官方驱动文件组成为一个I2C驱动和基于I2C的触摸芯片驱动

我们主要要做的就是

1 在cubemx中对模拟I2C的引脚进行初始化

2 删除官方文件中的外设初始化部分,将标准库函数改为对应的HAL库函数

3 在基于I2C的触摸芯片驱动中,添加LVGL所需要的函数接口

这里不多废话直接贴改后的代码,至于官方代码可以自行下载或者从我的连接下载

bsp_i2c_touch.h

#ifndef __I2C_TOUCH_H
#define __I2C_TOUCH_H

#include "stm32f4xx.h"
#include "stm32f4xx_hal.h"
#include "gpio.h"
/*设定使用的电容屏IIC设备地址*/
#define GTP_ADDRESS            0xBA

#define I2CT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT         ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))

/*I2C引脚*/
#define GTP_I2C_SCL_PIN                  GPIO_PIN_7                 
#define GTP_I2C_SCL_GPIO_PORT            GPIOD                       
#define GTP_I2C_SCL_GPIO_CLK             RCC_AHB1Periph_GPIOD
#define GTP_I2C_SCL_SOURCE               GPIO_PINSource7

#define GTP_I2C_SDA_PIN                  GPIO_PIN_3                 
#define GTP_I2C_SDA_GPIO_PORT            GPIOD                     
#define GTP_I2C_SDA_GPIO_CLK             RCC_AHB1Periph_GPIOD
#define GTP_I2C_SDA_SOURCE               GPIO_PINSource3

/*复位引脚*/
#define GTP_RST_GPIO_PORT                GPIOD
#define GTP_RST_GPIO_CLK                 RCC_AHB1Periph_GPIOD
#define GTP_RST_GPIO_PIN                 GPIO_PIN_6
/*中断引脚*/
#define GTP_INT_GPIO_PORT                GPIOG
#define GTP_INT_GPIO_CLK                 RCC_AHB1Periph_GPIOG
#define GTP_INT_GPIO_PIN                 GPIO_PIN_8
#define GTP_INT_EXTI_PORTSOURCE          EXTI_PortSourceGPIOG
#define GTP_INT_EXTI_PINSOURCE           EXTI_PinSource8
#define GTP_INT_EXTI_LINE                EXTI_Line8
#define GTP_INT_EXTI_IRQ                 EXTI9_5_IRQn
/*中断服务函数*/
#define GTP_IRQHandler                   EXTI9_5_IRQHandler

//软件IIC使用的宏
#define I2C_SCL_1()  HAL_GPIO_WritePin(GTP_I2C_SCL_GPIO_PORT,GTP_I2C_SCL_PIN, GPIO_PIN_SET)     /* SCL = 1 */
#define I2C_SCL_0()  HAL_GPIO_WritePin(GTP_I2C_SCL_GPIO_PORT,GTP_I2C_SCL_PIN, GPIO_PIN_RESET)       /* SCL = 0 */

#define I2C_SDA_1()  HAL_GPIO_WritePin(GTP_I2C_SDA_GPIO_PORT,GTP_I2C_SDA_PIN,GPIO_PIN_SET)      /* SDA = 1 */
#define I2C_SDA_0()  HAL_GPIO_WritePin(GTP_I2C_SDA_GPIO_PORT,GTP_I2C_SDA_PIN,GPIO_PIN_RESET)        /* SDA = 0 */

#define I2C_SDA_READ()  HAL_GPIO_ReadPin(GTP_I2C_SDA_GPIO_PORT, GTP_I2C_SDA_PIN)    /* 读SDA口线状态 */

//函数接口
void I2C_Touch_Init(void);
uint32_t I2C_WriteBytes(uint8_t ClientAddr,uint8_t* pBuffer,  uint8_t NumByteToWrite);
uint32_t I2C_ReadBytes(uint8_t ClientAddr,uint8_t* pBuffer, uint16_t NumByteToRead);
//void I2C_ResetChip(void);
//void I2C_GTP_IRQDisable(void);
//void I2C_GTP_IRQEnable(void);

#endif /* __I2C_TOUCH_H */

bsp_i2c_touch.c

/**
  ******************************************************************************
  * @file    bsp_i2c_ee.c
  * @author  STMicroelectronics
  * @version V1.0
  * @date    2015-xx-xx
  * @brief   电容触摸屏的专用iic驱动
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火  STM32 F407 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 

#include "bsp_i2c_touch.h"
#include "gt9xx.h"
#include "gpio.h"

/* STM32 I2C 快速模式 */
#define I2C_Speed              400000

/* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */
#define I2C_OWN_ADDRESS7      0x0A

static void Delay(__IO uint32_t nCount)  //简单的延时函数
{
    for(; nCount != 0; nCount--);
}

// /**
//   * @brief  使能触摸屏中断
//   * @param  无
//   * @retval 无
//   */
// void I2C_GTP_IRQEnable(void)
// {
//   EXTI_InitTypeDef EXTI_InitStructure;  
//   NVIC_InitTypeDef NVIC_InitStructure;
//   GPIO_InitTypeDef GPIO_InitStructure;  
//   /*配置 INT 为浮空输入 */   
//   GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
//   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
//   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//   GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
//   GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);  

//   /* 连接 EXTI 中断源 到INT 引脚 */
//   SYSCFG_EXTILineConfig(GTP_INT_EXTI_PORTSOURCE, GTP_INT_EXTI_PINSOURCE);

//   /* 选择 EXTI 中断源 */
//   EXTI_InitStructure.EXTI_Line = GTP_INT_EXTI_LINE;
//   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
//   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  
//   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
//   EXTI_Init(&EXTI_InitStructure);  

//   /* 配置中断优先级 */
//   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

//  /*使能中断*/
//   NVIC_InitStructure.NVIC_IRQChannel = GTP_INT_EXTI_IRQ;
//   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//   NVIC_Init(&NVIC_InitStructure);

// }

// /**
//   * @brief  关闭触摸屏中断
//   * @param  无
//   * @retval 无
//   */
// void I2C_GTP_IRQDisable(void)
// {
//   EXTI_InitTypeDef EXTI_InitStructure;
//   NVIC_InitTypeDef NVIC_InitStructure;
//   GPIO_InitTypeDef GPIO_InitStructure;
//   /*配置 INT 为浮空输入 */   
//   GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
//   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
//   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//   GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
//   GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);

//   /* 连接 EXTI 中断源 到INT 引脚 */
//   SYSCFG_EXTILineConfig(GTP_INT_EXTI_PORTSOURCE, GTP_INT_EXTI_PINSOURCE);

//   /* 选择 EXTI 中断源 */
//   EXTI_InitStructure.EXTI_Line = GTP_INT_EXTI_LINE;
//   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
//   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
//   EXTI_InitStructure.EXTI_LineCmd = DISABLE;
//   EXTI_Init(&EXTI_InitStructure);

//   /* 配置中断优先级 */
//   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

//   /* 关闭中断 */
//   NVIC_InitStructure.NVIC_IRQChannel = GTP_INT_EXTI_IRQ;
//   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//   NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
//   NVIC_Init(&NVIC_InitStructure);

// }

// /**
//   * @brief  触摸屏 I/O配置
//   * @param  无
//   * @retval 无
//   */
// static void I2C_GPIO_Config(void)
// {
//   GPIO_InitTypeDef GPIO_InitStructure;  

//   /*使能触摸屏使用的引脚的时钟*/
//   RCC_AHB1PeriphClockCmd(GTP_I2C_SCL_GPIO_CLK|
//                           GTP_I2C_SDA_GPIO_CLK|
//                         GTP_RST_GPIO_CLK|GTP_INT_GPIO_CLK, 
//                         ENABLE);

//   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

//     /*配置SCL引脚 */   
//     GPIO_InitStructure.GPIO_Pin = GTP_I2C_SCL_PIN;
//     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
//     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//     GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
//     GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
//     GPIO_Init(GTP_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);

//     /*配置SDA引脚 */
//     GPIO_InitStructure.GPIO_Pin = GTP_I2C_SDA_PIN;
//     GPIO_Init(GTP_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);

//   /*配置RST引脚,下拉推挽输出 */   
//   GPIO_InitStructure.GPIO_Pin = GTP_RST_GPIO_PIN;
//   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
//   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
//   GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;
//   GPIO_Init(GTP_RST_GPIO_PORT, &GPIO_InitStructure);

//   /*配置 INT引脚,下拉推挽输出,方便初始化 */   
//   GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
//   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
//   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
//  //设置为下拉,方便初始化
//   GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;
//   GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
// }

// /**
//   * @brief  对GT91xx芯片进行复位
//   * @param  无
//   * @retval 无
//   */
// void I2C_ResetChip(void)
// {
//    GPIO_InitTypeDef GPIO_InitStructure;

//   /*配置 INT引脚,下拉推挽输出,方便初始化 */   
//    GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
//    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
//    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
//    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;       //设置为下拉,方便初始化
//    GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);

//    /*初始化GT5688,rst为高电平,int为低电平,则gt5688的设备地址被配置为0xBA*/

//    /*复位为低电平,为初始化做准备*/
//    GPIO_ResetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN);
//    Delay(0x0FFFFF);

//    /*拉高一段时间,进行初始化*/
//    GPIO_SetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN);
//    Delay(0x0FFFFF);

//    /*把INT引脚设置为浮空输入模式,以便接收触摸中断信号*/
//    GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
//    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
//    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
//    GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
// }

/**
  * @brief  I2C 外设(GT5xx)初始化
  * @param  无
  * @retval 无
  */
void I2C_Touch_Init(void)
{
 // I2C_GPIO_Config(); 

 // I2C_ResetChip();

HAL_GPIO_WritePin (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN,GPIO_PIN_RESET);
Delay(0x0FFFFF);

//    /*拉高一段时间,进行初始化*/
HAL_GPIO_WritePin (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN,GPIO_PIN_SET);
Delay(0x0FFFFF);

 // I2C_GTP_IRQEnable();
      /*Configure GPIO pin : PG8 */
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

}
/*
*********************************************************************************************************
*   函 数 名: i2c_Delay
*   功能说明: I2C总线位延迟,最快400KHz
*   形    参:无
*   返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
    uint8_t i;

    /* 
        下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频180MHz ,MDK编译环境,1级优化

        循环次数为50时,SCL频率 = 333KHz 
        循环次数为30时,SCL频率 = 533KHz,  
        循环次数为20时,SCL频率 = 727KHz, 
  */
    for (i = 0; i < 10*2; i++);
}

/*
*********************************************************************************************************
*   函 数 名: i2c_Start
*   功能说明: CPU发起I2C总线启动信号
*   形    参:无
*   返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{
    /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
    I2C_SDA_1();
    I2C_SCL_1();
    i2c_Delay();
    I2C_SDA_0();
    i2c_Delay();
    I2C_SCL_0();
    i2c_Delay();
}

/*
*********************************************************************************************************
*   函 数 名: i2c_Start
*   功能说明: CPU发起I2C总线停止信号
*   形    参:无
*   返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
    /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
    I2C_SDA_0();
    I2C_SCL_1();
    i2c_Delay();
    I2C_SDA_1();
}

/*
*********************************************************************************************************
*   函 数 名: i2c_SendByte
*   功能说明: CPU向I2C总线设备发送8bit数据
*   形    参:_ucByte : 等待发送的字节
*   返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
    uint8_t i;

    /* 先发送字节的高位bit7 */
    for (i = 0; i < 8; i++)
    {       
        if (_ucByte & 0x80)
        {
            I2C_SDA_1();
        }
        else
        {
            I2C_SDA_0();
        }
        i2c_Delay();
        I2C_SCL_1();
        i2c_Delay();    
        I2C_SCL_0();
        if (i == 7)
        {
             I2C_SDA_1(); // 释放总线
        }
        _ucByte <<= 1;    /* 左移一个bit */
        i2c_Delay();
    }
}

/*
*********************************************************************************************************
*   函 数 名: i2c_ReadByte
*   功能说明: CPU从I2C总线设备读取8bit数据
*   形    参:无
*   返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{
    uint8_t i;
    uint8_t value;

    /* 读到第1个bit为数据的bit7 */
    value = 0;
    for (i = 0; i < 8; i++)
    {
        value <<= 1;
        I2C_SCL_1();
        i2c_Delay();
        if (I2C_SDA_READ())
        {
            value++;
        }
        I2C_SCL_0();
        i2c_Delay();
    }
    return value;
}

/*
*********************************************************************************************************
*   函 数 名: i2c_WaitAck
*   功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
*   形    参:无
*   返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
    uint8_t re;

    I2C_SDA_1();    /* CPU释放SDA总线 */
    i2c_Delay();
    I2C_SCL_1();    /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    i2c_Delay();
    if (I2C_SDA_READ()) /* CPU读取SDA口线状态 */
    {
        re = 1;
    }
    else
    {
        re = 0;
    }
    I2C_SCL_0();
    i2c_Delay();
    return re;
}

/*
*********************************************************************************************************
*   函 数 名: i2c_Ack
*   功能说明: CPU产生一个ACK信号
*   形    参:无
*   返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(void)
{
    I2C_SDA_0();    /* CPU驱动SDA = 0 */
    i2c_Delay();
    I2C_SCL_1();    /* CPU产生1个时钟 */
    i2c_Delay();
    I2C_SCL_0();
    i2c_Delay();
    I2C_SDA_1();    /* CPU释放SDA总线 */
}

/*
*********************************************************************************************************
*   函 数 名: i2c_NAck
*   功能说明: CPU产生1个NACK信号
*   形    参:无
*   返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{
    I2C_SDA_1();    /* CPU驱动SDA = 1 */
    i2c_Delay();
    I2C_SCL_1();    /* CPU产生1个时钟 */
    i2c_Delay();
    I2C_SCL_0();
    i2c_Delay();    
}

#define I2C_DIR_WR  0       /* 写控制bit */
#define I2C_DIR_RD  1       /* 读控制bit */

/**
  * @brief   使用IIC读取数据
  * @param   
  *     @arg ClientAddr:从设备地址
  *     @arg pBuffer:存放由从机读取的数据的缓冲区指针
  *     @arg NumByteToRead:读取的数据长度
  * @retval  无
  */
uint32_t I2C_ReadBytes(uint8_t ClientAddr,uint8_t* pBuffer, uint16_t NumByteToRead)
{

    /* 第1步:发起I2C总线启动信号 */
    i2c_Start();

    /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(ClientAddr | I2C_DIR_RD);  /* 此处是读指令 */

    /* 第3步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;  /* 器件无应答 */
    }

    while(NumByteToRead) 
  {
   if(NumByteToRead == 1)
    {
            i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */

      /* 发送I2C总线停止信号 */
      i2c_Stop();
    }

   *pBuffer = i2c_ReadByte();

    /* 读指针自增 */
    pBuffer++; 

    /*计数器自减 */
    NumByteToRead--;

    i2c_Ack();  /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */  
  }

    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 0;   /* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 1;
}

/**
  * @brief   使用IIC写入数据
  * @param   
  *     @arg ClientAddr:从设备地址
  *     @arg pBuffer:缓冲区指针
  *     @arg NumByteToWrite:写的字节数
  * @retval  无
  */
uint32_t I2C_WriteBytes(uint8_t ClientAddr,uint8_t* pBuffer,  uint8_t NumByteToWrite)
{
    uint16_t m; 

  /* 第0步:发停止信号,启动内部写操作 */
  i2c_Stop();

  /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms             
    CLK频率为200KHz时,查询次数为30次左右
  */
  for (m = 0; m < 1000; m++)
  {             
    /* 第1步:发起I2C总线启动信号 */
    i2c_Start();

    /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(ClientAddr | I2C_DIR_WR);  /* 此处是写指令 */

    /* 第3步:发送一个时钟,判断器件是否正确应答 */
    if (i2c_WaitAck() == 0)
    {
      break;
    }
  }
  if (m  == 1000)
  {
    goto cmd_fail;  /* EEPROM器件写超时 */
  } 

  while(NumByteToWrite--)
  {
  /* 第4步:开始写入数据 */
  i2c_SendByte(*pBuffer);

  /* 第5步:检查ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail;  /* 器件无应答 */
  }

      pBuffer++;    /* 地址增1 */      
  }

    /* 命令执行成功,发送I2C总线停止信号 */
    i2c_Stop();
    return 0;

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 1;
}

/*********************************************END OF FILE**********************/

gt9xx.h


#ifndef _GOODIX_GT9XX_H
#define _GOODIX_GT9XX_H

#include "stm32f4xx.h"
#include "stm32f4xx_hal.h"

#ifndef NULL
  #define NULL        0
#endif

#define UPDATE_CONFIG    0    // 1 :更新配置    0 :不更新配置

/*flags的可取值,注释掉的在本工程没有用到*/
//#define I2C_M_TEN     0x0010  /* 表示这是个10位地址 */
//#define I2C_M_NOSTART     0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
//#define I2C_M_REV_DIR_ADDR    0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
//#define I2C_M_IGNORE_NAK  0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
//#define I2C_M_NO_RD_ACK       0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
//#define I2C_M_RECV_LEN        0x0400  /* length will be first received byte */

/* 表示读数据 */ 
#define I2C_M_RD        0x0001  

/*
 * 存储I2C通讯的信息
 * @addr:  从设备的I2C设备地址  
 * @flags: 控制标志
 * @len:  读写数据的长度
 * @buf:  存储读写数据的指针
 **/
struct i2c_msg {
    uint8_t addr;       /*从设备的I2C设备地址 */
    uint16_t flags; /*控制标志*/
    uint16_t len;       /*读写数据的长度           */
    uint8_t *buf;       /*存储读写数据的指针 */
};

/** 
  * @brief 触摸屏参数
  */
typedef struct
{
  /*根据触摸屏类型配置*/
  uint16_t max_width;  //触点最大值,高
  uint16_t max_height;  //触点最大值,宽

  uint16_t config_reg_addr;     //不同类型的触摸ic配置寄存器地址不同

}TOUCH_PARAM_TypeDef;

typedef enum 
{
    GT917S=0,
    GT911=1,
    GT5688=2,
  GT9147=3,
  GT9157=4,
}TOUCH_IC;

#define GTP_MAX_HEIGHT   480
#define GTP_MAX_WIDTH    800
#define GTP_INT_TRIGGER  0
#define GTP_MAX_TOUCH         5

//***************************
#define GTP_DRIVER_VERSION          "V2.2<2014/01/14>"
#define GTP_I2C_NAME                "Goodix-TS"
#define GT91XX_CONFIG_PROC_FILE     "gt9xx_config"
#define GTP_POLL_TIME         10    
#define GTP_ADDR_LENGTH       2
#define GTP_CONFIG_MIN_LENGTH 186
#define GTP_CONFIG_MAX_LENGTH 256
#define FAIL                  0
#define SUCCESS               1
#define SWITCH_OFF            0
#define SWITCH_ON             1

//******************** For GT9XXF Start **********************//
#define GTP_REG_BAK_REF                 0x99EC
#define GTP_REG_MAIN_CLK                0x8020
#define GTP_REG_CHIP_TYPE               0x8000
#define GTP_REG_HAVE_KEY                0x8057
#define GTP_REG_MATRIX_DRVNUM           0x8069     
#define GTP_REG_MATRIX_SENNUM           0x806A
#define GTP_REG_COMMAND                 0x8040

#define GTP_COMMAND_READSTATUS      0
#define GTP_COMMAND_DIFFERENCE      1
#define GTP_COMMAND_SOFTRESET           2
#define GTP_COMMAND_UPDATE              3
#define GTP_COMMAND_CALCULATE       4
#define GTP_COMMAND_TURNOFF         5

#define GTP_FL_FW_BURN              0x00
#define GTP_FL_ESD_RECOVERY         0x01
#define GTP_FL_READ_REPAIR          0x02

#define GTP_BAK_REF_SEND                0
#define GTP_BAK_REF_STORE               1
#define CFG_LOC_DRVA_NUM                29
#define CFG_LOC_DRVB_NUM                30
#define CFG_LOC_SENS_NUM                31

#define GTP_CHK_FW_MAX                  40
#define GTP_CHK_FS_MNT_MAX              300
#define GTP_BAK_REF_PATH                "/data/gtp_ref.bin"
#define GTP_MAIN_CLK_PATH               "/data/gtp_clk.bin"
#define GTP_RQST_CONFIG                 0x01
#define GTP_RQST_BAK_REF                0x02
#define GTP_RQST_RESET                  0x03
#define GTP_RQST_MAIN_CLOCK             0x04
#define GTP_RQST_RESPONDED              0x00
#define GTP_RQST_IDLE                   0xFF

//******************** For GT9XXF End **********************//
// Registers define
#define GTP_READ_COOR_ADDR    0x814E
#define GTP_REG_SLEEP         0x8040
#define GTP_REG_SENSOR_ID     0x814A
#define GTP_REG_CONFIG_DATA   touch_param[touchIC].config_reg_addr
#define GTP_REG_VERSION       0x8140

#define RESOLUTION_LOC        3
#define TRIGGER_LOC           8
#define X2Y_LOC                     (1<<3)

#define CFG_GROUP_LEN(p_cfg_grp)  (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0]))

//***************************PART1:ON/OFF define*******************************

#define GTP_DEBUG_ON            0
#define GTP_DEBUG_ARRAY_ON    0
#define GTP_DEBUG_FUNC_ON       0
// Log define
#define GTP_INFO(fmt,arg...)           printf("<<-GTP-INFO->> "fmt"\n",##arg)
#define GTP_ERROR(fmt,arg...)          printf("<<-GTP-ERROR->> "fmt"\n",##arg)
#define GTP_DEBUG(fmt,arg...)          do{\
                                         if(GTP_DEBUG_ON)\
                                         printf("<<-GTP-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                                                                    }while(0)

#define GTP_DEBUG_ARRAY(array, num)    do{\
                                         int32_t i;\
                                         uint8_t* a = array;\
                                         if(GTP_DEBUG_ARRAY_ON)\
                                         {\
                                            printf("<<-GTP-DEBUG-ARRAY->>\n");\
                                            for (i = 0; i < (num); i++)\
                                            {\
                                                printf("%02x   ", (a)[i]);\
                                                if ((i + 1 ) %10 == 0)\
                                                {\
                                                    printf("\n");\
                                                }\
                                            }\
                                            printf("\n");\
                                        }\
                                       }while(0)

#define GTP_DEBUG_FUNC()               do{\
                                         if(GTP_DEBUG_FUNC_ON)\
                                         printf("<<-GTP-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\
                                       }while(0)

#define GTP_SWAP(x, y)                 do{\
                                         typeof(x) z = x;\
                                         x = y;\
                                         y = z;\
                                       }while (0)

//*****************************End of Part III********************************
int8_t GTP_Reset_Guitar(void);
int32_t GTP_Read_Version(void);
//void GTP_IRQ_Disable(void);
//void GTP_IRQ_Enable(void);
int32_t GTP_Init_Panel(void);
int8_t GTP_Send_Command(uint8_t command);
int16_t Get_Touchpad_x(void);
int16_t Get_Touchpad_y(void);
int16_t Get_Touchpad_Is_Down(void);
void Goodix_TS_Work_Func(void);

#endif /* _GOODIX_GT9XX_H_ */
/**
  ******************************************************************************
  * @file    gt5xx.c
  * @author  fire
  * @version V1.0
  * @date    2015-xx-xx
  * @brief   i2c电容屏驱动函数gt9157芯片
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火  STM32 F407 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "gt9xx.h"
#include "bsp_i2c_touch.h"
#include "bsp_ili9806g_lcd.h"
// 4.5寸屏GT5688驱动配置
const uint8_t CTP_CFG_GT5688[] =  {
            0x96,0xE0,0x01,0x56,0x03,0x05,0x35,0x00,0x01,0x00,
            0x00,0x05,0x50,0x3C,0x53,0x11,0x00,0x00,0x22,0x22,
            0x14,0x18,0x1A,0x1D,0x0A,0x04,0x00,0x00,0x00,0x00,
            0x00,0x00,0x53,0x00,0x14,0x00,0x00,0x84,0x00,0x00,
            0x3C,0x19,0x19,0x64,0x1E,0x28,0x88,0x29,0x0A,0x2D,
            0x2F,0x29,0x0C,0x20,0x33,0x60,0x13,0x02,0x24,0x00,
            0x00,0x20,0x3C,0xC0,0x14,0x02,0x00,0x00,0x54,0xAC,
            0x24,0x9C,0x29,0x8C,0x2D,0x80,0x32,0x77,0x37,0x6E,
            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x50,0x3C,
            0xFF,0xFF,0x07,0x00,0x00,0x00,0x02,0x14,0x14,0x03,
            0x04,0x00,0x21,0x64,0x0A,0x00,0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
            0x32,0x20,0x50,0x3C,0x3C,0x00,0x00,0x00,0x00,0x00,
            0x0D,0x06,0x0C,0x05,0x0B,0x04,0x0A,0x03,0x09,0x02,
            0xFF,0xFF,0xFF,0xFF,0x00,0x01,0x02,0x03,0x04,0x05,
            0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
            0x10,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00,0x3C,0x00,0x05,0x1E,0x00,0x02,
            0x2A,0x1E,0x19,0x14,0x02,0x00,0x03,0x0A,0x05,0x00,
            0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0xFF,0x86,
            0x22,0x03,0x00,0x00,0x33,0x00,0x0F,0x00,0x00,0x00,
            0x50,0x3C,0x50,0x00,0x00,0x00,0x1A,0x64,0x01

};

//GT9147配置参数表
//第一个字节为版本号(0X60),必须保证新的版本号大于等于GT9147内部
//flash原有版本号,才会更新配置.
const uint8_t CTP_CFG_GT9147[]=
{
  0x99,0xE0,0x01,0x20,0x03,0x05,0x34,0x00,0x02,
0x08,0x1E,0x08,0x50,0x3C,0x0F,0x05,0x00,0x00,
0xFF,0x67,0x02,0x02,0x00,0x18,0x1A,0x1E,0x14,
0x88,0x28,0x0A,0x55,0x57,0xD3,0x07,0x03,0x00,
0x00,0x42,0x32,0x1D,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x32,0x00,0x00,0x2A,0x4B,0x78,0x94,
0xD5,0x02,0x07,0x00,0x00,0x04,0x88,0x4E,0x00,
0x7E,0x56,0x00,0x76,0x5E,0x00,0x6E,0x68,0x00,
0x67,0x72,0x00,0x67,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x0F,0x03,0x06,0x10,0x42,0xF8,0x0F,0x14,
0x00,0x00,0x00,0x00,0x1A,0x18,0x16,0x14,0x12,
0x10,0x0E,0x0C,0x0A,0x08,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,
0x04,0x05,0x06,0x08,0x0A,0x0C,0x1D,0x1E,0x1F,
0x20,0x22,0x24,0x28,0x29,0xFF,0xFF,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x37,0x01
//  0x99,0XE0,0X01,0X20,0X03,0X05,0X35,0X00,0X02,0X08,
//  0X1E,0X08,0X50,0X3C,0X0F,0X05,0X00,0X00,0XFF,0X67,
//  0X50,0X00,0X00,0X18,0X1A,0X1E,0X14,0X89,0X28,0X0A,
//  0X30,0X2E,0XBB,0X0A,0X03,0X00,0X00,0X02,0X33,0X1D,
//  0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X32,0X00,0X00,
//  0X2A,0X1C,0X5A,0X94,0XC5,0X02,0X07,0X00,0X00,0X00,
//  0XB5,0X1F,0X00,0X90,0X28,0X00,0X77,0X32,0X00,0X62,
//  0X3F,0X00,0X52,0X50,0X00,0X52,0X00,0X00,0X00,0X00,
//  0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
//  0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,
//  0X0F,0X03,0X06,0X10,0X42,0XF8,0X0F,0X14,0X00,0X00,
//  0X00,0X00,0X1A,0X18,0X16,0X14,0X12,0X10,0X0E,0X0C,
//  0X0A,0X08,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
//  0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
//  0X00,0X00,0X29,0X28,0X24,0X22,0X20,0X1F,0X1E,0X1D,
//  0X0E,0X0C,0X0A,0X08,0X06,0X05,0X04,0X02,0X00,0XFF,
//  0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
//  0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
//  0XFF,0XFF,0XFF,0XFF,
};  

const uint8_t CTP_CFG_GT917S[] =  {
  0x84,0x20,0x03,0xE0,0x01,0x05,0x05,0x00,0x00,0x40,
  0x00,0x0F,0x78,0x64,0x53,0x11,0x00,0x00,0x00,0x00,
  0x23,0x17,0x19,0x1D,0x0F,0x04,0x00,0x00,0x00,0x00,
  0x00,0x00,0x04,0x51,0x14,0x00,0x00,0x00,0x00,0x00,
  0x32,0x00,0x00,0x50,0x38,0x28,0x8A,0x20,0x11,0x37,
  0x39,0xA2,0x07,0x38,0x6D,0x28,0x11,0x03,0x24,0x00,
  0x01,0x28,0x50,0xC0,0x94,0x02,0x00,0x00,0x53,0xB8,
  0x2E,0xA2,0x35,0x8F,0x3B,0x80,0x42,0x75,0x49,0x6B,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x4C,0x3C,
  0xFF,0xFF,0x07,0x14,0x14,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x73,
  0x50,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x1F,0x1D,0x1B,0x1A,0x19,0x18,0x17,0x16,0x15,0x09,
  0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0xFF,0xFF,0xFF,
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x1C,0x1B,0x1A,0x19,0x18,0x17,0x15,0x14,
  0x13,0x12,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x05,0x00,0x00,0x0F,
  0x00,0x00,0x00,0x80,0x46,0x08,0x96,0x50,0x32,0x0A,
  0x0A,0x64,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x32,0x03,0x0C,0x08,0x23,0x00,0x14,0x23,0x00,0x28,
  0x46,0x30,0x3C,0xD0,0x07,0x50,0x70,0xB0,0x01
};

const uint8_t CTP_CFG_GT911[] =  {
  0x53,0x20,0x03,0xE0,0x01,0x05,0x0D,0x10,0x01,0x18,
  0x28,0x0F,0x50,0x32,0x03,0x05,0x00,0x00,0x00,0x00,
  0x11,0x11,0x05,0x18,0x1A,0x1E,0x14,0x88,0x29,0x0A,
  0x52,0x50,0x40,0x04,0x00,0x00,0x00,0x1A,0x32,0x1C,
  0x00,0x01,0x00,0x0F,0x00,0x2A,0xFF,0x7F,0x00,0x50,
  0x32,0x3C,0x64,0x94,0xD5,0x02,0x07,0x00,0x00,0x04,
  0x9F,0x3F,0x00,0x90,0x46,0x00,0x84,0x4D,0x00,0x79,
  0x55,0x00,0x6D,0x5F,0x00,0x6D,0x00,0x00,0x00,0x00,
  0xF0,0x4A,0x3A,0xFF,0xFF,0x27,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0E,0x10,
  0x12,0x14,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x22,0x21,0x20,0x1F,0x1E,0x1D,0x1C,0x18,
  0x16,0x12,0x10,0x0F,0x08,0x06,0x04,0x02,0x00,0xFF,
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0xFF,0xFF,0x17,0x01
};

//uint8_t config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]
//                = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};

TOUCH_IC touchIC = GT917S;          

const TOUCH_PARAM_TypeDef touch_param[2] = 
{
  /* GT917S,4.3寸屏 */
  {
  .max_width = 800,
  .max_height = 480,
  .config_reg_addr = 0x8050,
  },

  /* GT911,4.3寸屏 */
  {
  .max_width = 800,
  .max_height = 480,
  .config_reg_addr = 0x8047,
  },
};

static int8_t GTP_I2C_Test(void);

static void Delay(__IO uint32_t nCount)  //简单的延时函数
{
    for(; nCount != 0; nCount--);
}

/**
  * @brief   使用IIC进行数据传输
  * @param
  *     @arg i2c_msg:数据传输结构体
  *     @arg num:数据传输结构体的个数
  * @retval  正常完成的传输结构个数,若不正常,返回0xff
  */
static int I2C_Transfer( struct i2c_msg *msgs,int num)
{
    int im = 0;
    int ret = 0;

    GTP_DEBUG_FUNC();

    for (im = 0; ret == 0 && im != num; im++)
    {
        if ((msgs[im].flags&I2C_M_RD))                                                              //根据flag判断是读数据还是写数据
        {
            ret = I2C_ReadBytes(msgs[im].addr, msgs[im].buf, msgs[im].len);     //IIC读取数据
        } else
        {
            ret = I2C_WriteBytes(msgs[im].addr,  msgs[im].buf, msgs[im].len);   //IIC写入数据
        }
    }

    if(ret)
        return ret;

    return im;                                                      //正常完成的传输结构个数
}

/**
  * @brief   从IIC设备中读取数据
  * @param
  *     @arg client_addr:设备地址
  *     @arg  buf[0~1]: 读取数据寄存器的起始地址
  *     @arg buf[2~len-1]: 存储读出来数据的缓冲buffer
  *     @arg len:    GTP_ADDR_LENGTH + read bytes count(寄存器地址长度+读取的数据字节数)
  * @retval  i2c_msgs传输结构体的个数,2为成功,其它为失败
  */
static int32_t GTP_I2C_Read(uint8_t client_addr, uint8_t *buf, int32_t len)
{
    struct i2c_msg msgs[2];
    int32_t ret=-1;
    int32_t retries = 0;

    GTP_DEBUG_FUNC();
    /*一个读数据的过程可以分为两个传输过程:
     * 1. IIC  写入 要读取的寄存器地址
     * 2. IIC  读取  数据
     * */

    msgs[0].flags = !I2C_M_RD;                  //写入
    msgs[0].addr  = client_addr;                    //IIC设备地址
    msgs[0].len   = GTP_ADDR_LENGTH;    //寄存器地址为2字节(即写入两字节的数据)
    msgs[0].buf   = &buf[0];                        //buf[0~1]存储的是要读取的寄存器地址

    msgs[1].flags = I2C_M_RD;                   //读取
    msgs[1].addr  = client_addr;                    //IIC设备地址
    msgs[1].len   = len - GTP_ADDR_LENGTH;  //要读取的数据长度
    msgs[1].buf   = &buf[GTP_ADDR_LENGTH];  //buf[GTP_ADDR_LENGTH]之后的缓冲区存储读出的数据

    while(retries < 5)
    {
        ret = I2C_Transfer( msgs, 2);                   //调用IIC数据传输过程函数,有2个传输过程
        if(ret == 2)break;
        retries++;
    }
    if((retries >= 5))
    {
        GTP_ERROR("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((uint16_t)(buf[0] << 8)) | buf[1]), len-2, ret);
    }
    return ret;
}

/**
  * @brief   向IIC设备写入数据
  * @param
  *     @arg client_addr:设备地址
  *     @arg  buf[0~1]: 要写入的数据寄存器的起始地址
  *     @arg buf[2~len-1]: 要写入的数据
  *     @arg len:    GTP_ADDR_LENGTH + write bytes count(寄存器地址长度+写入的数据字节数)
  * @retval  i2c_msgs传输结构体的个数,1为成功,其它为失败
  */
static int32_t GTP_I2C_Write(uint8_t client_addr,uint8_t *buf,int32_t len)
{
    struct i2c_msg msg;
    int32_t ret = -1;
    int32_t retries = 0;

    GTP_DEBUG_FUNC();
    /*一个写数据的过程只需要一个传输过程:
     * 1. IIC连续 写入 数据寄存器地址及数据
     * */
    msg.flags = !I2C_M_RD;          //写入
    msg.addr  = client_addr;            //从设备地址
    msg.len   = len;                            //长度直接等于(寄存器地址长度+写入的数据字节数)
    msg.buf   = buf;                        //直接连续写入缓冲区中的数据(包括了寄存器地址)

    while(retries < 5)
    {
        ret = I2C_Transfer(&msg, 1);    //调用IIC数据传输过程函数,1个传输过程
        if (ret == 1)break;
        retries++;
    }
    if((retries >= 5))
    {

        GTP_ERROR("I2C Write: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((uint16_t)(buf[0] << 8)) | buf[1]), len-2, ret);

    }
    return ret;
}

/**
  * @brief   使用IIC读取再次数据,检验是否正常
  * @param
  *     @arg client:设备地址
  *     @arg  addr: 寄存器地址
  *     @arg rxbuf: 存储读出的数据
  *     @arg len:    读取的字节数
  * @retval
  *     @arg FAIL
  *     @arg SUCCESS
  */
 int32_t GTP_I2C_Read_dbl_check(uint8_t client_addr, uint16_t addr, uint8_t *rxbuf, int len)
{
    uint8_t buf[16] = {0};
    uint8_t confirm_buf[16] = {0};
    uint8_t retry = 0;

    GTP_DEBUG_FUNC();

    while (retry++ < 3)
    {
        memset(buf, 0xAA, 16);
        buf[0] = (uint8_t)(addr >> 8);
        buf[1] = (uint8_t)(addr & 0xFF);
        GTP_I2C_Read(client_addr, buf, len + 2);

        memset(confirm_buf, 0xAB, 16);
        confirm_buf[0] = (uint8_t)(addr >> 8);
        confirm_buf[1] = (uint8_t)(addr & 0xFF);
        GTP_I2C_Read(client_addr, confirm_buf, len + 2);

        if (!memcmp(buf, confirm_buf, len+2))
        {
            memcpy(rxbuf, confirm_buf+2, len);
            return SUCCESS;
        }
    }    
    GTP_ERROR("I2C read 0x%04X, %d bytes, double check failed!", addr, len);
    return FAIL;
}

/**
  * @brief   关闭GT91xx中断
  * @param 无
  * @retval 无
  */
void GTP_IRQ_Disable(void)
{

    GTP_DEBUG_FUNC();

    //I2C_GTP_IRQDisable();
}

/**
  * @brief   使能GT91xx中断
  * @param 无
  * @retval 无
  */
void GTP_IRQ_Enable(void)
{
    GTP_DEBUG_FUNC();

      //I2C_GTP_IRQEnable();    
}

/**
  * @brief   用于处理或报告触屏检测到按下
  * @param
  *    @arg     id: 触摸顺序trackID
  *    @arg     x:  触摸的 x 坐标
  *    @arg     y:  触摸的 y 坐标
  *    @arg     w:  触摸的 大小
  * @retval 无
  */
/*用于记录连续触摸时(长按)的上一次触摸位置,负数值表示上一次无触摸按下*/
static int16_t pre_x[GTP_MAX_TOUCH] ={-1,-1,-1,-1,-1};
static int16_t pre_y[GTP_MAX_TOUCH] ={-1,-1,-1,-1,-1};

static void GTP_Touch_Down(int32_t id,int32_t x,int32_t y,int32_t w)
{

    GTP_DEBUG_FUNC();

    /*取x、y初始值大于屏幕像素值*/
    GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);

    /* 处理触摸按钮,用于触摸画板 */
    //Touch_Button_Down(x,y); 

    // /*处理描绘轨迹,用于触摸画板 */
    // Draw_Trail(pre_x[id],pre_y[id],x,y,&brush);

        /************************************/
        /*在此处添加自己的触摸点按下时处理过程即可*/
        /* (x,y) 即为最新的触摸点 *************/
        /************************************/

        /*prex,prey数组存储上一次触摸的位置,id为轨迹编号(多点触控时有多轨迹)*/
    pre_x[id] = x; pre_y[id] =y;

}

/**
  * @brief   用于处理或报告触屏释放
  * @param 释放点的id号
  * @retval 无
  */
static void GTP_Touch_Up( int32_t id)
{

    /*处理触摸释放,用于触摸画板*/
    //Touch_Button_Up(pre_x[id],pre_y[id]);

        /*****************************************/
        /*在此处添加自己的触摸点释放时的处理过程即可*/
        /* pre_x[id],pre_y[id] 即为最新的释放点 ****/
        /*******************************************/   
        /***id为轨迹编号(多点触控时有多轨迹)********/

    /*触笔释放,把pre xy 重置为负*/
      pre_x[id] = -1;
      pre_y[id] = -1;       

    GTP_DEBUG("Touch id[%2d] release!", id);

}

extern int32_t x = 0;
extern int32_t y = 0;

volatile int16_t touchpad_x = 0;
volatile int16_t touchpad_y = 0;
volatile int16_t touchpad_is_down = 0;

int16_t Get_Touchpad_x(void)
{
    return touchpad_x;
}

int16_t Get_Touchpad_y(void)
{
    return touchpad_y;
}

int16_t Get_Touchpad_Is_Down(void)
{
    return touchpad_is_down;
}
/**
  * @brief   触屏处理函数,轮询或者在触摸中断调用
  * @param 无
  * @retval 无
  */
void Goodix_TS_Work_Func(void)
{
    uint8_t  end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
    uint8_t  point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
    // uint8_t  touch_num = 0;
    uint8_t  finger = 0;
    static uint16_t pre_touch = 0;
    static uint8_t pre_id[GTP_MAX_TOUCH] = {0};
    uint8_t  touch_num = 0;
    uint8_t client_addr=GTP_ADDRESS;
    uint8_t* coor_data = NULL;
    int32_t input_x = 0;
    int32_t input_y = 0;
    int32_t input_w = 0;
    uint8_t id = 0;

    int32_t i  = 0;
    int32_t ret = -1;

    GTP_DEBUG_FUNC();

    ret = GTP_I2C_Read(client_addr, point_data, 12);//10字节寄存器加2字节地址
    if (ret < 0)
    {
        GTP_ERROR("I2C transfer error. errno:%d\n ", ret);

        return;
    }

    finger = point_data[GTP_ADDR_LENGTH];//状态寄存器数据

    if (finger == 0x00)     //没有数据,退出
    {
        return;
    }

    if((finger & 0x80) == 0)//判断buffer status位
    {
        goto exit_work_func;//坐标未就绪,数据无效
    }

    touch_num = finger & 0x0f;//坐标点数
    if (touch_num > GTP_MAX_TOUCH)
    {
        goto exit_work_func;//大于最大支持点数,错误退出
    }

    if (touch_num > 1)//不止一个点
    {
        uint8_t buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};

        ret = GTP_I2C_Read(client_addr, buf, 2 + 8 * (touch_num - 1));
        memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));          //复制其余点数的数据到point_data
    }

    if (pre_touch>touch_num)             //pre_touch>touch_num,表示有的点释放了
    {
        for (i = 0; i < pre_touch; i++)                      //一个点一个点处理
         {
            uint8_t j;
           for(j=0; j<touch_num; j++)
           {
               coor_data = &point_data[j * 8 + 3];
               id = coor_data[0] & 0x0F;                                    //track id
              if(pre_id[i] == id)
                break;

              if(j >= touch_num-1)                                           //遍历当前所有id都找不到pre_id[i],表示已释放
              {
                 GTP_Touch_Up( pre_id[i]);
              }
           }
       }
    }

    if (touch_num)
    {
        for (i = 0; i < touch_num; i++)                      //一个点一个点处理
        {
            coor_data = &point_data[i * 8 + 3];

            id = coor_data[0] & 0x0F;                                   //track id
            pre_id[i] = id;

            input_x  = coor_data[1] | (coor_data[2] << 8);    //x坐标
            input_y  = coor_data[3] | (coor_data[4] << 8);    //y坐标
            input_w  = coor_data[5] | (coor_data[6] << 8);    //size
                    //printf("X:%d, Y:%d \n", input_x, input_y);
            {
                                    /*根据扫描模式更正X/Y起始方向*/
                                switch(LCD_SCAN_MODE)
                                {
                                    case 0:
                                      x  = input_x;
                                      y  = input_y;
                                        break;
                                    case 1:
                                      x  = input_y;
                                        y  = input_x;                                       
                                        break;  
                                    case 2:
                                      x  = ILI9806G_LESS_PIXEL - input_x;
                                      y  = input_y;                                 
                                        break;
                                    case 3:
                                        x  = input_y;
                                        y  = ILI9806G_LESS_PIXEL - input_x;                                     
                                        break;  
                                    case 4:
                                      x  = input_x;
                                      y  = ILI9806G_MORE_PIXEL - input_y;
                                        break;                                      
                                    case 5:
                                      x  = 800 - input_y;
                                      y  = input_x;
                                        break;  
                                    case 6:
                                      x  = ILI9806G_LESS_PIXEL - input_x;
                                      y  = ILI9806G_MORE_PIXEL - input_y;
                                        break;                                      
                                    case 7:
                                      x  = 800 - input_y;
                                      y  = ILI9806G_LESS_PIXEL - input_x;
                                        break;                                  
                                    default:
                                    break;
                                }
//                              printf("X:%d, Y:%d \n", input_x, input_y);
                                GTP_Touch_Down( id, x, y, input_w);//数据处理
            }
        }

        touchpad_x = input_y;
                touchpad_y = input_x;
                touchpad_is_down = 1;
    }
    else if (pre_touch)     //touch_ num=0 且pre_touch!=0
    {
      touchpad_is_down = 0;
      for(i=0;i<pre_touch;i++)
      {
          GTP_Touch_Up(pre_id[i]);
      }
    }

    pre_touch = touch_num;

exit_work_func:
    {
        ret = GTP_I2C_Write(client_addr, end_cmd, 3);
        if (ret < 0)
        {
            GTP_INFO("I2C write end_cmd error!");
        }
    }

}

/**
  * @brief   给触屏芯片重新复位
  * @param 无
  * @retval 无
  */
 int8_t GTP_Reset_Guitar(void)
{
    GTP_DEBUG_FUNC();
#if 0
    //I2C_ResetChip();
    return 0;
#else       //软件复位
    int8_t ret = -1;
    int8_t retry = 0;
    uint8_t reset_command[3]={(uint8_t)GTP_REG_COMMAND>>8,(uint8_t)GTP_REG_COMMAND&0xFF,2};

    //写入复位命令
    while(retry++ < 5)
    {
        ret = GTP_I2C_Write(GTP_ADDRESS, reset_command, 3);
        if (ret > 0)
        {
            GTP_INFO("GTP enter sleep!");

            return ret;
        }

    }
    GTP_ERROR("GTP send sleep cmd failed.");
    return ret;
#endif

}

 /**
   * @brief   进入睡眠模式
   * @param 无
   * @retval 1为成功,其它为失败
   */
//int8_t GTP_Enter_Sleep(void)
//{
//    int8_t ret = -1;
//    int8_t retry = 0;
//    uint8_t reset_comment[3] = {(uint8_t)(GTP_REG_COMMENT >> 8), (uint8_t)GTP_REG_COMMENT&0xFF, 5};//5
//
//    GTP_DEBUG_FUNC();
//
//    while(retry++ < 5)
//    {
//        ret = GTP_I2C_Write(GTP_ADDRESS, reset_comment, 3);
//        if (ret > 0)
//        {
//            GTP_INFO("GTP enter sleep!");
//
//            return ret;
//        }
//
//    }
//    GTP_ERROR("GTP send sleep cmd failed.");
//    return ret;
//}

int8_t GTP_Send_Command(uint8_t command)
{
    int8_t ret = -1;
    int8_t retry = 0;
    uint8_t command_buf[3] = {(uint8_t)(GTP_REG_COMMAND >> 8), (uint8_t)GTP_REG_COMMAND&0xFF, GTP_COMMAND_READSTATUS};

    GTP_DEBUG_FUNC();

    while(retry++ < 5)
    {
        ret = GTP_I2C_Write(GTP_ADDRESS, command_buf, 3);
        if (ret > 0)
        {
            GTP_INFO("send command success!");

            return ret;
        }

    }
    GTP_ERROR("send command fail!");
    return ret;
}

/**
  * @brief   唤醒触摸屏
  * @param 无
  * @retval 0为成功,其它为失败
  */
int8_t GTP_WakeUp_Sleep(void)
{
    uint8_t retry = 0;
    int8_t ret = -1;

    GTP_DEBUG_FUNC();

    while(retry++ < 10)
    {
        ret = GTP_I2C_Test();
        if (ret > 0)
        {
            GTP_INFO("GTP wakeup sleep.");
            return ret;
        }
        GTP_Reset_Guitar();
    }

    GTP_ERROR("GTP wakeup sleep failed.");
    return ret;
}

static int32_t GTP_Get_Info(void)
{
    uint8_t opr_buf[10] = {0};
    int32_t ret = 0;

    uint16_t abs_x_max = GTP_MAX_WIDTH;
    uint16_t abs_y_max = GTP_MAX_HEIGHT;
    uint8_t int_trigger_type = GTP_INT_TRIGGER;

    opr_buf[0] = (uint8_t)((GTP_REG_CONFIG_DATA+1) >> 8);
    opr_buf[1] = (uint8_t)((GTP_REG_CONFIG_DATA+1) & 0xFF);

    ret = GTP_I2C_Read(GTP_ADDRESS, opr_buf, 10);
    if (ret < 0)
    {
        return FAIL;
    }

    abs_x_max = (opr_buf[3] << 8) + opr_buf[2];
    abs_y_max = (opr_buf[5] << 8) + opr_buf[4];
        GTP_DEBUG("RES");   
        GTP_DEBUG_ARRAY(&opr_buf[0],10);

    opr_buf[0] = (uint8_t)((GTP_REG_CONFIG_DATA+6) >> 8);
    opr_buf[1] = (uint8_t)((GTP_REG_CONFIG_DATA+6) & 0xFF);
    ret = GTP_I2C_Read(GTP_ADDRESS, opr_buf, 3);
    if (ret < 0)
    {
        return FAIL;
    }
    int_trigger_type = opr_buf[2] & 0x03;

    GTP_INFO("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
            abs_x_max,abs_y_max,int_trigger_type);

    return SUCCESS;    
}

/*******************************************************
Function:
    Initialize gtp.
Input:
    ts: goodix private data
Output:
    Executive outcomes.
        0: succeed, otherwise: failed
*******************************************************/
 int32_t GTP_Init_Panel(void)
{
    int32_t ret = -1;

    int32_t i = 0;
    uint16_t check_sum = 0;
    int32_t retry = 0;

    const uint8_t* cfg_info;
    uint8_t cfg_info_len  ;
        uint8_t* config;

    uint8_t cfg_num =0 ;        //需要配置的寄存器个数

    GTP_DEBUG_FUNC();

//uint8_t config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]
//                = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};

    I2C_Touch_Init();

    ret = GTP_I2C_Test();
    if (ret < 0)
    {
        GTP_ERROR("I2C communication ERROR!");
                return ret;
    } 

        //获取触摸IC的型号
    GTP_Read_Version(); 

#if UPDATE_CONFIG

    config = (uint8_t *)malloc (GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);

        config[0] = GTP_REG_CONFIG_DATA >> 8;
        config[1] =  GTP_REG_CONFIG_DATA & 0xff;

        //根据IC的型号指向不同的配置
        if(touchIC == GT5688)
        {
            cfg_info =  CTP_CFG_GT5688; //指向寄存器配置
            cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT5688);//计算配置表的大小
        }
        else if(touchIC == GT917S)
        {
            cfg_info =  CTP_CFG_GT917S; //指向寄存器配置
            cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT917S);//计算配置表的大小
        }
        else if(touchIC == GT911)
        {
            cfg_info =  CTP_CFG_GT911; //指向寄存器配置
            cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT911);//计算配置表的大小
        }
        else
        {//默认配置为GT917S
            cfg_info =  CTP_CFG_GT917S; //指向寄存器配置
            cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT917S);//计算配置表的大小
        }

    memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
    memcpy(&config[GTP_ADDR_LENGTH], cfg_info, cfg_info_len);

        cfg_num = cfg_info_len;

        GTP_DEBUG("cfg_info_len = %d ",cfg_info_len);
        GTP_DEBUG("cfg_num = %d ",cfg_num);
        GTP_DEBUG_ARRAY(config,6);

        /*根据LCD的扫描方向设置分辨率*/
        config[GTP_ADDR_LENGTH+1] = LCD_X_LENGTH & 0xFF;
        config[GTP_ADDR_LENGTH+2] = LCD_X_LENGTH >> 8;
        config[GTP_ADDR_LENGTH+3] = LCD_Y_LENGTH & 0xFF;
        config[GTP_ADDR_LENGTH+4] = LCD_Y_LENGTH >> 8;

        /*根据扫描模式设置X2Y交换*/
        if(touchIC == GT917S)
        {
            switch(LCD_SCAN_MODE)
            {
                case 0:case 2:case 4: case 6:
                    config[GTP_ADDR_LENGTH+6] |= (X2Y_LOC);
                    break;

                case 1:case 3:case 5: case 7:
                    config[GTP_ADDR_LENGTH+6] &= ~(X2Y_LOC);
                    break;      
            }
        }

        //计算要写入checksum寄存器的值
        check_sum = 0;

        /* 计算check sum校验值 */
        if(touchIC == GT911)
        {
            for (i = GTP_ADDR_LENGTH; i < cfg_num; i++)
            {
                check_sum += (config[i] & 0xFF);
            }
            config[ cfg_num] = (~(check_sum & 0xFF)) + 1;   //checksum
            config[ cfg_num+1] =  1;                        //refresh 配置更新标志
        }
        else if(touchIC == GT9157)
        {
            for (i = GTP_ADDR_LENGTH; i < cfg_num+GTP_ADDR_LENGTH; i++)
            {
                check_sum += (config[i] & 0xFF);
            }
            config[ cfg_num+GTP_ADDR_LENGTH] = (~(check_sum & 0xFF)) + 1;   //checksum
            config[ cfg_num+GTP_ADDR_LENGTH+1] =  1;                        //refresh 配置更新标志
        }
        else if(touchIC == GT5688 || touchIC == GT917S) 
        {
            for (i = GTP_ADDR_LENGTH; i < (cfg_num+GTP_ADDR_LENGTH -3); i += 2) 
            {
            check_sum += (config[i] << 8) + config[i + 1];
            }

            check_sum = 0 - check_sum;
            GTP_DEBUG("Config checksum: 0x%04X", check_sum);
            //更新checksum
            config[(cfg_num+GTP_ADDR_LENGTH -3)] = (check_sum >> 8) & 0xFF;
            config[(cfg_num+GTP_ADDR_LENGTH -2)] = check_sum & 0xFF;
            config[(cfg_num+GTP_ADDR_LENGTH -1)] = 0x01;
        }

    //写入配置信息
    for (retry = 0; retry < 5; retry++)
    {
        ret = GTP_I2C_Write(GTP_ADDRESS, config , cfg_num + GTP_ADDR_LENGTH+2);
        if (ret > 0)
        {
            break;
        }
    }
    Delay(0xfffff);             //延迟等待芯片更新

#if 1   //读出写入的数据,检查是否正常写入
    //检验读出的数据与写入的是否相同
    {
            uint16_t i;
            uint8_t buf[300];
             buf[0] = config[0];
             buf[1] =config[1];    //寄存器地址

            GTP_DEBUG_FUNC();

            ret = GTP_I2C_Read(GTP_ADDRESS, buf, sizeof(buf));

                    GTP_DEBUG("read ");

                    GTP_DEBUG_ARRAY(buf,cfg_num);

                GTP_DEBUG("write ");

                    GTP_DEBUG_ARRAY(config,cfg_num);

                    //不对比版本号
            for(i=1;i<cfg_num+GTP_ADDR_LENGTH-3;i++)
            {

                if(config[i] != buf[i])
                {
                    GTP_ERROR("Config fail ! i = %d ",i);
                            free(config);
                    return -1;
                }
            }
            if(i==cfg_num+GTP_ADDR_LENGTH-3)
                GTP_DEBUG("Config success ! i = %d ",i);
    }
#endif
    free(config);
#endif

     /*使能中断,这样才能检测触摸数据*/
    //  I2C_GTP_IRQEnable();

    GTP_Get_Info();

    return 0;
}

/*******************************************************
Function:
    Read chip version.
Input:
    client:  i2c device
    version: buffer to keep ic firmware version
Output:
    read operation return.
        2: succeed, otherwise: failed
*******************************************************/
int32_t GTP_Read_Version(void)
{
    int32_t ret = -1;
    uint8_t buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};    //寄存器地址

    GTP_DEBUG_FUNC();

    ret = GTP_I2C_Read(GTP_ADDRESS, buf, sizeof(buf));
    if (ret < 0)
    {
        GTP_ERROR("GTP read version failed");
        return ret;
    }
    if (buf[2] == '5')
    {
        GTP_INFO("IC1 Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);

                //GT5688芯片
                if(buf[2] == '5' && buf[3] == '6' && buf[4] == '8'&& buf[5] == '8')
                    touchIC = GT5688;
    }        
    else if (buf[5] == 0x00)
    {
        GTP_INFO("IC2 Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);

                //GT911芯片
                if(buf[2] == '9' && buf[3] == '1' && buf[4] == '1')
                    touchIC = GT911;
    }
    else if (buf[5] == '7')
    {
        GTP_INFO("IC3 Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);

                //GT9147芯片
                if(buf[2] == '9' && buf[3] == '1' && buf[4] == '4' && buf[5] == '7')
                    touchIC = GT9147;
        }
    else
    {
        GTP_INFO("IC3 Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);

                //GT9157芯片
                if(buf[2] == '9' && buf[3] == '1' && buf[4] == '5' && buf[5] == '7')
                    touchIC = GT9157;
        }
    return ret;
}

/*******************************************************
Function:
    I2c test Function.
Input:
    client:i2c client.
Output:
    Executive outcomes.
        2: succeed, otherwise failed.
*******************************************************/
static int8_t GTP_I2C_Test( void)
{
    uint8_t test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
    uint8_t retry = 0;
    int8_t ret = -1;

    GTP_DEBUG_FUNC();

    while(retry++ < 5)
    {
        ret = GTP_I2C_Read(GTP_ADDRESS, test, 3);
        if (ret > 0)
        {
            return ret;
        }
        GTP_ERROR("GTP i2c test failed time %d.",retry);
    }
    return ret;
}

//检测到触摸中断时调用,
void GTP_TouchProcess(void)
{
  GTP_DEBUG_FUNC();
  Goodix_TS_Work_Func();

}

//MODULE_DESCRIPTION("GTP Series Driver");
//MODULE_LICENSE("GPL");

触摸接口函数

static void touchpad_init(void)
{
    /*Your code comes here*/
    GTP_Init_Panel();
}

/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    /*Save the pressed coordinates and the state*/
    if(touchpad_is_pressed()) {
        touchpad_get_xy(&last_x, &last_y);
        data->state = LV_INDEV_STATE_PR;
    }
    else {
        data->state = LV_INDEV_STATE_REL;
    }

    /*Set the last pressed coordinates*/
    data->point.x = last_x;
    data->point.y = last_y;
}

/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
    /*Your code comes here*/
   if(Get_Touchpad_Is_Down())
            return true;
        else
            return false;
}

/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    /*Your code comes here*/

    (*x) = Get_Touchpad_x();
    (*y) = MY_DISP_VER_RES -  Get_Touchpad_y();
}

资源连接

链接: https://pan.baidu.com/s/1PLWZkBRcj3iDwr_9Vth3Ew?pwd=2bxa 提取码: 2bxa

有什么疑问可以邮箱或者留言

点个赞吧!!

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇