1# OpenHarmony HDF驱动编程规范 2 3## 前言 4 5### 目的 6 7OpenHarmony的目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展。具有“硬件互助,资源共享”、“一次开发,多端部署”、“统一OS,弹性部署”的技术特性。 8 9HDF(Hardware Driver Foundation)驱动框架,为开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。 10 11因此,对基于HDF实现的OpenHarmony驱动代码需要有一定的编程规约,以满足驱动代码的“一次开发,多端部署”技术特性。本文以此为初衷,结合OpenHarmony和HDF的特点,拟定了相关编程规约,用于指导驱动代码的开发编码,提升代码的规范性及可移植性,供开发者参考。 12 13## 编程规范 14 15### 总则 16 17#### 【规则】OpenHarmony的驱动程序,应当使用HDF框架提供的能力实现 18 19【说明】HDF驱动框架提供了驱动加载、驱动服务管理和驱动消息机制,同时还提供了操作系统抽象层(OSAL, Operating System Abstraction Layer)和平台抽象层(PAL, Platform Abstraction Layer)来保证驱动程序的跨系统跨平台部署的特性。除此之外,HDF提供了驱动模型的抽象、公共工具、外围器件框架等能力。开发者应该基于HDF提供的这些能力开发驱动,从而保证驱动程序可以在各种形态的OpenHarmony上进行部署。 20 21#### 【规则】开发者应当遵循此规范要求,开发能够同时满足内核态和用户态的驱动 22 23【说明】内核态驱动与用户态驱动天然存在着差异,两种形态适用的场景也不尽相同。开发者在业务设计和开发的时候应当遵循此规范,使用HDF提供的OSAL、PAL等特性来屏蔽形态的差异,来保证开发的驱动同时满足内核态和用户态。 24 25#### 【建议】使用HDF框架时,编译脚本应当包含drivers/framework/include目录,而不是子模块目录 26 27【说明】drivers/framework/include目录是HDF对外暴露的头文件根目录,此目录下面按照功能划分为核心框架、OSAL和PAL等多个子模块目录。在使用对应头文件时,建议编译脚本包含到drivers/framework/include目录,这样在代码中进行引用时,可以避免重复包含,也便于区分对应子模块,达到驱动范围内的统一。 28 29【样例】 30 31```gn 32config("xxxx_private_config") { 33 include_dirs = [ 34 "//drivers/framework/include", 35 "//drivers/framework/include/core", # 不建议 36 ] 37} 38``` 39 40```c 41#include <core/hdf_device_desc.h> 42#include <hdf_device_desc.h> // 不建议 43``` 44 45### HDF核心框架 46 47#### 【规则】应当按照驱动入口对象HdfDriverEntry中的职责定义来实现Bind、Init和Release方法,避免职责不单一引入问题 48 49【说明】HdfDriverEntry对象是HDF驱动的入口,其中的三个方法指针均有各自的职责,开发者需按照方法职责来实现对应函数。 50 51```c 52struct HdfDriverEntry g_sampleDriverEntry = { 53 .moduleVersion = 1, 54 .moduleName = "sample_driver", 55 .Bind = SampleDriverBind, // 职责:绑定驱动对外提供的服务接口到HDF 56 .Init = SampleDriverInit, // 职责:初始化驱动自身的业务 57 .Release = SampleDriverRelease, // 职责:释放驱动资源,发生异常时也会调用 58}; 59 60HDF_INIT(g_sampleDriverEntry); 61``` 62 63#### 【规则】驱动服务的结构定义,首个成员必须是IDeviceIoService类型 64 65【说明】HDF框架内部实现约束,驱动定义的服务接口,首个成员必须是IDeviceIoService类型。 66 67【样例】 68 69```c 70struct ISampleDriverService { 71 struct IDeviceIoService ioService; // 首个成员必须是IDeviceIoService类型 72 int32_t (*FunctionA)(void); // 驱动的第一个服务接口 73 int32_t (*FunctionB)(uint32_t inputCode); // 驱动的第二个服务接口,可以依次往下累加 74}; 75``` 76 77【样例】 78 79```c 80struct ISampleDriverService { 81 struct IDeviceIoService ioService; // 首个成员必须是IDeviceIoService类型 82 void *instance; // 也可以封装服务实例,在实例中提供服务接口 83}; 84``` 85 86#### 【规则】在HdfDriverEntry的Bind方法中,必须完成全部驱动服务接口的绑定,禁止将服务接口未定义或定义为空 87 88【说明】驱动定义的服务接口,均是对外暴露的,如果未定义或定义为空,可能会导致外部调用时产生异常,从而降低驱动的可靠性。 89 90【样例】 91 92```c 93int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject) 94{ 95 static struct ISampleDriverService sampleDriver = { 96 .FunctionA = SampleDriverServiceA, 97 .FunctionB = NULL, // 禁止定义为空 98 }; 99 // 将ioService与HDF创建的设备对象进行绑定 100 deviceObject->service = &sampleDriver.ioService; 101 return HDF_SUCCESS; 102} 103``` 104 105#### 【建议】在HdfDriverEntry的Init方法中,应当调用HdfDeviceSetClass接口,对驱动的类型进行定义 106 107【说明】驱动的类型可以用于归类当前设备的驱动程序,也可以用来查询当前设备的驱动能力。为了便于后续驱动的统一管理,建议通过HdfDeviceSetClass接口来设置当前驱动的类型。 108 109【样例】 110 111```c 112int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject) 113{ 114 // 设置驱动的类型为DISPLAY 115 if (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_DISPLAY)) { 116 HDF_LOGE("HdfDeviceSetClass failed"); 117 return HDF_FAILURE; 118 } 119 return HDF_SUCCESS; 120} 121``` 122 123### HCS配置规范 124 125HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。 126 127驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息。 128 129**设备描述信息** 130 131HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述。 132 133#### 【规则】在进行驱动设备配置之前,应当明确驱动所属的硬件和部署形态,规划需要配置的目录和文件 134 135【说明】在OpenHarmony源码的vendor目录下,按照芯片厂商、开发板、配置的目录进行规划,HDF驱动的配置位于hdf_config目录下。根据硬件规格,此hdf_config目录下存放内核态配置信息或者分别内核态和用户态的配置信息。开发者应当根据驱动所属的硬件和部署形态,确定在哪一个目录下进行配置。 136 137【样例】 138 139```bash 140$openharmony_src_root/vendor/hisilicon/hispark_taurus/hdf_config # 内核态配置文件目录,无用户态 141 142$openharmony_src_root/vendor/hisilicon/hispark_taurus_standard/hdf_config/khdf # 内核态配置文件目录 143$openharmony_src_root/vendor/hisilicon/hispark_taurus_standard/hdf_config/uhdf # 用户态配置文件目录 144$openharmony_src_root/vendor/hisilicon/hispark_taurus_standard/hdf_config/khdf/device_info/device_info.hcs # 内核态驱动设备描述配置文件 145$openharmony_src_root/vendor/hisilicon/hispark_taurus_standard/hdf_config/khdf/lcd/lcd_config.hcs # 内核态驱动私有配置文件 146``` 147 148#### 【规则】驱动设备在配置时,应当充分使用已有的配置信息,继承已有的配置模板 149 150【说明】在HDF框架定义的device_info.hcs配置文件中,已经配置好了host、device和deviceNode的模板,开发者在配置驱动设备时,应该充分利用已有配置信息和HCS的继承特性,减少重复的配置工作量。 151 152【样例】 153 154``` 155root { 156 device_info { 157 match_attr = "hdf_manager"; 158 template host { // host模板 159 hostName = ""; 160 priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序 161 template device { // device模板 162 template deviceNode { // deviceNode模板 163 policy = 0; // policy字段是驱动服务发布的策略 164 priority = 100; // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序 165 preload = 0; // 驱动按需加载字段 166 permission = 0664; // 驱动创建设备节点权限 167 moduleName = ""; 168 serviceName = ""; 169 deviceMatchAttr = ""; 170 } 171 } 172 } 173 // 继承模板的节点如果使用模板中的默认值,则节点字段可以缺省 174 sample_host :: host { // sample_host继承了host模板 175 hostName = "host0"; // host名称,host节点是用来存放某一类驱动的容器 176 device_sample :: device { // device_sample继承了device模板 177 device0 :: deviceNode { // device0继承了deviceNode模板 178 policy = 1; // 覆写了模板中的policy值 179 moduleName = "sample_driver"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 180 serviceName = "sample_service"; // 驱动对外发布服务的名称,必须唯一 181 deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 182 } 183 } 184 } 185 } 186} 187``` 188 189#### 【规则】驱动模型的设计和归类应当满足业务需要和既定类型,禁止重复配置Host和Device 190 191【说明】HDF框架将一类设备驱动放在同一个Host里面,开发者也可以将Host中的驱动功能分层独立开发和部署,支持一个驱动多个Node,HDF驱动模型如下图所示: 192 193![HDF驱动模型.png](../device-dev/driver/figures/HDF驱动模型.png) 194 195开发者应当将同一类的设备放在同一个Host里面,在新增设备时,检查是否已经存在同类型的Host。如果已存在Host,则将Device配置在此Host中,禁止重复配置Host。一个驱动设备应该只属于一类驱动类型,因此也禁止将同一个Device配置在不同Host当中。 196 197#### 【规则】驱动服务必须按照业务规则设置对外发布的策略,禁止设置不必要的发布策略 198 199【说明】驱动服务是HDF驱动设备对外提供能力的对象,由HDF框架统一管理。HDF框架定义了驱动对外发布服务的策略,是由配置文件中的policy字段来控制,policy字段的取值范围以及含义如下: 200 201```c 202typedef enum { 203 /* 驱动不提供服务 */ 204 SERVICE_POLICY_NONE = 0, 205 /* 驱动对内核态发布服务 */ 206 SERVICE_POLICY_PUBLIC = 1, 207 /* 驱动对内核态和用户态都发布服务 */ 208 SERVICE_POLICY_CAPACITY = 2, 209 /* 驱动服务不对外发布服务,但可以被订阅 */ 210 SERVICE_POLICY_FRIENDLY = 3, 211 /* 驱动私有服务不对外发布服务,也不能被订阅 */ 212 SERVICE_POLICY_PRIVATE = 4, 213 /* 错误的服务策略 */ 214 SERVICE_POLICY_INVALID 215} ServicePolicy; 216``` 217 218因此,驱动服务应该按照业务规则来设置发布策略,禁止设置不必要的发布策略,如内核态驱动设置用户态的发布策略。 219 220【样例】 221 222``` 223root { 224 device_info { 225 sample_host { 226 sample_device { 227 device0 { 228 policy = 1; // 驱动对内核态发布服务 229 ... 230 } 231 } 232 } 233 } 234} 235``` 236 237#### 【规则】驱动创建设备节点权限必须与驱动的发布规则互相匹配 238 239【说明】在HDF框架定义的device_info.hcs配置文件中,permission为驱动创建的设备节点权限字段。该字段的取值使用Unix文件权限的八进制数字模式来表示,长度为4位,例如0644。permission字段仅在驱动服务对用户态发布服务时(即policy = 2)才会生效。 240 241开发者应当保证驱动服务的发布策略与设备节点的权限互相匹配,否则可能会导致驱动服务无法访问或设备节点的权限被放大。 242 243【样例】 244 245``` 246root { 247 device_info { 248 sample_host { 249 sample_device { 250 device0 { 251 policy = 2; // 驱动对内核态和用户态都发布服务 252 permission = 0640; // 建议值 253 ... 254 } 255 } 256 } 257 } 258} 259``` 260 261【反例】 262 263``` 264root { 265 device_info { 266 sample_host { 267 sample_device { 268 device0 { 269 policy = 2; // 驱动对内核态和用户态都发布服务 270 permission = 0777; // 权限过大 271 ... 272 } 273 } 274 } 275 } 276} 277``` 278 279【反例】 280 281``` 282root { 283 device_info { 284 sample_host { 285 sample_device { 286 device0 { 287 policy = 1; // 驱动对内核态发布服务,不会创建设备节点 288 permission = 0640; // 冗余配置 289 ... 290 } 291 } 292 } 293 } 294} 295``` 296 297#### 【规则】应当根据业务要求配置是否按需加载 298 299【说明】在HDF框架定义的device_info.hcs配置文件中,preload为驱动按需加载字段,取值的范围见如下枚举: 300 301```c 302typedef enum { 303 /* 系统启动时默认加载 */ 304 DEVICE_PRELOAD_ENABLE = 0, 305 /* 当系统支持快启时,则在快启完成后再加载;如果系统不支持快启,与DEVICE_PRELOAD_ENABLE含义相同 */ 306 DEVICE_PRELOAD_ENABLE_STEP2, 307 /* 系统启动时默认不加载,当使用时HDF框架会尝试动态加载 */ 308 DEVICE_PRELOAD_DISABLE, 309 /* 无效值 */ 310 DEVICE_PRELOAD_INVALID 311} DevicePreload; 312``` 313 314开发者应当根据驱动的业务要求,将preload字段配置为相应的值,从而HDF框架可以按照preload规则进行驱动的加载。 315 316【样例】 317 318``` 319root { 320 device_info { 321 sample_host { 322 sample_device { 323 device0 { 324 preload = 2; // 使用时按需加载 325 ... 326 } 327 } 328 } 329 } 330} 331``` 332 333#### 【建议】当preload字段配置为默认加载时,应当根据业务要求配置按序加载的优先级 334 335【说明】在HDF框架定义的device_info.hcs配置文件中,priority字段(取值范围为整数0到200)是用来表示Host和驱动的优先级。不同的Host内的驱动,Host的priority值越小,驱动加载优先级越高;同一个Host内驱动的priority值越小,加载优先级越高。priority字段的默认值为100,当未配置或字段值相同时,HDF框架将不保证驱动的加载顺序。开发者应当根据业务场景的要求,配置priority字段,保证各个驱动的启动顺序。 336 337【样例】 338 339``` 340root { 341 device_info { 342 sample_host0 { 343 priority = 100; 344 sample_device { 345 device0 { 346 preload = 0; // 默认加载 347 priority = 100; // HDF保证在device1之前加载 348 ... 349 } 350 device1 { 351 preload = 0; // 默认加载 352 priority = 200; // HDF保证在device0之后加载 353 ... 354 } 355 } 356 } 357 sample_host1 { 358 priority = 100; // 由于与sample_host0的优先级相同,HDF将不保证加载顺序 359 ... 360 } 361 } 362} 363``` 364 365**驱动私有配置信息** 366 367如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject中的property里面,通过Bind和Init传递给驱动。 368 369#### 【规则】驱动私有配置文件应当按照器件类型或者模块进行目录划分,并放置在相应的目录下 370 371【说明】开发者应当对驱动的私有配置文件进行合理的目录规划,禁止将私有配置文件放置在配置的根目录下。 372 373【样例】 374 375```bash 376$openharmony_src_root/vendor/hisilicon/hispark_taurus_standard/hdf_config/khdf/sample/sample_config.hcs # 正确,将私有配置文件放置在了sample目录下 377 378$openharmony_src_root/vendor/hisilicon/hispark_taurus_standard/hdf_config/khdf/sample_config.hcs # 错误,将私有配置文件放置在了配置根目录下 379``` 380 381#### 【规则】应当将驱动私有配置文件包含到hdf_config配置目录下的hdf.hcs文件中 382 383【说明】hdf.hcs文件是配置信息的汇总文件,在HDF编译和运行时,将会解析此文件中的内容,加载驱动的私有配置信息到驱动的设备节点中。开发者应当保证hdf.hcs文件中包含了驱动的私有配置文件,从而保证驱动能够正确初始化。 384 385【样例】 386 387```c 388#include "device_info/device_info.hcs" 389#include "sample/sample_config.hcs" // 包含驱动私有配置文件 390 391root { 392 module = "hisilicon,hi35xx_chip"; 393} 394``` 395 396#### 【规则】驱动私有配置信息中的matchAttr字段值,必须与device_info.hcs中配置的deviceMatchAttr字段值一致 397 398【说明】HDF框架会通过match_attr字段的值,来与驱动设备进行关联。如果配置错误,将导致私有配置信息无法获取。 399 400【样例】 401 402``` 403root { 404 sample_config { 405 ... 406 match_attr = "sample_config"; // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致 407 } 408} 409``` 410 411#### 【规则】驱动私有配置信息中的字段名,使用下划线命名法 412 413【说明】由于C/C++语言编程指导的命名规则要求,驱动的私有配置信息中的字段名,应当使用下划线命名法。这样,在实现代码中对私有配置数据结构进行定义时,可以满足命名规则,也便于代码和配置文件的统一管理。 414 415【样例】 416 417``` 418root { 419 sample_config { 420 sample_version = 1; // 使用下划线命名 421 sample_bus = "I2C_0"; 422 match_attr = "sample_config"; 423 } 424} 425``` 426 427### HCS宏 428 429驱动的私有配置信息会被加载到HdfDeviceObject中的property中,因此会占用一定的内存空间,这在轻量和小型系统中带来的缺点尤为明显。为了减少私有配置信息的内存占用,HDF框架提供了HCS宏,来解析驱动的私有配置信息。 430 431#### 【规则】在内存敏感或跨系统类型的驱动场景下,应当使用HCS宏来解析驱动的私有配置信息 432 433【说明】开发者应当明确驱动的使用场景,如果对内存敏感或者需要跨轻量、小型和标准系统使用,应当使用HCS宏来解析驱动的私有配置信息,从而保证驱动的性能和可移植性。 434 435【样例】 436 437```c 438#include <utils/hcs_macro.h> 439 440#define SAMPLE_CONFIG_NODE HCS_NODE(HCS_ROOT, sample_config) 441 442ASSERT_EQ(HCS_PROP(SAMPLE_CONFIG_NODE, sampleVersion), 1); 443ASSERT_EQ(HCS_PROP(SAMPLE_CONFIG_NODE, sample_bus), "I2C_0"); 444ASSERT_EQ(HCS_PROP(SAMPLE_CONFIG_NODE, match_attr), "sample_config"); 445``` 446 447### HDF工具 448 449#### 【规则】在使用HdfSbuf进行数据通信时,应当明确通信的场景,并根据相应场景确定创建的HdfSbuf类型 450 451【说明】HdfSbuf是HDF进行数据传输时的数据结构,此结构根据通信的场景区分为不同的类型,定义在hdf_sbuf.h头文件的枚举中: 452 453```c 454enum HdfSbufType { 455 SBUF_RAW = 0, /* SBUF used for communication between the user space and the kernel space */ 456 SBUF_IPC, /* SBUF used for inter-process communication (IPC) */ 457 SBUF_IPC_HW, /* Reserved for extension */ 458 SBUF_TYPE_MAX, /* Maximum value of the SBUF type */ 459}; 460``` 461 462开发者在进行数据通信时,应当明确是跨用户态和内核态通信场景,还是用户态的进程间通信,从而创建相应的HdfSbuf。 463 464【样例】 465 466```c 467void SampleDispatchBetweenUserAndKernel() 468{ 469 int32_t ret; 470 /* 跨用户态和内核态进行通信的场景 */ 471 struct HdfSBuf *data = HdfSBufTypedObtain(SBUF_RAW); 472 ... 473 ret = sample->dispatcher->Dispatch(&sample->object, CMD_SAMPLE_DISPATCH, data, NULL); 474 ... 475 HdfSBufRecycle(data); 476} 477``` 478 479【样例】 480 481```c++ 482void SampleDispatchIpc() 483{ 484 /* 跨进程进行通信的场景 */ 485 struct HdfSBuf *data = HdfSBufTypedObtain(SBUF_IPC); 486 ... 487 int ret = sample->dispatcher->Dispatch(sample, CMD_SAMPLE_DISPATCH, data, nullptr); 488 ... 489 HdfSBufRecycle(data); 490} 491``` 492 493#### 【规则】在使用HDF的日志打印时,应当明确定义HDF_LOG_TAG日志打印的标签 494 495【说明】HDF框架提供了一套日志打印工具hdf_log.h,开发者可以直接使用HDF的日志打印进行驱动运行日志的输出。HDF_LOG_TAG宏的作用是定义日志打印的标签,开发者必须在打印日志前进行定义。 496 497【样例】 498 499```c 500#include <hdf_log.h> 501 502#define HDF_LOG_TAG sample_driver // 定义日志的标签 503 504int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject) 505{ 506 HDF_LOGI("sample driver is initialized"); // 使用HDF日志工具打印日志 507 return HDF_SUCCESS; 508} 509``` 510 511#### 【规则】应当对HDF框架方法的返回值进行有效判断,并使用HDF提供的错误码 512 513【说明】HDF框架提供的方法有明确的错误码返回值,开发者在使用时应当进行判断,而不是选择忽略。对应的返回值为hdf_base.h头文件中的错误码,开发者在使用HDF或实现自定义方法时,应当统一使用HDF提供的错误码。 514 515【样例】 516 517```c 518int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject) 519{ 520 int32_t ret; 521 // 判断设备类型设置是否成功 522 if (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_DISPLAY)) { 523 HDF_LOGE("HdfDeviceSetClass failed"); 524 return HDF_FAILURE; 525 } 526 ret = InitDiver(); 527 // 自定义方法使用HDF的错误码 528 if (ret != HDF_SUCCESS) { 529 HDF_LOGE("init driver is failed"); 530 return ret; 531 } 532 return HDF_SUCCESS; 533} 534``` 535 536### OSAL框架 537 538HDF OSAL框架屏蔽了OpenHarmony各个系统类型之间的接口差异,对外提供统一的OS接口,包括内存管理、线程、互斥体、自旋锁、信号量、定时器、文件、中断、时间、原子、固件、I/O操作模块。 539 540#### 【规则】跨轻量、小型和标准系统类型的驱动,必须通过OSAL框架来使用操作系统接口 541 542【说明】OSAL屏蔽了OS接口之间的差异,开发者应当基于OSAL来操作OS的接口,保证驱动能够跨系统类型运行。 543 544【样例】 545 546```c 547#include <osal/osal_mem.h> 548#include <util/hdf_log.h> 549 550struct DevHandle *SampleInit(void) 551{ 552 struct DevHandle *handle = (struct DevHandle *)OsalMemCalloc(sizeof(struct DevHandle)); 553 if (handle == NULL) { 554 HDF_LOGE("OsalMemCalloc handle failed"); 555 return NULL; 556 } 557 return handle; 558} 559``` 560 561【样例】 562 563```c 564#include <osal/osal_time.h> 565 566void SampleSleep(uint32_t timeMs) 567{ 568 OsalMSleep(timeMs); 569} 570``` 571 572### PAL框架 573 574HDF PAL框架对平台类驱动进行了抽象,并对外提供统一的操作接口,包括GPIO、I2C、SPI、UART、RTC、SDIO、EMMC、DSI、PWM、WATCHDOG等模块。 575 576#### 【规则】跨轻量、小型和标准系统类型的驱动,必须通过PAL框架来使用平台驱动 577 578【说明】PAL屏蔽了不同系统类型之间的平台驱动接口差异,开发者应当基于PAL来操作这些接口,保证驱动能够跨系统类型运行。 579 580【样例】 581 582```c 583#include <platform/gpio_if.h> 584#include <util/hdf_log.h> 585#include <osal/osal_irq.h> 586#include <osal/osal_time.h> 587 588static uint32_t g_irqCnt; 589 590/* GPIO中断服务样例函数 */ 591static int32_t SampleGpioIrqHandler(uint16_t gpio, void *data) 592{ 593 HDF_LOGE("%s: irq triggered, on gpio:%u, data=%p", __func__, gpio, data); 594 g_irqCnt++; // 如果中断服务函数触发执行,则将全局中断计数加1 595 return GpioDisableIrq(gpio); 596} 597 598/* GPIO样例函数 */ 599static int32_t SampleGpioIrqEdge(void) 600{ 601 int32_t ret; 602 uint16_t valRead; 603 uint16_t mode; 604 uint16_t gpio = 83; // 待测试的GPIO管脚号 605 uint32_t timeout; 606 607 /* 将管脚方向设置为输出 */ 608 ret = GpioSetDir(gpio, GPIO_DIR_OUT); 609 ... 610 /* 禁止该管脚中断 */ 611 ret = GpioDisableIrq(gpio); 612 ... 613 /* 为管脚设置中断服务函数,触发模式为上升沿和下降沿共同触发 */ 614 mode = OSAL_IRQF_TRIGGER_RISING | OSAL_IRQF_TRIGGER_FALLING; 615 ret = GpioSetIrq(gpio, mode, SampleGpioIrqHandler, NULL); 616 ... 617 /* 使能此管脚中断 */ 618 ret = GpioEnableIrq(gpio); 619 ... 620 g_irqCnt = 0; // 清除全局计数器 621 timeout = 0; // 等待时间清零 622 /* 等待此管脚中断服务函数触发,等待超时时间为1000毫秒 */ 623 while (g_irqCnt <= 0 && timeout < 1000) { 624 ret = GpioRead(gpio, &valRead); 625 ... 626 ret = GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW); 627 ... 628 OsalMDelay(200); // 等待中断触发 629 timeout += 200; 630 } 631 ret = GpioUnSetIrq(gpio); 632 ... 633 return (g_irqCnt > 0) ? HDF_SUCCESS : HDF_FAILURE; 634} 635``` 636