OpenHarmony南向之TP触摸屏
OpenHarmony南向之TP触摸屏
概述
Touchscreen驱动用于驱动触摸屏使其正常工作,该驱动主要完成如下工作:对触摸屏驱动IC进行上电、配置硬件管脚并初始化其状态、注册中断、配置通信接口(I2C或SPI)、设定Input相关配置、下载及更新固件等操作。
Touchscreen驱动基于HDF的Input驱动模型
Input驱动模型
Input驱动模型基于HDF驱动框架、Platform接口、OSAL接口进行开发,向上对接规范化的驱动接口HDI(Hardware Device Interface)层,通过Input-HDI层对外提供硬件能力,即上层Input Service可以通过HDI接口层获取相应的驱动能力,进而操控Touchscreen等输入设备。
Input驱动模型核心部分由设备管理层、公共驱动层、器件驱动层组成。
- Input设备管理:为各类输入设备驱动提供Input设备的注册、注销接口,同时对Input设备列表进行统一管理。
- Input平台驱动:指各类Input设备的公共抽象驱动(例如触摸屏的公共驱动),该部分主要负责对板级硬件进行初始化、硬件中断处理、向manager注册Input设备等。
- Input器件驱动:指各器件厂家的差异化驱动,开发者可以通过适配平台驱动预留的差异化接口进行器件驱动开发,实现器件驱动开发量最小化。
- Input数据通道(event hub):提供一套通用的数据上报通道,各类别的Input设备驱动均可用此通道上报Input事件。
- Input配置解析(hcs):负责对Input设备的板级配置及器件私有配置进行解析及管理。
Touchscreen驱动架构
现在以RK3568为例,来看看Touchscreen整个驱动架构
主要相关代码路径:
drivers/hdf_core/framework/model/input/driver/touchscreen/
vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs
vendor/hihope/rk3568/hdf_config/khdf/input/input_config.hcs
主要代码框架:
下面详细展开
hcs
先看看hcs文件
device_info.hcs
input :: host {
hostName = "input_host";
priority = 100;
device_input_manager :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
preload = 0;
permission = 0660;
moduleName = "HDF_INPUT_MANAGER";
serviceName = "hdf_input_host";
deviceMatchAttr = "";
}
}
device_hdf_touch :: device {
device0 :: deviceNode {
policy = 2;
priority = 120;
preload = 0;
permission = 0660;
moduleName = "HDF_TOUCH";
serviceName = "hdf_input_event1";
deviceMatchAttr = "touch_device1";
}
}
device_touch_chip :: device {
device0 :: deviceNode {
policy = 0;
priority = 130;
preload = 0;
permission = 0660;
moduleName = "HDF_TOUCH_GT911";
serviceName = "hdf_touch_gt911_service";
deviceMatchAttr = "zsj_gt911_5p5";
}
}
...
}
从上面可以看出,与触摸相关的主要是3个节点,刚好与实现TS驱动模型对应:
- HDF_INPUT_MANAGER:Input设备管理
- HDF_TOUCH:触摸屏的公共抽象驱动
- HDF_TOUCH_GT911:具体的Input器件驱动
后面章节会基于源码详细展开这3块
input_config.hcs
root {
input_config {
touchConfig {
touch0 {
boardConfig {
match_attr = "touch_device1";
inputAttr {
/* 0:touch 1:key 2:keyboard 3:mouse 4:button 5:crown 6:encoder */
inputType = 0;
solutionX = 720;
solutionY = 1280;
devName = "main_touch";
}
busConfig {
// 0:i2c 1:spi
busType = 0;
busNum = 1;
clkGpio = 86;
dataGpio = 87;
i2cClkIomux = [0x114f0048, 0x403];
i2cDataIomux = [0x114f004c, 0x403];
}
pinConfig {
rstGpio = 14;
intGpio = 13;
rstRegCfg = [0x112f0094, 0x400];
intRegCfg = [0x112f0098, 0x400];
}
powerConfig {
/* 0:unused 1:ldo 2:gpio 3:pmic */
vccType = 2;
vccNum = 20; // gpio20
vccValue = 1800;
vciType = 1;
vciNum = 12; // ldo12
vciValue = 3300;
}
featureConfig {
capacitanceTest = 0;
gestureMode = 0;
gloverMOde = 0;
coverMode = 0;
chargerMode = 0;
knuckleMode = 0;
}
}
chipConfig {
template touchChip {
match_attr = "";
chipName = "gt911";
vendorName = "zsj";
chipInfo = "AAAA11222"; // 4-ProjectName, 2-TP IC, 3-TP Module
/* 0:i2c 1:spi*/
busType = 0;
deviceAddr = 0x5D;
/* 0:None 1:Rising 2:Failing 4:High-level 8:Low-level */
irqFlag = 2;
maxSpeed = 400;
chipVersion = 0; //parse Coord TypeA
powerSequence {
/* [type, status, dir , delay]
<type> 0:none 1:vcc-1.8v 2:vci-3.3v 3:reset 4:int
<status> 0:off or low 1:on or high 2:no ops
<dir> 0:input 1:output 2:no ops
<delay> meanings delay xms, 20: delay 20ms
*/
powerOnSeq = [4, 0, 1, 5,
3, 0, 1, 10,
3, 1, 1, 60,
4, 2, 0, 50];
suspendSeq = [3, 0, 2, 10];
resumeSeq = [3, 1, 2, 10];
powerOffSeq = [3, 0, 2, 10,
1, 0, 2, 20];
}
}
chip0 :: touchChip {
match_attr = "zsj_gt911_5p5";
chipInfo = "ZIDN45100"; // 4-ProjectName, 2-TP IC, 3-TP Module
chipVersion = 0; //parse point by TypeA
}
...
}
...
}
}
}
}
device_info
中的节点通过 deviceMatchAttr
和 match_attr
字段匹配配置,
chipConfig配置中主要定义了一些与实际硬件相关的信息:
- 硬件接口的总线类型,主要有2类:SPI和I2C
- 设备地址,这里使用的是I2C接口,所以就是I2C从设备的地址
- 总线最高速率
- 中断触发方式:边缘触发(上升沿、下降沿),还是电平触发(高低电平)
- 电源相关的时序:上下电时序,休眠和唤醒时序
- 自定义的一些info,版本等
驱动源码分析
HDF_TOUCH_GT911驱动
HDF_TOUCH_GT911驱动(touch_gt911.c
)初始化(HdfGoodixChipInit
)的主要过程:
//获取上面hcs中的chipConfig
chipCfg = ChipConfigInstance()
//前面获取的配置
chipDev->chipCfg = chipCfg;
//函数操作集,主要包括初始化,休眠唤醒,数据处理,固件更新,能力设置等接口,见后面定义
chipDev->ops = &g_gt911ChipOps;
chipDev->chipName = chipCfg->chipName;
chipDev->vendorName = chipCfg->vendorName;
device->priv = (void *)chipDev;
//注册 TouchChipDevice, 具体实现在后面的`HDF_TOUCH`驱动中
RegisterTouchChipDevice(chipDev)
static struct TouchChipOps g_gt911ChipOps = {
.Init = ChipInit,
.Detect = ChipDetect,
.Resume = ChipResume,
.Suspend = ChipSuspend,
.DataHandle = ChipDataHandle,
.UpdateFirmware = UpdateFirmware,
.SetAbility = SetAbility,
};
RegisterTouchChipDevice()
函数的具体实现在后面的 HDF_TOUCH
驱动
HDF_TOUCH驱动
HDF_TOUCH驱动(hdf_touch.c
)主要有3大块
- 驱动初始化
部分伪代码:
//获取上面hcs中的 boardConfig
boardCfg = BoardConfigInstance(device);
// 初始化驱动数据,Setup bus接口(这里主要是i2c)
ret = TouchDriverInit(touchDriver, boardCfg);
//增加到驱动列表
AddTouchDriver(touchDriver);
//对于RK平台,这里会注册一个PM的监听器,用来处理休眠和唤醒
#if defined(CONFIG_ARCH_ROCKCHIP)
HdfTouchDriverRegisterPowerListener(device);
#endif
- Bind接口
通过Bind对外提供IoService接口(Dispatch),主要有以下接口:
switch (cmd) {
case GET_DEV_TYPE:
ret = TouchGetDevType(touchDriver, reply);
break;
case SET_PWR_STATUS:
ret = TouchSetPowerStatus(touchDriver, data);
break;
case GET_PWR_STATUS:
ret = TouchGetPowerStatus(touchDriver, reply);
break;
case GET_CHIP_NAME:
case GET_VENDOR_NAME:
case GET_CHIP_INFO:
ret = TouchGetDeviceStrInfo(touchDriver, cmd, reply);
break;
case GET_DEV_ATTR:
ret = TouchGetDeviceAttr(touchDriver, reply);
break;
case GET_DEV_ABILITY:
ret = TouchGetDeviceAbility(touchDriver, reply);
break;
case SET_GESTURE_MODE:
ret = TouchSetGestureMode(touchDriver, data);
break;
case RUN_CAPAC_TEST:
ret = TouchSelfCapacitance(touchDriver, data, reply);
break;
case RUN_EXTRA_CMD:
ret = TouchRunExtraCmd(touchDriver, data);
break;
default:
ret = HDF_SUCCESS;
HDF_LOGE("%s: cmd unknown, cmd = 0x%x", __func__, cmd);
break;
}
RegisterTouchChipDevice()
函数实现
部分伪代码:
//绑定驱动和设备
ret = DeviceBindDriver(chipDev);
//主要是上电操作,Detect设备,UpdateFirmware,
//配置使能中断,设置中断处理函数(坐标上报等就在里面)等操作,这里就不展开了
ret = ChipDriverInit(chipDev);
//注册Input 设备, 具体实现在后面的 HDF_INPUT_MANAGER 驱动中
ret = RegisterInputDevice(inputDev);
//调用chipdev的SetAbility接口
chipDev->ops->SetAbility(chipDev);
HDF_INPUT_MANAGER驱动
HDF_INPUT_MANAGER驱动(hdf_input_device_manager.c
),这里我们主要看下RegisterInputDevice()
函数的实现
部分伪代码:
//分配设备ID
ret = AllocDeviceID(inputDev);
//创建设备节点
ret = CreateDeviceNode(inputDev);
//分配相关的buf,主要是pkgBuf和eventBuf
ret = AllocPackageBuffer(inputDev);
#ifndef __LITEOS_M__
//初始化Event的工作队列,
ret = InitEventWorkQueue(inputDev);
#endif // __LITEOS_M__
//加入Input设备列表
AddInputDevice(inputDev);