driver-peripherals-touch-des.md 18.2 KB
Newer Older
D
duangavin123 已提交
1
# TOUCHSCREEN<a name="ZH-CN_TOPIC_0000001052857350"></a>
N
NEEN 已提交
2

D
duangavin123 已提交
3
-   [概述](#section175431838101617)
D
duangavin123 已提交
4 5
-   [接口说明](#section105459441659)
-   [开发步骤](#section65745222184)
D
duangavin123 已提交
6 7 8 9 10 11 12 13 14 15
-   [开发实例](#section263714411191)
    -   [设备描述配置](#section18249155619195)
    -   [板级配置及器件私有配置](#section3571192072014)
    -   [添加器件驱动](#section6356758162015)


## 概述<a name="section175431838101617"></a>

-   **Touchscreen驱动主要任务**

16
    Touchscreen驱动用于驱动触摸屏使其正常工作,该驱动主要完成如下工作:对触摸屏驱动IC进行上电、配置硬件管脚并初始化其状态、注册中断、配置通信接口(I2C或SPI)、设定Input相关配置、下载及更新固件等操作。
D
duangavin123 已提交
17 18 19 20


-   **Touchscreen驱动层次说明**

21
    本节主要介绍基于Input驱动模型开发touchscreen器件驱动,Input模型整体的框架如[图1](#fig6251184817261)。
D
duangavin123 已提交
22

23
    Input驱动模型基于HDF驱动框架、Platform接口、OSAL接口进行开发,向上对接规范化的驱动接口HDI(Hardware Driver Interface)层,通过Input-HDI层对外提供硬件能力,即上层Input service可以通过HDI接口层获取相应的驱动能力,进而操控touchscreen等输入设备。
D
duangavin123 已提交
24 25


26
**图 1**  基于HDF驱动框架的Input驱动模型<a name="fig6251184817261"></a>  
D
duangavin123 已提交
27
![](figures/基于HDF驱动框架的input驱动模型.png "基于HDF驱动框架的input驱动模型")
D
duangavin123 已提交
28 29 30 31 32

-   **Input驱动模型介绍**

    Input驱动模型核心部分由设备管理层、公共驱动层、器件驱动层组成。器件产生的数据借助平台数据通道能力从内核传递到用户态,驱动模型通过配置文件适配不同器件及硬件平台,提高开发者的器件驱动开发效率。如下部分为模型各部分的说明:

33
    - Input设备管理:为各类输入设备驱动提供Input设备的注册、注销接口,同时统一管理Input设备列表。
D
duangavin123 已提交
34

35
    - Input平台驱动:指各类Input设备的公共抽象驱动(例如触摸屏的公共驱动),负责对板级硬件进行初始化、硬件中断处理、向manager注册Input设备等。
D
duangavin123 已提交
36

37
    - Input器件驱动:指各器件厂家的差异化驱动,通过适配平台驱动预留的差异化接口,实现器件驱动开发量最小化。
D
duangavin123 已提交
38

39
    - Input数据通道:提供一套通用的数据上报通道,各类别的Input设备驱动均可用此通道上报Input事件。
D
duangavin123 已提交
40

41
    - Input配置解析:负责对Input设备的板级配置及器件私有配置进行解析及管理。
D
duangavin123 已提交
42 43 44 45


-   **基于HDF驱动框架开发器件驱动的优势**

46
    在HDF(Hardware Driver Foundation)[驱动管理框架](driver-hdf-development.md)的基础上,Input驱动模型调用OSAL接口层和Platfom接口层提供的基础接口进行开发,包括bus通信接口、操作系统原生接口(memory、lock、thread、timer等)。由于OSAL接口和Platform接口屏蔽了芯片平台差异,所以基于Input驱动模型实现的touchscreen驱动可以进行跨平台、跨OS迁移,以便逐步实现驱动的一次开发,多端部署。
D
duangavin123 已提交
47 48


D
duangavin123 已提交
49
## 接口说明<a name="section105459441659"></a>
D
duangavin123 已提交
50 51 52 53 54 55 56 57

Touchscreen器件的硬件接口相对简单,根据PIN脚的属性,可以简单分为如下三类:

-   电源接口
-   IO控制接口
-   通信接口

**图 2**  Touchscreen器件常用管脚<a name="fig1290384314416"></a>  
D
duangavin123 已提交
58
![](figures/Touchscreen器件常用管脚.png "Touchscreen器件常用管脚")
D
duangavin123 已提交
59 60 61 62

如上图所示的三类接口,分别做简要说明如下:

1.  **电源接口**
63 64
    -   LDO\_1P8:1.8V数字电路
    -   LDO\_3P3:3.3V模拟电路
D
duangavin123 已提交
65

66
        通常情况下,touchscreen驱动IC和LCD驱动IC是相互分离的,这种情况下,touchscreen驱动IC一般同时需要1.8V和3.3V两路供电。随着芯片演进,业内已有touchscreen驱动IC和LCD驱动IC集成在一颗IC中的芯片案例,对touchscreen而言,只需要关注1.8V供电即可,其内部需要的3.3V电源,会在驱动IC内部从LCD的VSP电源(典型值5.5V)中分出来。
D
duangavin123 已提交
67 68 69 70 71 72

2.  **IO控制接口**
    -   Reset:reset管脚,用于在系统休眠、唤醒时,由主机侧对驱动IC进行复位操作。
    -   INT:中断管脚,需要在驱动初始化时,配置为输入上拉状态。在驱动IC检测到外部触摸信号后,通过操作中断管脚来触发中断,器件驱动则会在中断处理函数中进行报点数据读取等操作。

3.  **通信接口**
D
duangavin123 已提交
73 74
    -   I2C:由于touchscreen的报点数据量相对较少,所以一般选用I2C方式传输数据。I2C的具体协议及对应操作接口,可以参考Platform接口层中的[“I2C”使用指南](driver-platform-i2c-des.md#section5361140416)
    -   SPI:部分厂商,由于需要传递的数据不止报点坐标,而是需要获取基础容值,数据量较大,所以会选用SPI通信方式。SPI的具体协议及对应操作接口,可以参考Platform接口层中的[“SPI” 使用指南](driver-platform-spi-des.md#section193356154511)
D
duangavin123 已提交
75 76


D
duangavin123 已提交
77
## 开发步骤<a name="section65745222184"></a>
D
duangavin123 已提交
78 79 80

Input驱动模型是基于HDF框架、Platform接口和OSAL接口开发,不区分操作系统和芯片平台,为touchscreen等输入器件提供统一的驱动开发架构。

81
如下以touchscreen器件驱动为例,说明Input驱动模型的完整加载流程:
D
duangavin123 已提交
82

D
duangavin123 已提交
83
(1)设备描述配置:由开发者参考已有模板进行设备描述配置,包括驱动加载顺序、板级硬件信息、器件私有数据信息等。
D
duangavin123 已提交
84

85
(2)加载Input设备管理驱动:Input设备管理驱动由HDF驱动加载,完成设备manager的创建并对其初始化。
D
duangavin123 已提交
86

D
duangavin123 已提交
87
(3)加载平台驱动:平台驱动由HDF框架加载,主要完成板级配置解析及硬件初始化,并提供器件注册接口。
D
duangavin123 已提交
88

D
duangavin123 已提交
89
(4)加载器件驱动:器件驱动也由HDF框架加载,完成器件设备的实例化,包括器件私有配置解析和平台预留的差异化接口适配。
D
duangavin123 已提交
90

D
duangavin123 已提交
91
(5)器件设备向平台驱动注册:将实例化的器件设备向平台驱动注册,实现设备和驱动的绑定,并完成中断注册、上下电等器件初始化工作。
D
duangavin123 已提交
92

93
(6)Input设备注册:在器件初始化完成后,实例化Input设备,并将其注册到Input manager进行管理。
D
duangavin123 已提交
94

D
duangavin123 已提交
95
请参考如下相关步骤:
D
duangavin123 已提交
96 97 98

1.  设备描述配置

D
duangavin123 已提交
99
    目前Input驱动基于HDF驱动框架编写,驱动的加载启动由HDF驱动管理框架统一处理。首先需要在对应的配置文件中,将驱动信息注册进去,如是否加载、加载优先级,此后HDF驱动框架会逐一启动注册过的驱动模块。驱动的相关配置请参考[HDF驱动框架配置指导](driver-hdf-development.md#section1969312275533)。
D
duangavin123 已提交
100 101 102 103 104 105 106

2.  板级配置及Touchscreen器件私有配置

    配置对应的IO管脚功能,例如对单板上为touchscreen设计预留的I2C Pin脚,需设置对应的寄存器,使其选择I2C的通信功能。

3.  实现器件差异化适配接口

D
duangavin123 已提交
107
    根据硬件单板设计的通信接口,使用Platform接口层提供的管脚操作接口配置对应的复位管脚、中断管脚以及电源操作,对于GPIO的操作,可参考[GPIO操作接口指导](driver-platform-gpio-des.md#section1635911016188)
D
duangavin123 已提交
108 109 110


## 开发实例<a name="section263714411191"></a>
N
NEEN 已提交
111 112 113

本实例提供touchscreen驱动开发示例,并简要对具体关键点进行开发说明。

D
duangavin123 已提交
114
### 设备描述配置<a name="section18249155619195"></a>
N
NEEN 已提交
115

116
如下配置主要包含Input驱动模型各模块层级信息,具体原理可参考[HDF驱动开发指南](driver-hdf-development.md),HDF框架依据该配置信息实现对Input模型各模块的依次加载等。
N
NEEN 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

```
input :: host {
            hostName = "input_host";
            priority = 100;
            device_input_manager :: device {
                device0 :: deviceNode {
                    policy = 2;        // 向外发布服务
                    priority = 100;    // 加载优先级,在input模块内,manager模块优先级应为最高
                    preload = 0;       // 加载该驱动 0:加载 1:不加载
                    permission = 0660;
                    moduleName = "HDF_INPUT_MANAGER";
                    serviceName = "input_dev_manager";
                    deviceMatchAttr = "";
                }
            }
            device_hdf_touch :: device {
                device0 :: deviceNode {
                    policy = 2;
                    priority = 120;
                    preload = 0;
                    permission = 0660;
                    moduleName = "HDF_TOUCH";
                    serviceName = "event1";
                    deviceMatchAttr = "touch_device1";
                }
            }

            device_touch_chip :: device {
                device0 :: deviceNode {
                    policy = 0;
                    priority = 130;
                    preload = 0;
                    permission = 0660;
                    moduleName = "HDF_TOUCH_SAMPLE";
                    serviceName = "hdf_touch_sample_service";
                    deviceMatchAttr = "zsj_sample_5p5";
                }
            }
}
```

D
duangavin123 已提交
159
### 板级配置及器件私有配置<a name="section3571192072014"></a>
N
NEEN 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

如下配置包含板级硬件配置及器件私有数据配置,实际业务开发时,可根据具体需求增删及修改如下配置文件信息。

```
root {
    input_config {
        touchConfig {
            touch0 {
                boardConfig {
                    match_attr = "touch_device1";
                    inputAttr {
                        inputType = 0;           // 0代表触摸屏
                        solutionX = 480; 
                        solutionY = 960;
                        devName = "main_touch";  // 设备名称
                    }
                    busConfig {
                        busType = 0;             // 0代表I2C
                        busNum = 6;
                        clkGpio = 86;
                        dataGpio = 87;
                        i2cClkIomux = [0x114f0048, 0x403];  // i2c_clk对应pin的寄存器配置
                        i2cDataIomux = [0x114f004c, 0x403]; // i2c_data对应pin的寄存器配置
                    }
                    pinConfig {
                        rstGpio = 3;
                        intGpio = 4;
                        rstRegCfg = [0x112f0094, 0x400];  // reset对应pin的寄存器配置
                        intRegCfg = [0x112f0098, 0x400];  // interrupt对应pin的寄存器配置
                    }
                    powerConfig {
                        vccType = 2;       // 1代表LDO、2代表GPIO、3代表PMIC
                        vccNum = 20;       // GPIO号为20
                        vccValue = 1800;   // 电压幅值为1800mV
                        vciType = 1;
                        vciNum = 12;
                        vciValue = 3300;
                    }
                    featureConfig {
                        capacitanceTest = 0;
                        gestureMode = 0;
                        gloverMOde = 0;
                        coverMode = 0;
                        chargerMode = 0;
                        knuckleMode = 0;
                    }
                }
                chipConfig {
                    template touchChip {
                        match_attr = "";
                        chipName = "sample";
                        vendorName = "zsj";
                        chipInfo = "AAAA11222";  // 1~4字符代表产品名,5~6字符代表IC型号,7~9字符代表模型型号
                        busType = 0;
                        deviceAddr = 0x5D;
                        irqFlag = 2;             // 1代表上升沿触发,2代表下降沿触发,4代表高电平触发,8代表低电平触发
                        maxSpeed = 400;
                        chipVersion = 0;
                        powerSequence {
                            /* 上电时序的配置含义说明:
                               [类型, 状态, 方向 , 延时]
                               <type> 0代表空,1代表vcc电源(1.8V),2代表VCI电源(3.3V),3代表复位管脚,4代表中断管脚
                               <status> 0代表下电或拉低,1代表上电或拉高,2代表无操作
                               <dir> 0代表输入方向,1代表输出方向,2代表无操作
                               <delay> 代表延时多少毫秒, 例如20代表延时20ms
                             */
                            powerOnSeq = [4, 0, 1, 0,
                                         3, 0, 1, 10,
                                         3, 1, 2, 60,
                                         4, 2, 0, 0];
                            suspendSeq = [3, 0, 2, 10];
                            resumeSeq = [3, 1, 2, 10];
                            powerOffSeq = [3, 0, 2, 10,
                                           1, 0, 2, 20];
                        }
                    }
                    chip0 :: touchChip {
                        match_attr = "zsj_sample_5p5";
                        chipInfo = "ZIDN45100";
                        chipVersion = 0;
                    }
                }
            }
        }
    }
}
```

D
duangavin123 已提交
248
### 添加器件驱动<a name="section6356758162015"></a>
N
NEEN 已提交
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405

在器件驱动中,主要实现了平台预留的差异化接口,以器件数据获取及解析进行示例说明。具体开发过程,需要根据实际使用的单板及器件进行适配。

```
/* 将从器件中读取到的报点数据解析为坐标 */
static void ParsePointData(ChipDevice *device, FrameData *frame, uint8_t *buf, uint8_t pointNum)
{
    int32_t resX = device->driver->boardCfg->attr.resolutionX;
    int32_t resY = device->driver->boardCfg->attr.resolutionY;

    for (int32_t i = 0; i < pointNum; i++) {
        frame->fingers[i].y = (buf[GT_POINT_SIZE * i + GT_X_LOW] & ONE_BYTE_MASK) |
                              ((buf[GT_POINT_SIZE * i + GT_X_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET);
        frame->fingers[i].x = (buf[GT_POINT_SIZE * i + GT_Y_LOW] & ONE_BYTE_MASK) |
                              ((buf[GT_POINT_SIZE * i + GT_Y_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET);
        frame->fingers[i].valid = true;
    }
}
/* 从器件中获取报点数据 */
static int32_t ChipDataHandle(ChipDevice *device)
{
    int32_t ret;
    uint8_t touchStatus = 0;
    uint8_t pointNum;
    uint8_t buf[GT_POINT_SIZE * MAX_SUPPORT_POINT] = {0};
    InputI2cClient *i2cClient = &device->driver->i2cClient;
    uint8_t reg[GT_ADDR_LEN] = {0};
    FrameData *frame = &device->driver->frameData;
    reg[0] = (GT_BUF_STATE_ADDR >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK;
    reg[1] = GT_BUF_STATE_ADDR & ONE_BYTE_MASK;
    ret = InputI2cRead(i2cClient, reg, GT_ADDR_LEN, &touchStatus, 1);
    if (ret < 0 || touchStatus == GT_EVENT_INVALID) {
        return HDF_FAILURE;
    }
    OsalMutexLock(&device->driver->mutex);
    (void)memset_s(frame, sizeof(FrameData), 0, sizeof(FrameData));
    if (touchStatus == GT_EVENT_UP) {
        frame->realPointNum = 0;
        frame->definedEvent = TOUCH_UP;
        goto exit;
    }
    reg[0] = (GT_X_LOW_BYTE_BASE >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK;
    reg[1] = GT_X_LOW_BYTE_BASE & ONE_BYTE_MASK;
    pointNum = touchStatus & GT_FINGER_NUM_MASK;
    if (pointNum <= 0 || pointNum > MAX_SUPPORT_POINT) {
        HDF_LOGE("%s: pointNum is invalid, %d", __func__, pointNum);
        (void)ChipCleanBuffer(i2cClient);
        OsalMutexUnlock(&device->driver->mutex);
        return HDF_FAILURE;
    }
    frame->realPointNum = pointNum;
    frame->definedEvent = TOUCH_DOWN;
    /* 从寄存器中读取报点值 */
    (void)InputI2cRead(i2cClient, reg, GT_ADDR_LEN, buf, GT_POINT_SIZE * pointNum);
    /* 解析报点值 */
    ParsePointData(device, frame, buf, pointNum);
exit:
    OsalMutexUnlock(&device->driver->mutex);
    if (ChipCleanBuffer(i2cClient) != HDF_SUCCESS) {
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

static struct TouchChipOps g_sampleChipOps = {
    .Init = ChipInit,
    .Detect = ChipDetect,
    .Resume = ChipResume,
    .Suspend = ChipSuspend,
    .DataHandle = ChipDataHandle,
};

static TouchChipCfg *ChipConfigInstance(struct HdfDeviceObject *device)
{
    TouchChipCfg *chipCfg = (TouchChipCfg *)OsalMemAlloc(sizeof(TouchChipCfg));
    if (chipCfg == NULL) {
        HDF_LOGE("%s: instance chip config failed", __func__);
        return NULL;
    }
    (void)memset_s(chipCfg, sizeof(TouchChipCfg), 0, sizeof(TouchChipCfg));
    /* 解析器件私有配置 */
    if (ParseTouchChipConfig(device->property, chipCfg) != HDF_SUCCESS) {
        HDF_LOGE("%s: parse chip config failed", __func__);
        OsalMemFree(chipCfg);
        chipCfg = NULL;
    }
    return chipCfg;
}

static ChipDevice *ChipDeviceInstance(void)
{
    ChipDevice *chipDev = (ChipDevice *)OsalMemAlloc(sizeof(ChipDevice));
    if (chipDev == NULL) {
        HDF_LOGE("%s: instance chip device failed", __func__);
        return NULL;
    }
    (void)memset_s(chipDev, sizeof(ChipDevice), 0, sizeof(ChipDevice));
    return chipDev;
}

static void FreeChipConfig(TouchChipCfg *config)
{
    if (config->pwrSeq.pwrOn.buf != NULL) {
        OsalMemFree(config->pwrSeq.pwrOn.buf);
    }
    if (config->pwrSeq.pwrOff.buf != NULL) {
        OsalMemFree(config->pwrSeq.pwrOff.buf);
    }
    OsalMemFree(config);
}

static int32_t HdfSampleChipInit(struct HdfDeviceObject *device)
{
    TouchChipCfg *chipCfg = NULL;
    ChipDevice *chipDev = NULL;
    HDF_LOGE("%s: enter", __func__);
    if (device == NULL) {
        return HDF_ERR_INVALID_PARAM;
    }
    /* 器件私有配置解析 */
    chipCfg = ChipConfigInstance(device);
    if (chipCfg == NULL) {
        return HDF_ERR_MALLOC_FAIL;
    }
    /* 器件设备实例化 */
    chipDev = ChipDeviceInstance();
    if (chipDev == NULL) {
        goto freeCfg;
    }
    chipDev->chipCfg = chipCfg;
    chipDev->ops = &g_sampleChipOps;
    chipDev->chipName = chipCfg->chipName;
    chipDev->vendorName = chipCfg->vendorName;

   /* 器件设备注册到平台驱动 */
    if (RegisterChipDevice(chipDev) != HDF_SUCCESS) {
        goto freeDev;
    }
    HDF_LOGI("%s: exit succ, chipName = %s", __func__, chipCfg->chipName);
    return HDF_SUCCESS;

freeDev:
    OsalMemFree(chipDev);
freeCfg:
    FreeChipConfig(chipCfg);
    return HDF_FAILURE;
}

struct HdfDriverEntry g_touchSampleChipEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_TOUCH_SAMPLE",
    .Init = HdfSampleChipInit,
};

HDF_INIT(g_touchSampleChipEntry);
```