openmv/lib/alif/components/Source/GT911_touch_driver.c
iabdalkader daf2bb30da misc: Restructure repo.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-04-13 08:28:34 +02:00

728 lines
18 KiB
C

/* Copyright (C) 2023 Alif Semiconductor - All Rights Reserved.
* Use, distribution and modification of this code is permitted under the
* terms stated in the Alif Semiconductor Software License Agreement
*
* You should have received a copy of the Alif Semiconductor Software
* License Agreement with this file. If not, please write to:
* contact@alifsemi.com, or visit: https://alifsemi.com/license
*
*/
/**************************************************************************//**
* @file GT911_touch_driver.c
* @author Prasanna Ravi, Ahmad Rashed and Chandra Bhushan Singh
* @email prasanna.ravi@alifsemi.com , ahmad.rashed@alifsemi.com and
* chandrabhushan.singh@alifsemi.com
* @version V1.0.0
* @date 3 August 2023
* @brief GT911 touch screen driver.
* @bug None.
* @Note None.
******************************************************************************/
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "RTE_Components.h"
#include "system_utils.h"
/* GPIO Driver */
#include "Driver_GPIO.h"
/* I2C Driver */
#include "Driver_I2C.h"
/* Pinmux Driver */
#include "pinconf.h"
/* Touchscreen Driver */
#include "Driver_Touch_Screen.h"
#if (!defined(RTE_Drivers_GT911))
#error "GT911 Touch not configured in RTE_Components.h!"
#endif
#if !( RTE_TOUCH_SCREEN)
#error "Touch Driver not enabled in the RTE_Device.h"
#endif
#if !(RTE_GT911)
#error "GT911 Touch is not enabled in the RTE_Device.h"
#endif
#if !(RTE_ACTIVE_TOUCH_POINTS)
#error "GT911 at least enable one active touch point"
#endif
/* Touch reset pin control GPIO port */
extern ARM_DRIVER_GPIO ARM_Driver_GPIO_(RTE_GT911_TOUCH_RESET_GPIO_PORT);
static ARM_DRIVER_GPIO *GPIO_Driver_RST = &ARM_Driver_GPIO_(RTE_GT911_TOUCH_RESET_GPIO_PORT);
/* Touch INT pin control GPIO port */
extern ARM_DRIVER_GPIO ARM_Driver_GPIO_(RTE_GT911_TOUCH_INT_GPIO_PORT);
static ARM_DRIVER_GPIO *GPIO_Driver_INT = &ARM_Driver_GPIO_(RTE_GT911_TOUCH_INT_GPIO_PORT);
/* I2C Driver instance for Touch screen communication */
#if(RTE_GT911_TOUCH_I2C_INSTANCE == 4)
#define GT911_TOUCH_I2C_INSTANCE I3C
#else
#define GT911_TOUCH_I2C_INSTANCE RTE_GT911_TOUCH_I2C_INSTANCE
#endif
extern ARM_DRIVER_I2C ARM_Driver_I2C_(GT911_TOUCH_I2C_INSTANCE);
static ARM_DRIVER_I2C *I2C_Driver = &ARM_Driver_I2C_(GT911_TOUCH_I2C_INSTANCE);
/* GT911 Driver specific macros */
/* Set the GT911_SLAVE_ADDR macro to either 0x5D or 0x14 to select the desired I2C slave address */
#define GT911_SLAVE_ADDR RTE_GT911_TOUCH_I2C_SLAVE_ADDRESS_SEL
#define GT911_COMMAND_REG 0x8040
#define GT911_PRODUCT_ID 0x8140
#define GT911_TOUCH_STATUS 0x814E
#define GT911_NUM_TOUCH_POINTS_MSK 0x7
#define GT911_FIRST_TOUCHPOINT_DATA 0x8150
#define GT911_REG_INDX_SIZE 2
#define TOUCH_I2C_TIMEOUT_US 100000
/* I2C transmission stop or restart selection macros */
#define RESTART (0X01)
#define STOP (0X00)
/* GT911 Driver status */
#define GT911_FLAG_DRV_INIT_DONE (1U << 0)
#define GT911_FLAG_POWER_ENABLED (1U << 1)
/* GT911 Touch event variables */
volatile uint8_t touch_event_gpio;
volatile uint8_t touch_event_i2c;
/* GT911 Touch driver Info variable */
static struct GT911_DRV_INFO
{
uint32_t touch_drv_status;
} gt911_drv_info;
#define ARM_TOUCH_SCREEN_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1, 0) /*Driver version*/
/*Driver version*/
static const ARM_DRIVER_VERSION DriverVersion =
{
ARM_TOUCH_SCREEN_API_VERSION,
ARM_TOUCH_SCREEN_DRV_VERSION
};
/* Driver Capabilities */
static const ARM_TOUCH_SCREEN_CAPABILITIES DriverCapabilities =
{
0, /* Not supports reentrant_operation */
1, /* Multiple touch points supported*/
0 /* reserved (must be zero)*/
};
/**
\fn ARM_DRIVER_VERSION ARM_TOUCH_SCREEN_GetVersion (void)
\brief Get touch screen driver version.
\return \ref ARM_DRIVER_VERSION.
*/
static ARM_DRIVER_VERSION ARM_TOUCH_SCREEN_GetVersion (void)
{
return DriverVersion;
}
/**
\fn ARM_TOUCH_SCREEN_CAPABILITIES ARM_TOUCH_SCREEN_GetCapabilities (void)
\brief Get touch screen driver capabilities.
\return \ref ARM_MIPI_DPHY_CAPABILITIES.
*/
static ARM_TOUCH_SCREEN_CAPABILITIES ARM_TOUCH_SCREEN_GetCapabilities (void)
{
return DriverCapabilities;
}
/**
\fn int32_t TOUCH_Write(uint8_t dev_addr, uint16_t reg_addr,
uint8_t *reg_data, uint8_t num_bytes)
\brief write register value to GT911 Touch screen registers.
\param[in] dev_addr : I2c slave address.
\param[in] reg_addr : Register address.
\param[in] reg_data : pointer to register data.
\param[in] num_bytes : Number of bytes to write.
\return \ref execution_status.
*/
static int32_t TOUCH_Write(uint8_t dev_addr, uint16_t reg_addr,
uint8_t *reg_data, uint8_t num_bytes)
{
uint8_t tx_data[12] = {0};
uint8_t i;
int32_t ret;
int32_t lp_count = 0;
tx_data[0] = (reg_addr >> 8) & 0xFF;
tx_data[1] = (reg_addr) & 0xFF;
if (num_bytes)
{
for(i = 0; i < num_bytes; i++)
tx_data[i + GT911_REG_INDX_SIZE] = reg_data[i];
}
touch_event_i2c = 0;
do
{
ret = I2C_Driver->MasterTransmit(dev_addr, tx_data,
num_bytes + GT911_REG_INDX_SIZE, STOP);
if(lp_count++ < TOUCH_I2C_TIMEOUT_US)
{
sys_busy_loop_us(1);
}
else
{
break;
}
} while(ret == ARM_DRIVER_ERROR_BUSY);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
lp_count = 0;
while (touch_event_i2c == 0)
{
if(lp_count++ < TOUCH_I2C_TIMEOUT_US)
{
sys_busy_loop_us(1);
}
else
{
return ARM_DRIVER_ERROR;
}
}
if (!(touch_event_i2c & ARM_I2C_EVENT_TRANSFER_DONE))
{
return ARM_DRIVER_ERROR;
}
return ARM_DRIVER_OK;
}
/**
\fn int32_t TOUCH_Read(uint8_t dev_addr, uint16_t reg_addr,
uint8_t *reg_data, uint8_t num_bytes)
\brief read register value from GT911 Touch screen registers.
\param[in] dev_addr : I2c slave address.
\param[in] reg_addr : Register address.
\param[in] reg_data : pointer to register data.
\param[in] num_bytes : Number of bytes to read.
\return \ref execution_status.
*/
static int32_t TOUCH_Read(uint8_t dev_addr, uint16_t reg_addr,
uint8_t *reg_data, uint8_t num_bytes)
{
uint8_t rx_data[12] = {0};
int32_t ret;
int32_t lp_count = 0;
ret = TOUCH_Write(dev_addr, reg_addr, NULL, 0);
if (ret != ARM_DRIVER_OK)
{
return ret;
}
touch_event_i2c = 0;
ret = I2C_Driver->MasterReceive(dev_addr, rx_data, num_bytes, STOP);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
while (touch_event_i2c == 0)
{
if(lp_count++ < TOUCH_I2C_TIMEOUT_US)
{
sys_busy_loop_us(1);
}
else
{
return ARM_DRIVER_ERROR;
}
}
if (!(touch_event_i2c & ARM_I2C_EVENT_TRANSFER_DONE))
{
return ARM_DRIVER_ERROR;
}
memcpy(reg_data, rx_data, num_bytes);
return ARM_DRIVER_OK;
}
/**
\fn int32_t TOUCH_IntEnable(bool enable)
\brief GT911 Touch screen INT line GPIO control.
\param[in] enable : true or false.
\return \ref execution_status.
*/
static int32_t TOUCH_IntEnable(bool enable)
{
uint32_t arg = ARM_GPIO_IRQ_POLARITY_LOW | ARM_GPIO_IRQ_EDGE_SENSITIVE_SINGLE | ARM_GPIO_IRQ_SENSITIVE_EDGE;
int32_t ret;
if(enable)
{
ret = GPIO_Driver_INT->Control(RTE_GT911_TOUCH_INT_PIN_NO, ARM_GPIO_ENABLE_INTERRUPT, &arg);
}
else
{
ret = GPIO_Driver_INT->Control(RTE_GT911_TOUCH_INT_PIN_NO, ARM_GPIO_DISABLE_INTERRUPT, NULL);
}
return ret;
}
/**
\fn void TOUCH_I2C_CB(uint32_t event)
\brief GT911 Touch screen I2C callback event.
\param[in] event: \ref I3C Event.
\return \ref execution_status.
*/
static void TOUCH_I2C_CB(uint32_t event)
{
touch_event_i2c |= event;
}
/**
\fn void TOUCH_GPIO_CB(uint32_t event)
\brief GT911 Touch screen GPIO callback event.
\param[in] event: \ref GPIO Interrupt events.
\return \ref execution_status.
*/
static void TOUCH_GPIO_CB(uint32_t event)
{
ARG_UNUSED (event);
(void)TOUCH_IntEnable(false);
touch_event_gpio = 1;
}
/**
\fn int32_t TOUCH_Reset(void)
\brief Hard Reset GT911 Touch Screen.
\param[in] none.
\return \ref execution_status.
*/
static int32_t TOUCH_Reset(void)
{
int32_t ret;
/* TOUCH INT pin */
ret = GPIO_Driver_INT->Control(RTE_GT911_TOUCH_INT_PIN_NO, ARM_GPIO_DISABLE_INTERRUPT, NULL);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = GPIO_Driver_INT->SetValue(RTE_GT911_TOUCH_INT_PIN_NO, GPIO_PIN_OUTPUT_STATE_LOW);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = GPIO_Driver_INT->SetDirection(RTE_GT911_TOUCH_INT_PIN_NO, GPIO_PIN_DIRECTION_OUTPUT);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
/* TOUCH RST pin */
ret = GPIO_Driver_RST->Control(RTE_GT911_TOUCH_RESET_PIN_NO, ARM_GPIO_DISABLE_INTERRUPT, NULL);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = GPIO_Driver_RST->SetValue(RTE_GT911_TOUCH_RESET_PIN_NO, GPIO_PIN_OUTPUT_STATE_LOW);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = GPIO_Driver_RST->SetDirection(RTE_GT911_TOUCH_RESET_PIN_NO, GPIO_PIN_DIRECTION_OUTPUT);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
sys_busy_loop_us(1000);
#if (GT911_SLAVE_ADDR == 0x14)
ret = GPIO_Driver_INT->SetValue(RTE_GT911_TOUCH_INT_PIN_NO, GPIO_PIN_OUTPUT_STATE_HIGH);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
sys_busy_loop_us(100);
#endif
ret = GPIO_Driver_RST->SetValue(RTE_GT911_TOUCH_RESET_PIN_NO, GPIO_PIN_OUTPUT_STATE_HIGH);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
sys_busy_loop_us(50000);
#if (GT911_SLAVE_ADDR == 0x14)
ret = GPIO_Driver_INT->SetValue(RTE_GT911_TOUCH_INT_PIN_NO, GPIO_PIN_OUTPUT_STATE_LOW);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
#endif
ret = GPIO_Driver_INT->SetDirection(RTE_GT911_TOUCH_INT_PIN_NO, GPIO_PIN_DIRECTION_INPUT);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
return ARM_DRIVER_OK ;
}
/**
\fn int32_t TOUCH_Init(void)
\brief Initialize GT911 Touch Screen.
This function will
- initialize i2c instance.
- initialize gpio interrupt and reset instance.
\param[in] none.
\return \ref execution_status.
*/
int32_t TOUCH_Init(void)
{
int32_t ret;
ret = I2C_Driver->Initialize(TOUCH_I2C_CB);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = I2C_Driver->PowerControl(ARM_POWER_FULL);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = I2C_Driver->Control(ARM_I2C_BUS_SPEED, ARM_I2C_BUS_SPEED_STANDARD);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = GPIO_Driver_INT->Initialize(RTE_GT911_TOUCH_INT_PIN_NO, &TOUCH_GPIO_CB);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = GPIO_Driver_INT->PowerControl(RTE_GT911_TOUCH_INT_PIN_NO, ARM_POWER_FULL);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = GPIO_Driver_RST->Initialize(RTE_GT911_TOUCH_RESET_PIN_NO, NULL);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = GPIO_Driver_RST->PowerControl(RTE_GT911_TOUCH_RESET_PIN_NO, ARM_POWER_FULL);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
return ARM_DRIVER_OK;
}
/**
\fn int32_t TOUCH_Setup(void)
\brief Setup GT911 Touch Screen.
This function will
- read GT911 product id, proceed only it is correct.
\param[in] none.
\return \ref execution_status.
*/
int32_t TOUCH_Setup(void)
{
uint8_t data = 0;
uint8_t product_id_1st_byte = 0;
int32_t ret;
ret = TOUCH_Reset();
{
if(ret != ARM_DRIVER_OK)
return ret;
}
ret = TOUCH_IntEnable(true);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = TOUCH_Write(GT911_SLAVE_ADDR, GT911_COMMAND_REG, &data, 1);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = TOUCH_Read(GT911_SLAVE_ADDR, GT911_PRODUCT_ID, &product_id_1st_byte, 1);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
/* "911" product id is in ascii character*/
if (product_id_1st_byte != '9')
{
return ARM_DRIVER_ERROR;
}
return ARM_DRIVER_OK;
}
/**
\fn int32_t TOUCH_Deinit(void)
\brief Un-initialize GT911 Touch Screen.
\param[in] none.
\return \ref execution_status.
*/
int32_t TOUCH_Deinit(void)
{
int32_t ret;
ret = I2C_Driver->PowerControl(ARM_POWER_OFF);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
ret = I2C_Driver->Uninitialize();
if(ret != ARM_DRIVER_OK)
{
return ret;
}
return ARM_DRIVER_OK;
}
/**
\fn int32_t ARM_TOUCH_SCREEN_Initialize (void)
\brief Initialize touch screen Interface.
\param[in] none.
\return \ref execution_status.
*/
static int32_t ARM_TOUCH_SCREEN_Initialize (void)
{
int32_t ret;
if(gt911_drv_info.touch_drv_status & GT911_FLAG_DRV_INIT_DONE)
{
return ARM_DRIVER_OK;
}
ret = TOUCH_Init();
if(ret != ARM_DRIVER_OK)
{
return ret;
}
gt911_drv_info.touch_drv_status = GT911_FLAG_DRV_INIT_DONE;
return ARM_DRIVER_OK;
}
/**
\fn int32_t ARM_TOUCH_SCREEN_Uninitialize (void)
\brief uninitialize touch screen Interface.
\return \ref execution_status.
*/
static int32_t ARM_TOUCH_SCREEN_Uninitialize (void)
{
int32_t ret;
ret = TOUCH_Deinit();
if(ret != ARM_DRIVER_OK)
{
return ret;
}
gt911_drv_info.touch_drv_status = 0;
return ARM_DRIVER_OK;
}
/**
\fn int32_t ARM_TOUCH_SCREEN_PowerControl (ARM_POWER_STATE state)
\brief Control touch screen Interface Power.
\param[in] state Power state.
\return \ref execution_status.
*/
static int32_t ARM_TOUCH_SCREEN_PowerControl (ARM_POWER_STATE state)
{
int32_t ret;
switch (state)
{
case ARM_POWER_OFF:
{
/*Disabling the IRQs*/
ret = TOUCH_IntEnable(false);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
gt911_drv_info.touch_drv_status &= ~GT911_FLAG_POWER_ENABLED;
break;
}
case ARM_POWER_FULL:
{
if (!(gt911_drv_info.touch_drv_status & GT911_FLAG_DRV_INIT_DONE))
{
return ARM_DRIVER_ERROR;
}
if (gt911_drv_info.touch_drv_status & GT911_FLAG_POWER_ENABLED)
{
return ARM_DRIVER_OK;
}
ret = TOUCH_Setup();
if(ret != ARM_DRIVER_OK)
{
return ret;
}
gt911_drv_info.touch_drv_status |= GT911_FLAG_POWER_ENABLED;
break;
}
case ARM_POWER_LOW:
default:
{
return ARM_DRIVER_ERROR_UNSUPPORTED;
}
}
return ARM_DRIVER_OK;
}
/**
\fn int32_t ARM_TOUCH_SCREEN_GetState (ARM_TOUCH_STATE *state)
\brief Get touch screen touch state.
\param[in] state pointer to ARM_TOUCH_STATE.
\return \ref execution_status.
*/
static int32_t ARM_TOUCH_SCREEN_GetState (ARM_TOUCH_STATE *state)
{
uint8_t data;
uint16_t nextXY = GT911_FIRST_TOUCHPOINT_DATA;
uint8_t rx_buf[4] = {0};
int32_t ret;
if (!(gt911_drv_info.touch_drv_status & GT911_FLAG_POWER_ENABLED))
{
return ARM_DRIVER_ERROR;
}
state->numtouches = 0;
if(touch_event_gpio)
{
touch_event_gpio = 0;
ret = TOUCH_Read(GT911_SLAVE_ADDR, GT911_TOUCH_STATUS, &data, 1);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
/* Get buffer status from GT911_TOUCH_STATUS 7th bit*/
bool msb = data & 0x80;
/* Get number of touch points from GT911_TOUCH_STATUS 0-3 bit*/
state->numtouches = ((data & GT911_NUM_TOUCH_POINTS_MSK) > RTE_ACTIVE_TOUCH_POINTS ?
RTE_ACTIVE_TOUCH_POINTS : (data & GT911_NUM_TOUCH_POINTS_MSK));
if (msb)
{
if(state->numtouches)
{
for(uint8_t i = 0; i < state->numtouches; i++)
{
ret = TOUCH_Read(GT911_SLAVE_ADDR, nextXY, rx_buf, 4);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
state->coordinates[i].x = rx_buf[1];
state->coordinates[i].y = rx_buf[3];
state->coordinates[i].x <<= 8;
state->coordinates[i].y <<= 8;
state->coordinates[i].x |= rx_buf[0];
state->coordinates[i].y |= rx_buf[2];
nextXY += 8U;
}
}
/*Clear status register*/
data = 0;
ret = TOUCH_Write(GT911_SLAVE_ADDR, GT911_TOUCH_STATUS, &data, 1);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
}
/* re-enable gt911 interrupt */
ret = TOUCH_IntEnable(true);
if(ret != ARM_DRIVER_OK)
{
return ret;
}
}
return ARM_DRIVER_OK;
}
extern ARM_DRIVER_TOUCH_SCREEN GT911;
ARM_DRIVER_TOUCH_SCREEN GT911 =
{
ARM_TOUCH_SCREEN_GetVersion,
ARM_TOUCH_SCREEN_GetCapabilities,
ARM_TOUCH_SCREEN_Initialize,
ARM_TOUCH_SCREEN_Uninitialize,
ARM_TOUCH_SCREEN_PowerControl,
ARM_TOUCH_SCREEN_GetState
};