1# 驱动开发示例<a name="ZH-CN_TOPIC_0000001174350613"></a> 2 3- [驱动程序介绍](#s8efc1952ebfe4d1ea717182e108c29bb) 4- [编译和烧录](#section660016185110) 5- [镜像运行](#section333215226219) 6- [下一步学习](#section9712145420182) 7 8本节指导开发者在单板上运行第一个驱动程序,其中包括驱动程序介绍、编译、烧写、运行等步骤。 9 10## 驱动程序介绍<a name="s8efc1952ebfe4d1ea717182e108c29bb"></a> 11 12下面基于HDF框架,提供一个简单的UART(Universal Asynchronous Receiver/Transmitter)平台驱动开发样例,包含配置文件的添加,驱动代码的实现以及用户态程序和驱动交互的流程。驱动程序源码位于vendor/huawei/hdf/sample目录 13 141. 添加配置。 15 16 在HDF框架的驱动配置文件(例如device/hisilicon/hi3516dv300/sdk\_liteos/config/uart/uart\_config.hcs)中添加该驱动的配置信息,如下所示: 17 18 ``` 19 root { 20 platform { 21 uart_sample { 22 num = 5; // UART设备编号 23 base = 0x120a0000; // UART 寄存器基地址 24 irqNum = 38; 25 baudrate = 115200; 26 uartClk = 24000000; 27 wlen = 0x60; 28 parity = 0; 29 stopBit = 0; 30 match_attr = "sample_uart_5"; 31 } 32 } 33 } 34 ``` 35 36 在HDF框架的设备配置文件(例如vendor/hisilicon/ipcamera\_hi3516dv300\_liteos/config/device\_info/device\_info.hcs)中添加该驱动的设备节点信息,如下所示: 37 38 ``` 39 root { 40 device_info { 41 platform :: host { 42 hostName = "platform_host"; 43 priority = 50; 44 device_uart :: device { 45 device5 :: deviceNode { 46 policy = 2; 47 priority = 10; 48 permission = 0660; 49 moduleName = "UART_SAMPLE"; 50 serviceName = "HDF_PLATFORM_UART_5"; 51 deviceMatchAttr = "sample_uart_5"; 52 } 53 } 54 } 55 } 56 } 57 ``` 58 59 > **说明:** 60 >配置文件与UART驱动示例的源码在同一个路径,需要手动添加到Hi3516DV300单板路径下。 61 622. 注册UART驱动入口。 63 64 基于HDF框架注册UART驱动的入口HdfDriverEntry,代码如下: 65 66 ``` 67 // 绑定UART驱动接口到HDF框架 68 static int32_t SampleUartDriverBind(struct HdfDeviceObject *device) 69 { 70 struct UartHost *uartHost = NULL; 71 72 if (device == NULL) { 73 return HDF_ERR_INVALID_OBJECT; 74 } 75 HDF_LOGI("Enter %s:", __func__); 76 77 uartHost = UartHostCreate(device); 78 if (uartHost == NULL) { 79 HDF_LOGE("%s: UartHostCreate failed", __func__); 80 return HDF_FAILURE; 81 } 82 uartHost->service.Dispatch = SampleDispatch; 83 return HDF_SUCCESS; 84 } 85 86 // 从UART驱动的HCS中获取配置信息 87 static uint32_t GetUartDeviceResource( 88 struct UartDevice *device, const struct DeviceResourceNode *resourceNode) 89 { 90 struct UartResource *resource = &device->resource; 91 struct DeviceResourceIface *dri = NULL; 92 dri = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 93 if (dri == NULL || dri->GetUint32 == NULL) { 94 HDF_LOGE("DeviceResourceIface is invalid"); 95 return HDF_FAILURE; 96 } 97 98 if (dri->GetUint32(resourceNode, "num", &resource->num, 0) != HDF_SUCCESS) { 99 HDF_LOGE("uart config read num fail"); 100 return HDF_FAILURE; 101 } 102 if (dri->GetUint32(resourceNode, "base", &resource->base, 0) != HDF_SUCCESS) { 103 HDF_LOGE("uart config read base fail"); 104 return HDF_FAILURE; 105 } 106 resource->physBase = (unsigned long)OsalIoRemap(resource->base, 0x48); 107 if (resource->physBase == 0) { 108 HDF_LOGE("uart config fail to remap physBase"); 109 return HDF_FAILURE; 110 } 111 if (dri->GetUint32(resourceNode, "irqNum", &resource->irqNum, 0) != HDF_SUCCESS) { 112 HDF_LOGE("uart config read irqNum fail"); 113 return HDF_FAILURE; 114 } 115 if (dri->GetUint32(resourceNode, "baudrate", &resource->baudrate, 0) != HDF_SUCCESS) { 116 HDF_LOGE("uart config read baudrate fail"); 117 return HDF_FAILURE; 118 } 119 if (dri->GetUint32(resourceNode, "wlen", &resource->wlen, 0) != HDF_SUCCESS) { 120 HDF_LOGE("uart config read wlen fail"); 121 return HDF_FAILURE; 122 } 123 if (dri->GetUint32(resourceNode, "parity", &resource->parity, 0) != HDF_SUCCESS) { 124 HDF_LOGE("uart config read parity fail"); 125 return HDF_FAILURE; 126 } 127 if (dri->GetUint32(resourceNode, "stopBit", &resource->stopBit, 0) != HDF_SUCCESS) { 128 HDF_LOGE("uart config read stopBit fail"); 129 return HDF_FAILURE; 130 } 131 if (dri->GetUint32(resourceNode, "uartClk", &resource->uartClk, 0) != HDF_SUCCESS) { 132 HDF_LOGE("uart config read uartClk fail"); 133 return HDF_FAILURE; 134 } 135 return HDF_SUCCESS; 136 } 137 138 // 将UART驱动的配置和接口附加到HDF驱动框架 139 static int32_t AttachUartDevice(struct UartHost *host, struct HdfDeviceObject *device) 140 { 141 int32_t ret; 142 struct UartDevice *uartDevice = NULL; 143 if (device->property == NULL) { 144 HDF_LOGE("%s: property is NULL", __func__); 145 return HDF_FAILURE; 146 } 147 uartDevice = (struct UartDevice *)OsalMemCalloc(sizeof(struct UartDevice)); 148 if (uartDevice == NULL) { 149 HDF_LOGE("%s: OsalMemCalloc uartDevice error", __func__); 150 return HDF_ERR_MALLOC_FAIL; 151 } 152 ret = GetUartDeviceResource(uartDevice, device->property); 153 if (ret != HDF_SUCCESS) { 154 (void)OsalMemFree(uartDevice); 155 return HDF_FAILURE; 156 } 157 host->num = uartDevice->resource.num; 158 host->priv = uartDevice; 159 AddUartDevice(host); 160 return InitUartDevice(uartDevice); 161 } 162 163 // 初始化UART驱动 164 static int32_t SampleUartDriverInit(struct HdfDeviceObject *device) 165 { 166 int32_t ret; 167 struct UartHost *host = NULL; 168 169 if (device == NULL) { 170 HDF_LOGE("%s: device is NULL", __func__); 171 return HDF_ERR_INVALID_OBJECT; 172 } 173 HDF_LOGI("Enter %s:", __func__); 174 host = UartHostFromDevice(device); 175 if (host == NULL) { 176 HDF_LOGE("%s: host is NULL", __func__); 177 return HDF_FAILURE; 178 } 179 ret = AttachUartDevice(host, device); 180 if (ret != HDF_SUCCESS) { 181 HDF_LOGE("%s: attach error", __func__); 182 return HDF_FAILURE; 183 } 184 host->method = &g_sampleUartHostMethod; 185 return ret; 186 } 187 188 static void DeinitUartDevice(struct UartDevice *device) 189 { 190 struct UartRegisterMap *regMap = (struct UartRegisterMap *)device->resource.physBase; 191 /* wait for uart enter idle. */ 192 while (UartPl011IsBusy(regMap)); 193 UartPl011ResetRegisters(regMap); 194 uart_clk_cfg(0, false); 195 OsalIoUnmap((void *)device->resource.physBase); 196 device->state = UART_DEVICE_UNINITIALIZED; 197 } 198 199 // 解绑并释放UART驱动 200 static void DetachUartDevice(struct UartHost *host) 201 { 202 struct UartDevice *uartDevice = NULL; 203 204 if (host->priv == NULL) { 205 HDF_LOGE("%s: invalid parameter", __func__); 206 return; 207 } 208 uartDevice = host->priv; 209 DeinitUartDevice(uartDevice); 210 (void)OsalMemFree(uartDevice); 211 host->priv = NULL; 212 } 213 214 // 释放UART驱动 215 static void SampleUartDriverRelease(struct HdfDeviceObject *device) 216 { 217 struct UartHost *host = NULL; 218 HDF_LOGI("Enter %s:", __func__); 219 220 if (device == NULL) { 221 HDF_LOGE("%s: device is NULL", __func__); 222 return; 223 } 224 host = UartHostFromDevice(device); 225 if (host == NULL) { 226 HDF_LOGE("%s: host is NULL", __func__); 227 return; 228 } 229 if (host->priv != NULL) { 230 DetachUartDevice(host); 231 } 232 UartHostDestroy(host); 233 } 234 235 struct HdfDriverEntry g_sampleUartDriverEntry = { 236 .moduleVersion = 1, 237 .moduleName = "UART_SAMPLE", 238 .Bind = SampleUartDriverBind, 239 .Init = SampleUartDriverInit, 240 .Release = SampleUartDriverRelease, 241 }; 242 243 HDF_INIT(g_sampleUartDriverEntry); 244 ``` 245 2463. 注册UART驱动接口。 247 248 HDF框架提供了UART驱动接口的模板方法UartHostMethod,实现UART驱动接口的代码如下: 249 250 ``` 251 static int32_t SampleUartHostInit(struct UartHost *host) 252 { 253 HDF_LOGI("%s: Enter", __func__); 254 if (host == NULL) { 255 HDF_LOGE("%s: invalid parameter", __func__); 256 return HDF_ERR_INVALID_PARAM; 257 } 258 return HDF_SUCCESS; 259 } 260 261 static int32_t SampleUartHostDeinit(struct UartHost *host) 262 { 263 HDF_LOGI("%s: Enter", __func__); 264 if (host == NULL) { 265 HDF_LOGE("%s: invalid parameter", __func__); 266 return HDF_ERR_INVALID_PARAM; 267 } 268 return HDF_SUCCESS; 269 } 270 271 // 向UART中写入数据 272 static int32_t SampleUartHostWrite(struct UartHost *host, uint8_t *data, uint32_t size) 273 { 274 HDF_LOGI("%s: Enter", __func__); 275 uint32_t idx; 276 struct UartRegisterMap *regMap = NULL; 277 struct UartDevice *device = NULL; 278 279 if (host == NULL || data == NULL || size == 0) { 280 HDF_LOGE("%s: invalid parameter", __func__); 281 return HDF_ERR_INVALID_PARAM; 282 } 283 device = (struct UartDevice *)host->priv; 284 if (device == NULL) { 285 HDF_LOGE("%s: device is NULL", __func__); 286 return HDF_ERR_INVALID_PARAM; 287 } 288 regMap = (struct UartRegisterMap *)device->resource.physBase; 289 for (idx = 0; idx < size; idx++) { 290 UartPl011Write(regMap, data[idx]); 291 } 292 return HDF_SUCCESS; 293 } 294 295 // 设置UART的波特率 296 static int32_t SampleUartHostSetBaud(struct UartHost *host, uint32_t baudRate) 297 { 298 HDF_LOGI("%s: Enter", __func__); 299 struct UartDevice *device = NULL; 300 struct UartRegisterMap *regMap = NULL; 301 UartPl011Error err; 302 303 if (host == NULL) { 304 HDF_LOGE("%s: invalid parameter", __func__); 305 return HDF_ERR_INVALID_PARAM; 306 } 307 device = (struct UartDevice *)host->priv; 308 if (device == NULL) { 309 HDF_LOGE("%s: device is NULL", __func__); 310 return HDF_ERR_INVALID_PARAM; 311 } 312 regMap = (struct UartRegisterMap *)device->resource.physBase; 313 if (device->state != UART_DEVICE_INITIALIZED) { 314 return UART_PL011_ERR_NOT_INIT; 315 } 316 if (baudRate == 0) { 317 return UART_PL011_ERR_INVALID_BAUD; 318 } 319 err = UartPl011SetBaudrate(regMap, device->uartClk, baudRate); 320 if (err == UART_PL011_ERR_NONE) { 321 device->baudrate = baudRate; 322 } 323 return err; 324 } 325 326 // 获取UART的波特率 327 static int32_t SampleUartHostGetBaud(struct UartHost *host, uint32_t *baudRate) 328 { 329 HDF_LOGI("%s: Enter", __func__); 330 struct UartDevice *device = NULL; 331 332 if (host == NULL) { 333 HDF_LOGE("%s: invalid parameter", __func__); 334 return HDF_ERR_INVALID_PARAM; 335 } 336 device = (struct UartDevice *)host->priv; 337 if (device == NULL) { 338 HDF_LOGE("%s: device is NULL", __func__); 339 return HDF_ERR_INVALID_PARAM; 340 } 341 *baudRate = device->baudrate; 342 return HDF_SUCCESS; 343 } 344 345 // 在HdfUartSampleInit方法中绑定 346 struct UartHostMethod g_sampleUartHostMethod = { 347 .Init = SampleUartHostInit, 348 .Deinit = SampleUartHostDeinit, 349 .Read = NULL, 350 .Write = SampleUartHostWrite, 351 .SetBaud = SampleUartHostSetBaud, 352 .GetBaud = SampleUartHostGetBaud, 353 .SetAttribute = NULL, 354 .GetAttribute = NULL, 355 .SetTransMode = NULL, 356 }; 357 ``` 358 359 在device/hisilicon/drivers/lite.mk编译脚本中增加示例UART驱动模块,代码如下: 360 361 ``` 362 LITEOS_BASELIB += -lhdf_uart_sample 363 LIB_SUBDIRS += $(LITEOS_SOURCE_ROOT)/vendor/huawei/hdf/sample/platform/uart 364 ``` 365 3664. 用户程序和驱动交互代码。 367 368 UART驱动成功初始化后,会创建/dev/uartdev-5设备节点,通过设备节点与UART驱动交互的代码如下: 369 370 ``` 371 #include <stdlib.h> 372 #include <unistd.h> 373 #include <fcntl.h> 374 #include "hdf_log.h" 375 376 #define HDF_LOG_TAG "hello_uart" 377 #define INFO_SIZE 16 378 379 int main(void) 380 { 381 int ret; 382 int fd; 383 const char info[INFO_SIZE] = {" HELLO UART! "}; 384 385 fd = open("/dev/uartdev-5", O_RDWR); 386 if (fd < 0) { 387 HDF_LOGE("hello_uart uartdev-5 open failed %d", fd); 388 return -1; 389 } 390 ret = write(fd, info, INFO_SIZE); 391 if (ret != 0) { 392 HDF_LOGE("hello_uart write uartdev-5 ret is %d", ret); 393 } 394 ret = close(fd); 395 if (ret != 0) { 396 HDF_LOGE("hello_uart uartdev-5 close failed %d", fd); 397 return -1; 398 } 399 return ret; 400 } 401 ``` 402 403 在build/lite/components/drivers.json驱动配置中hdf\_hi3516dv300\_liteos\_a组件下的targets中增加hello\_uart\_sample组件,代码如下: 404 405 ``` 406 { 407 "components": [ 408 { 409 "component": "hdf_hi3516dv300_liteos_a", 410 ... 411 "targets": [ 412 "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample" 413 ] 414 } 415 ] 416 } 417 ``` 418 419 > **说明:** 420 >如上代码均为示例代码,完整代码可以在vendor/huawei/hdf/sample查看。 421 >示例代码默认不参与编译,需要手动添加到编译脚本中。 422 423 424## 编译和烧录<a name="section660016185110"></a> 425 426参考《运行Hello OHOS》进行编译和烧录:[编译](quickstart-lite-steps-hi3516-running.md#section1077671315253)、[烧录](quickstart-lite-steps-hi3516-running.md#section1347011412201) 427 428## 镜像运行<a name="section333215226219"></a> 429 4301. 连接串口。 431 432 > **须知:** 433 >若无法连接串口,请参考[常见问题](quickstart-lite-steps-hi3516-faqs.md#section14871149155911)进行排查。 434 435 **图 1** 连接串口图<a name="fig124315964718"></a> 436  437 438 1. 单击**Monitor**打开串口。 439 2. 连续输入回车直到串口显示"hisilicon"。 440 3. 单板初次启动或修改启动参数,请进入[步骤2](quickstart-lite-steps-hi3516-running.md#l5b42e79a33ea4d35982b78a22913b0b1),否则进入[步骤3](quickstart-lite-steps-hi3516-running.md#ld26f18828aa44c36bfa36be150e60e49)。 441 4422. (单板初次启动必选)修改U-boot的bootcmd及bootargs内容:该步骤为固化操作,若不修改参数只需执行一次。每次复位单板均会自动进入系统。 443 444 > **须知:** 445 >U-boot引导程序默认会有2秒的等待时间,用户可使用回车打断等待并显示"hisilicon",通过**reset**命令可再次启动系统。 446 447 **表 1** U-boot修改命令 448 449 <a name="zh-cn_topic_0000001151888681_table1323441103813"></a> 450 <table><thead align="left"><tr id="zh-cn_topic_0000001151888681_row1423410183818"><th class="cellrowborder" valign="top" width="50%" id="mcps1.2.3.1.1"><p id="zh-cn_topic_0000001151888681_p623461163818"><a name="zh-cn_topic_0000001151888681_p623461163818"></a><a name="zh-cn_topic_0000001151888681_p623461163818"></a>执行命令</p> 451 </th> 452 <th class="cellrowborder" valign="top" width="50%" id="mcps1.2.3.1.2"><p id="zh-cn_topic_0000001151888681_p42341014388"><a name="zh-cn_topic_0000001151888681_p42341014388"></a><a name="zh-cn_topic_0000001151888681_p42341014388"></a>命令解释</p> 453 </th> 454 </tr> 455 </thead> 456 <tbody><tr id="zh-cn_topic_0000001151888681_row1623471113817"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="zh-cn_topic_0000001151888681_p102341719385"><a name="zh-cn_topic_0000001151888681_p102341719385"></a><a name="zh-cn_topic_0000001151888681_p102341719385"></a>setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000";</p> 457 </td> 458 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="zh-cn_topic_0000001151888681_p92347120389"><a name="zh-cn_topic_0000001151888681_p92347120389"></a><a name="zh-cn_topic_0000001151888681_p92347120389"></a>读取FLASH起始地址为0x800(单位为512B,即1MB),大小为0x4800(单位为512B,即9MB)的内容到0x80000000的内存地址,该大小(9MB)与IDE中所填写OHOS_Image.bin文件大小<strong id="zh-cn_topic_0000001151888681_b15685648113111"><a name="zh-cn_topic_0000001151888681_b15685648113111"></a><a name="zh-cn_topic_0000001151888681_b15685648113111"></a>必须相同</strong>。</p> 459 </td> 460 </tr> 461 <tr id="zh-cn_topic_0000001151888681_row12234912381"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="zh-cn_topic_0000001151888681_p172306219392"><a name="zh-cn_topic_0000001151888681_p172306219392"></a><a name="zh-cn_topic_0000001151888681_p172306219392"></a>setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=20M rw";</p> 462 </td> 463 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="zh-cn_topic_0000001151888681_p13489329396"><a name="zh-cn_topic_0000001151888681_p13489329396"></a><a name="zh-cn_topic_0000001151888681_p13489329396"></a>表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat,</p> 464 <p id="zh-cn_topic_0000001151888681_p12481832163913"><a name="zh-cn_topic_0000001151888681_p12481832163913"></a><a name="zh-cn_topic_0000001151888681_p12481832163913"></a>“rootaddr=10M rootsize=20M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与IDE中新增rootfs.img文件时所填大小<strong id="zh-cn_topic_0000001151888681_b24816327398"><a name="zh-cn_topic_0000001151888681_b24816327398"></a><a name="zh-cn_topic_0000001151888681_b24816327398"></a>必须相同</strong>。</p> 465 </td> 466 </tr> 467 <tr id="zh-cn_topic_0000001151888681_row18234161153820"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="zh-cn_topic_0000001151888681_p823417118386"><a name="zh-cn_topic_0000001151888681_p823417118386"></a><a name="zh-cn_topic_0000001151888681_p823417118386"></a>saveenv</p> 468 </td> 469 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="zh-cn_topic_0000001151888681_p32341616389"><a name="zh-cn_topic_0000001151888681_p32341616389"></a><a name="zh-cn_topic_0000001151888681_p32341616389"></a>表示保存当前配置。</p> 470 </td> 471 </tr> 472 <tr id="zh-cn_topic_0000001151888681_row192345113811"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="zh-cn_topic_0000001151888681_p7235111183819"><a name="zh-cn_topic_0000001151888681_p7235111183819"></a><a name="zh-cn_topic_0000001151888681_p7235111183819"></a>reset</p> 473 </td> 474 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="zh-cn_topic_0000001151888681_p123781411114016"><a name="zh-cn_topic_0000001151888681_p123781411114016"></a><a name="zh-cn_topic_0000001151888681_p123781411114016"></a>表示复位单板。</p> 475 </td> 476 </tr> 477 </tbody> 478 </table> 479 480 > **须知:** 481 >**“go 0x80000000”**为可选指令,默认配置已将该指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用"回车"打断自动启动。 482 4833. 输入**“reset”**指令并回车,重启单板,启动成功如下图,输入回车串口显示OHOS字样。 484 485 **图 2** 系统启动图<a name="fig14618415485"></a> 486  487 4884. 根目录下,在命令行输入指令“**./bin/hello\_uart**”执行写入的demo程序,显示成功结果如下所示。 489 490 ``` 491 OHOS # ./bin/hello_uart 492 OHOS # HELLO UART! 493 ``` 494 495 496## 下一步学习<a name="section9712145420182"></a> 497 498恭喜,您已完成Hi3516 快速上手!建议您下一步进入[带屏摄像头产品开发](../guide/device-camera.md)的学习 。 499 500