• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 外设驱动开发示例<a name="ZH-CN_TOPIC_0000001157063303"></a>
2
3
4## 概述<a name="section86753818426"></a>
5
6本文档将介绍如何基于Hi3516DV300开发板完成基于HDF\_Input模型的触摸屏器件驱动开发,从而使开发者快速入门,进行基于OpenHarmony的外设驱动开发。
7
8### 硬件资源简介<a name="section123071189431"></a>
9
10Hi3516DV300开发板套件所提供的触摸屏器件IC为GT911,该器件采用标准I2C与主机通信,通过6pin软排线与主板连接。6pin分布以及实物连接图如下图所示:
11
12![](figures/6-pin-distribution-and-physical-connection.png)
13
14### Input模型简介<a name="section53684425430"></a>
15
16Input驱动模型核心部分由设备管理层、公共驱动层、器件驱动层组成。其中:
17
18-   设备管理层:主要为各类输入设备驱动提供input设备的注册、注销接口,同时统一管理input设备列表;
19-   公共驱动层:负责对板级硬件进行初始化、硬件中断处理、向manager注册input设备等;
20-   器件驱动层:通过适配平台驱动预留的差异化接口,实现器件驱动开发量最小化;
21
22此外,Input模型预先实现了数据通道以及设备配置信息解析等函数。
23
24关于Input模型的详细介绍请参考《[Touchscreen开发概述](../driver/driver-peripherals-touch-des.md#概述)》。
25
26## 环境搭建<a name="section661075474418"></a>
27
28环境准备具体操作请参考[快速入门环境搭建章节](../quick-start/quickstart-overview.md)。
29
30>![](../public_sys-resources/icon-notice.gif) **须知:**
31>本示例针对OpenHarmony轻量系统、小型系统、标准系统都适用,本文以标准系统为例。其他系统的开发者可参考对应系统的指导文档进行环境搭建。
32
33## TouchScreen器件驱动开发<a name="section15233162984520"></a>
34
35基于Input模型适配一款触摸屏IC需要完成的具体工作见下。
36
37### 配置设备描述信息<a name="section16761205604515"></a>
38
39驱动注册到HDF框架所需要的设备驱动描述信息,如驱动是否加载以及加载次序等。
40
41配置文件路径:./drivers/adapter/khdf/linux/hcs/device\_info/device\_info.hcs
42
43device\_info.hcs中的信息主要提供给HDF框架使用,包含了Input模型各层驱动注册到HDF框架所必需的信息,开发者无特殊场景需求无需改动。各驱动层私有配置信息通过“deviceMatchAttr”字段与input\_config.hcs中的“match\_attr”相关内容进行匹配。
44
45配置文件中与input模块相关的内容如下所示,相关字段的详细含义可以参考《[驱动配置](../driver/driver-hdf-development.md)》:
46
47```
48input :: host {
49            hostName = "input_host";
50            priority = 100;
51            device_input_manager :: device {              // Input管理层设备描述信息
52                device0 :: deviceNode {
53                    policy = 2;                           // 向内核用户态均发布服务
54                    priority = 100;                       // input管理层驱动优先级默认为100
55                    preload = 0;                          // 加载该驱动
56                    permission = 0660;                    // 驱动创建设备节点权限
57                    moduleName = "HDF_INPUT_MANAGER";     // 与驱动入口的moduleName匹配
58                    serviceName = "hdf_input_host";       // HDF框架生成的节点名
59                    deviceMatchAttr = "";                 // manager目前不需要私有配置,因此为空
60                }
61            }
62
63            device_hdf_touch :: device {                  // Input公共驱动层设备描述信息
64                device0 :: deviceNode {
65                    policy = 2;                           // 向内核用户态均发布服务
66                    priority = 120;                       // input公共驱动优先级默认为120
67                    preload = 0;                          // 加载该驱动
68                    permission = 0660;                    // 驱动创建设备节点权限
69                    moduleName = "HDF_TOUCH";             // 与驱动入口的moduleName匹配
70                    serviceName = "hdf_input_event1";     // HDF框架生成的节点名
71                    deviceMatchAttr = "touch_device1";    // 与私有配置信息中的“match_attr”字段保持一致
72                }
73            }
74
75            device_touch_chip :: device {                 // Input器件驱动层信息
76                device0 :: deviceNode {
77                    policy = 0;                           // 向内核用户态均不发布服务
78                    priority = 130;                       // input器件驱动优先级默认为130
79                    preload = 0;                          // 加载该驱动
80                    permission = 0660;                    // 驱动创建设备节点权限
81                    moduleName = "HDF_TOUCH_GT911";       // 与驱动入口的moduleName匹配
82                    serviceName = "hdf_touch_gt911_service";// HDF框架生成的节点名
83                    deviceMatchAttr = "zsj_gt911_5p5";    // 与私有配置信息中的“match_attr”字段保持一致
84                }
85            }
86  }
87```
88
89该配置文件中需要重点关注的字段有:
90
91“priority”决定驱动加载顺序;
92
93“preload”决定驱动是否加载;
94
95“moduleName”需要与驱动注册入口处的“moduleName”字段保持一致;
96
97“serviceName”HDF框架依据该字段创建节点名;
98
99“deviceMatchAttr”需要与私有配置信息中的“match\_attr”字段保持一致。
100
101通过配置设备描述信息,使得HDF框架通过moduleName与注册至驱动入口的代码相匹配,保证了驱动的正常加载,通过priority字段保证了各驱动的加载顺序。
102
103### 配置Touchscreen器件信息<a name="section156331030144617"></a>
104
105器件私有信息包括上下电时序等,平台硬件信息包括器件连接主板的GPIO端口信息等。
106
107配置文件路径:./drivers/adapter/khdf/linux/hcs/input/input\_config.hcs
108
109input\_config.hcs中的信息由驱动代码进行读取解析,主要由公共驱动层的私有配置信息及器件驱动层的私有配置信息组成。文件中的配置包含板级硬件信息及器件私有配置信息,实际业务开发时,可根据具体需求增删及修改对应内容。
110
111```
112root {
113    input_config {
114        touchConfig {
115            touch0 {                                                 // 第一款触摸屏
116                boardConfig {                                        // 板级硬件信息
117                    match_attr = "touch_device1";                    // 与设备描述配置信息中公共驱动层私有配置信息的“match_attr”字段保持一致
118                    inputAttr {
119                        /* 0:touch 1:key 2:keyboard 3:mouse 4:button 5:crown 6:encoder */
120                        inputType = 0;                               // input类型为touch
121                        solutionX = 480;                             // 分辨率X信息
122                        solutionY = 960;                             // 分辨率Y信息
123                        devName = "main_touch";                      // 设备名称
124                    }
125                    busConfig {
126                        /* 0:i2c 1:spi */
127                        busType = 0;                                 // GT911采用I2C通信
128                        busNum = 6;                                  // 与主机芯片第6路I2C通信
129                        clkGpio = 86;                                // 主机芯片SCL管脚
130                        dataGpio = 87;                               // 主机芯片SDA管脚
131                        i2cClkIomux = [0x114f0048, 0x403];           // SCL管脚配置信息
132                        i2cDataIomux = [0x114f004c, 0x403];          // SDA管脚配置信息
133                    }
134                    pinConfig {
135                        rstGpio = 3;                                 // 复位管脚连接主机芯片的3号管脚
136                        intGpio = 4;                                 // 中断管脚连接主机芯片的4号管脚
137                        rstRegCfg = [0x112f0094, 0x400];             // 复位管脚配置信息
138                        intRegCfg = [0x112f0098, 0x400];             // 中断管脚配置信息
139                    }
140                    powerConfig {
141                        /* 0:unused 1:ldo 2:gpio 3:pmic */
142                        vccType = 2;                                  // GPIO供电
143                        vccNum = 20;                                  // gpio20
144                        vccValue = 1800;                              // 电压幅值为1800mV
145                        vciType = 1;                                  // LDO供电
146                        vciNum = 12;                                  // ldo12
147                        vciValue = 3300;                              // 电压幅值为3300mV
148                    }
149
150                    featureConfig {
151                        capacitanceTest = 0;                          // 容值测试
152                        gestureMode = 0;                              // 手势模式
153                        gloverMode = 0;                               // 手套模式
154                        coverMode = 0;                                // 皮套模式
155                        chargerMode = 0;                              // 充电模式
156                        knuckleMode = 0;                              // 指关节模式
157                    }
158                }
159                chipConfig {                                          // 器件私有信息配置
160                    template touchChip {                              // 模板
161                        match_attr = "";
162                        chipName = "gt911";                           // 触摸屏IC型号
163                        vendorName = "zsj";                           // 供应商
164                        chipInfo = "AAAA11222";                       // 1~4字符代表产品名,5~6字符代表IC型号,7~9字符代表模型型号
165                        busType = 0;                                  // 0代表I2C,1代表SPI
166                        deviceAddr = 0x5D;                            // 器件IC通信地址
167                        irqFlag = 2;                                  // 1代表上升沿触发,2代表下降沿触发,4代表高电平触发,8代表低电平触发
168                        maxSpeed = 400;                               // 最大通信速率为400Hz
169                        chipVersion = 0;                              // 触摸屏IC版本号
170                        powerSequence {
171                             /* 上电时序的配置含义说明:
172                               [类型, 状态, 方向 , 延时]
173                               <type> 0代表空,1代表vcc电源(1.8V),2代表VCI电源(3.3V),3代表复位管脚,4代表中断管脚
174                               <status> 0代表下电或拉低,1代表上电或拉高,2代表无操作
175                               <dir> 0代表输入方向,1代表输出方向,2代表无操作
176                               <delay> 代表延时多少毫秒, 例如20代表延时20ms
177                             */
178                            powerOnSeq = [4, 0, 1, 0,                 // 中断管脚配置为输出,且进行拉低
179                                         3, 0, 1, 10,                 // 复位管脚配置为输出,且进行拉低,延时10ms
180                                         3, 1, 2, 60,                 // 复位管脚无操作,且进行拉高,延时60ms
181                                         4, 2, 0, 0];                 // 中断管脚配置为输入
182                            suspendSeq = [3, 0, 2, 10];               // 复位管脚无操作,且进行拉低,延时10ms
183                            resumeSeq = [3, 1, 2, 10];                // 复位管脚无操作,且进行拉高,延时10ms
184                            powerOffSeq = [3, 0, 2, 10,               // 复位管脚无操作,且进行拉低,延时10ms
185                                           1, 0, 2, 20];              // 电源正极管脚无操作,且进行拉低,延时20ms
186                        }
187                    }
188
189                    chip0 :: touchChip {
190                        match_attr = "zsj_gt911_5p5";                 // 与设备描述配置信息中器件私有配置信息的“match_attr”字段保持一致
191                        chipInfo = "ZIDN45100";                       // 产品名+模组编号+芯片编号的组合信息 用于给用户态区分当前器件
192                        chipVersion = 0;                              // IC型号的版本
193                    }
194                }
195            }
196        }
197    }
198}
199```
200
201示例中“touchConfig”包含了“touch0”,"touch0"包含了“boardConfig”与“chipConfig”;“boardConfig”字段包含了Hi3516DV300板级硬件信息,“chipConfig”包含了触摸屏器件的私有信息,如果需要替换触摸屏器件,重新配置“chipConfig”对应的字段信息即可。同时产品可以配置多款触摸屏,示例中用“touch0”代表了套件中默认的触摸屏的硬件接口以及器件的配置信息,如产品需要配置副屏,可在与“touch0”并列的位置配置“touch1”的信息。
202
203### 适配器件私有驱动<a name="section17127331595"></a>
204
205Input模型对Input设备开发流程进行了抽象,开发者只需要适配器件驱动层,无需改动管理驱动层以及公共驱动层。
206
207Input模型由三层驱动组成,开发者适配一款全新触摸屏驱动只需要适配器件驱动层即可,重点实现差异化接口,本小节以代码示例的形式展示开发者需要重点完成的工作。
208
2091.  触摸屏器件差异化接口适配
210
211    示例代码路径:./drivers/framework/model/input/driver/touchscreen/touch\_gt911.c
212
213    ```
214    static struct TouchChipOps g_gt911ChipOps = {                                  // 器件IC接口
215        .Init = ChipInit,                                                          // 初始化
216        .Detect = ChipDetect,                                                      // 器件检测
217        .Resume = ChipResume,                                                      // 唤醒
218        .Suspend = ChipSuspend,                                                    // 休眠
219        .DataHandle = ChipDataHandle,                                              // 器件数据读取
220        .UpdateFirmware = UpdateFirmware,                                          // 固件升级
221    };
222
223    /* 不同触摸屏厂家使用的IC不一样,对应的寄存器操作也不一样,因此器件驱动层代码重点适配差异化接口部分,如下示例代码展示了GT911的数据解析*/
224
225    static int32_t ChipDataHandle(ChipDevice *device)
226    {
227        ...
228        /*  GT911获取坐标之前需先读取状态寄存器  */
229        reg[0] = (GT_BUF_STATE_ADDR >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK;
230        reg[1] = GT_BUF_STATE_ADDR & ONE_BYTE_MASK;
231        ret = InputI2cRead(i2cClient, reg, GT_ADDR_LEN, &touchStatus, 1);
232        if (ret < 0 || touchStatus == GT_EVENT_INVALID) {
233            return HDF_FAILURE;
234        }
235        ...
236        /* 根据状态寄存器的值读取数据寄存器数据 */
237        reg[0] = (GT_X_LOW_BYTE_BASE >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK;
238        reg[1] = GT_X_LOW_BYTE_BASE & ONE_BYTE_MASK;
239        pointNum = touchStatus & GT_FINGER_NUM_MASK;
240        if (pointNum == 0 || pointNum > MAX_SUPPORT_POINT) {
241            HDF_LOGE("%s: pointNum is invalid, %u", __func__, pointNum);
242            (void)ChipCleanBuffer(i2cClient);
243            OsalMutexUnlock(&device->driver->mutex);
244            return HDF_FAILURE;
245        }
246        frame->realPointNum = pointNum;
247        frame->definedEvent = TOUCH_DOWN;
248        (void)InputI2cRead(i2cClient, reg, GT_ADDR_LEN, buf, GT_POINT_SIZE * pointNum);
249        /* 对获取的数据进行解析 */
250        ParsePointData(device, frame, buf, pointNum);
251        ...
252    }
253    static void ParsePointData(ChipDevice *device, FrameData *frame, uint8_t *buf, uint8_t pointNum)
254    {
255        ...
256        /* 每个坐标值由两个字节组成,对获取的单字节数据进行拼接得到最终的坐标值 */
257        for (i = 0; i < pointNum; i++) {
258                frame->fingers[i].trackId = buf[GT_POINT_SIZE * i + GT_TRACK_ID];
259                frame->fingers[i].y = (buf[GT_POINT_SIZE * i + GT_X_LOW] & ONE_BYTE_MASK) |
260                                      ((buf[GT_POINT_SIZE * i + GT_X_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET);
261                frame->fingers[i].x = (buf[GT_POINT_SIZE * i + GT_Y_LOW] & ONE_BYTE_MASK) |
262                                      ((buf[GT_POINT_SIZE * i + GT_Y_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET);
263                /* 对解析出来的坐标值进行打印 */
264                HDF_LOGD("%s: x = %d, y = %d", __func__, frame->fingers[i].x, frame->fingers[i].y);
265         }
266    }
267    ```
268
2692.  器件层驱动初始化及注册驱动至HDF框架
270
271    示例代码路径:./drivers/framework/model/input/driver/touchscreen/touch\_gt911.c
272
273    ```
274    static int32_t HdfGoodixChipInit(struct HdfDeviceObject *device)
275    {
276        ...
277        /* 器件配置结构体内存申请、配置信息解析及挂载 */
278        chipCfg = ChipConfigInstance(device);
279        ...
280        /* 器件实例化 */
281        chipDev = ChipDeviceInstance();
282        ...
283        /* 器件信息挂载及器件私有操作挂载 */
284        chipDev->chipCfg = chipCfg;
285        chipDev->ops = &g_gt911ChipOps;
286        ...
287        /* 注册器件驱动至平台驱动 */
288        RegisterChipDevice(chipDev);
289        ...
290    }
291    struct HdfDriverEntry g_touchGoodixChipEntry = {
292        .moduleVersion = 1,
293        .moduleName = "HDF_TOUCH_GT911",   // 该moduleName与device_info.hcs文件中器件驱动层的moduleName信息相匹配
294        .Init = HdfGoodixChipInit,         // 器件驱动初始化函数
295    };
296    HDF_INIT(g_touchGoodixChipEntry);      // 注册器件驱动至HDF框架
297    ```
298
299    器件私有驱动层主要实现了各器件厂商差异较大的部分,如器件休眠唤醒、数据解析以及固件升级等。
300
301    至此,基于HDF框架及Input模型的触摸屏驱动适配完成。
302
303
304## 编译及烧录<a name="section16465031164711"></a>
305
3061.  编辑Makefile文件,添加本示例中的内容:
307
308    文件路径:./drivers/adapter/khdf/linux/model/input/Makefile
309
310    添加内容如下:
311
312    ```
313    obj-$(CONFIG_DRIVERS_HDF_TP_5P5_GT911) += \
314                  $(INPUT_ROOT_DIR)/touchscreen/touch_gt911.o
315    ```
316
317    其中touch\_gt911.o为本示例中追加的内容。
318
3192.  具体编译及烧录操作请参考[标准系统快速入门的编译及烧录章节](../quick-start/quickstart-overview.md)。
320
321## 调试验证<a name="section62577313482"></a>
322
323如下所示为开机启动日志部分截取
324
325```
326[I/HDF_INPUT_DRV] HdfInputManagerInit: enter                            // 管理驱动层初始化
327[I/HDF_INPUT_DRV] HdfInputManagerInit: exit succ                        // 初始化成功
328[I/osal_cdev] add cdev hdf_input_host success
329[I/HDF_LOG_TAG] HdfTouchDriverProbe: enter                              // 公共驱动层初始化
330[I/HDF_LOG_TAG] HdfTouchDriverProbe: main_touch exit succ               // 初始化成功
331[I/osal_cdev] add cdev hdf_input_event1 success
332[I/HDF_INPUT_DRV] HdfGoodixChipInit: enter                              // 器件驱动层初始化
333[I/HDF_INPUT_DRV] ChipDetect: IC FW version is 0x1060
334[I/HDF_INPUT_DRV] Product_ID: 911_1060, x_sol = 960, y_sol = 480
335[I/HDF_LOG_TAG] ChipDriverInit: chipDetect succ, ret = 0
336[I/HDF_LOG_TAG] InputDeviceInstance: inputDev->devName = main_touch
337[I/HDF_INPUT_DRV] HdfGoodixChipInit: exit succ, chipName = gt911        // 初始化成功
338```
339
340## Input模型工作流程解析<a name="section1578569154917"></a>
341
342为了让开发者更清晰的了解Input模型工作流程,本节将对input模型加载的关键流程代码进行说明。
343
344>![](../public_sys-resources/icon-notice.gif) **须知:**
345>本章节为Input模型工作流程说明,开发者无需进行开发。
346
347### 私有配置信息解析<a name="section1310113815495"></a>
348
349示例代码路径:./drivers/framework/model/input/driver/input\_config\_parser.c
350
351根据OSAL提供的配置解析函数,可以将hcs文件中各字段含义进行解析,具体请参考input\_config\_parser.c中各函数的实现。如果提供的模板不能满足需求,在hcs文件中添加相应信息后,需要根据添加的字段开发相应的解析函数。
352
353```
354static int32_t ParseAttr(struct DeviceResourceIface *parser, const struct DeviceResourceNode *attrNode, BoardAttrCfg *attr)
355{
356    int32_t ret;
357    ret = parser->GetUint8(attrNode, "inputType", &attr->devType, 0);     // 获取inputType字段信息,保存在BoardAttrCfg结构体中
358    CHECK_PARSER_RET(ret, "GetUint8");
359    ...
360    return HDF_SUCCESS;
361}
362```
363
364### 管理驱动层初始化及注册驱动至HDF框架<a name="section614512119500"></a>
365
366示例代码路径:./drivers/framework/model/input/driver/hdf\_input\_device\_manager.c
367
368```
369static int32_t HdfInputManagerInit(struct HdfDeviceObject *device)
370{
371    /* 分配内存给manager,manager中将存放所有input设备 */
372    g_inputManager = InputManagerInstance();
373    ...
374}
375struct HdfDriverEntry g_hdfInputEntry = {
376    .moduleVersion = 1,
377    .moduleName = "HDF_INPUT_MANAGER",
378    .Bind = HdfInputManagerBind,
379    .Init = HdfInputManagerInit,
380    .Release = HdfInputManagerRelease,
381};
382
383HDF_INIT(g_hdfInputEntry);                                               //驱动注册入口
384```
385
386### 公共驱动层初始化及注册驱动至HDF框架<a name="section16194201755019"></a>
387
388示例代码路径:./drivers/framework/model/input/driver/hdf\_touch.c
389
390```
391static int32_t HdfTouchDriverProbe(struct HdfDeviceObject *device)
392{
393    ...
394    /* 板级信息结构体内存申请及hcs配置信息解析 */
395    boardCfg = BoardConfigInstance(device);
396    ...
397    /* 公共驱动结构体内存申请 */
398    touchDriver = TouchDriverInstance();
399    ...
400    /* 依据解析出的板级信息进行公共资源初始化,如IIC初始化 */
401    ret = TouchDriverInit(touchDriver, boardCfg);
402    if (ret == HDF_SUCCESS) {
403        ...
404        /* 添加驱动至公共驱动层驱动管理链表,当设备与驱动进行绑定时使用该链表进行查询 */
405        AddTouchDriver(touchDriver);
406        ...
407    }
408    ...
409}
410struct HdfDriverEntry g_hdfTouchEntry = {
411    .moduleVersion = 1,
412    .moduleName = "HDF_TOUCH",
413    .Bind = HdfTouchDriverBind,
414    .Init = HdfTouchDriverProbe,
415    .Release = HdfTouchDriverRelease,
416};
417
418HDF_INIT(g_hdfTouchEntry);                                              //驱动注册入口
419```
420
421### 器件驱动层初始化及注册驱动至HDF框架<a name="section1090743312505"></a>
422
423具体请参考[适配器件私有驱动](#section17127331595)器件层驱动初始化及注册驱动至HDF框架部分。
424
425### 具体调用逻辑串联函数<a name="section81801147529"></a>
426
427Input模型管理层驱动init函数初始化了设备管理链表,公共驱动层初始化函数完成了相关结构体的内存申请。器件驱动相关信息通过RegisterChipDevice函数对公共驱动层相关结构体进行信息填充,同时完成了相关硬件信息的初始化(如中断注册等),绑定设备与驱动组成inputDev通过RegisterInputDevice函数向驱动管理层进行注册,在RegisterInputDevice函数中主要实现了将inputDev向设备管理链表的添加等功能。如下所示为两个函数的实现部分:
428
429```
430//函数具体实现代码位置 :./drivers/framework/model/input/driver/hdf_touch.c
431int32_t RegisterChipDevice(ChipDevice *chipDev)
432{
433    ...
434    /* 绑定设备与驱动,从而通过InputDeviceInstance函数创建inputDev */
435    DeviceBindDriver(chipDev);
436    ...
437    /* 主要包含器件中断注册及中断处理函数,中断处理函数中有数据上报用户态的数据通道 */
438    ChipDriverInit(chipDev);
439    ...
440    /* 申请内存实例化InputDev */
441    inputDev = InputDeviceInstance(chipDev);
442    ...
443    /* 将InputDev设备注册至input驱动管理层 */
444    RegisterInputDevice(inputDev);
445    ...
446}
447
448//函数具体实现代码位置 :./drivers/framework/model/input/driver/hdf_input_device_manager.c
449int32_t RegisterInputDevice(InputDevice *inputDev)
450{
451    ...
452    /* 申请ID,该ID对于不同input设备唯一 */
453    ret = AllocDeviceID(inputDev);
454    ...
455    /* 该函数包含了对hid类设备的特殊处理,对于触摸屏驱动,该函数无实质操作; */
456    CreateDeviceNode(inputDev);
457    /* 内核态数据传送至用户态需使用IOService能力,需要申请buffer */
458    AllocPackageBuffer(inputDev);
459    /* 将input设备添加进设备全局管理链表 */
460    AddInputDevice(inputDev);
461    ...
462}
463```
464
465