开发Hi3516第一个驱动程序示例

本节指导开发者在单板上运行第一个驱动程序,其中包括驱动程序介绍、编译、烧写、运行等步骤。

获取源码

参考“开发Hi3516第一个应用程序示例”获取源码。

驱动程序介绍

下面基于HDF框架,提供一个简单的UART(Universal Asynchronous Receiver/Transmitter)平台驱动开发样例,包含配置文件的添加,驱动代码的实现以及用户态程序和驱动交互的流程。驱动程序源码位于vendor/huawei/hdf/sample目录。

  1. 添加配置。

在HDF框架的驱动配置文件(例如vendor/hisi/hi35xx/hi3516dv300/config/uart/uart_config.hcs)中添加该驱动的配置信息,如下所示:

```

  1. root {
  2. platform {
  3. uart_sample {
  4. num = 5; // UART设备编号
  5. base = 0x120a0000; // UART 寄存器基地址
  6. irqNum = 38;
  7. baudrate = 115200;
  8. uartClk = 24000000;
  9. wlen = 0x60;
  10. parity = 0;
  11. stopBit = 0;
  12. match_attr = "sample_uart_5";
  13. }
  14. }
  15. } ```

在HDF框架的设备配置文件(例如vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs)中添加该驱动的设备节点信息,如下所示:

```

  1. root {
  2. device_info {
  3. platform :: host {
  4. hostName = "platform_host";
  5. priority = 50;
  6. device_uart :: device {
  7. device5 :: deviceNode {
  8. policy = 2;
  9. priority = 10;
  10. permission = 0660;
  11. moduleName = "UART_SAMPLE";
  12. serviceName = "HDF_PLATFORM_UART_5";
  13. deviceMatchAttr = "sample_uart_5";
  14. }
  15. }
  16. }
  17. }
  18. } ```

开发Hi3516第一个驱动程序示例 - 图1 说明: 配置文件与UART驱动示例的源码在同一个路径,需要手动添加到Hi3516DV300单板路径下。 2. 注册UART驱动入口。

基于HDF框架注册UART驱动的入口HdfDriverEntry,代码如下:

```

  1. // 绑定UART驱动接口到HDF框架
  2. static int32_t HdfUartSampleBind(struct HdfDeviceObject *device)
  3. {
  4. if (device == NULL) {
  5. return HDF_ERR_INVALID_OBJECT;
  6. }
  7. HDF_LOGI("Enter %s:", func);
  8. return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS;
  9. }

  10. // 从UART驱动的HCS中获取配置信息

  11. static uint32_t UartDeviceGetResource(
  12. struct UartDevice device, const struct DeviceResourceNode resourceNode)
  13. {
  14. struct UartResource *resource = &device->resource;
  15. struct DeviceResourceIface *dri = NULL;
  16. dri = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
  17. if (dri == NULL || dri->GetUint32 == NULL) {
  18. HDF_LOGE("DeviceResourceIface is invalid");
  19. return HDF_FAILURE;
  20. }

  21. if (dri->GetUint32(resourceNode, "num", &resource->num, 0) != HDF_SUCCESS) {

  22. HDF_LOGE("uart config read num fail");
  23. return HDF_FAILURE;
  24. }
  25. if (dri->GetUint32(resourceNode, "base", &resource->base, 0) != HDF_SUCCESS) {
  26. HDF_LOGE("uart config read base fail");
  27. return HDF_FAILURE;
  28. }
  29. resource->physBase = (unsigned long) OsalIoRemap(resource->base, 0x48);
  30. if (resource->physBase == 0) {
  31. HDF_LOGE("uart config fail to remap physBase");
  32. return HDF_FAILURE;
  33. }
  34. if (dri->GetUint32(resourceNode, "irqNum", &resource->irqNum, 0) != HDF_SUCCESS) {
  35. HDF_LOGE("uart config read irqNum fail");
  36. return HDF_FAILURE;
  37. }
  38. if (dri->GetUint32(resourceNode, "baudrate", &resource->baudrate, 0) != HDF_SUCCESS) {
  39. HDF_LOGE("uart config read baudrate fail");
  40. return HDF_FAILURE;
  41. }
  42. if (dri->GetUint32(resourceNode, "wlen", &resource->wlen, 0) != HDF_SUCCESS) {
  43. HDF_LOGE("uart config read wlen fail");
  44. return HDF_FAILURE;
  45. }
  46. if (dri->GetUint32(resourceNode, "parity", &resource->parity, 0) != HDF_SUCCESS) {
  47. HDF_LOGE("uart config read parity fail");
  48. return HDF_FAILURE;
  49. }
  50. if (dri->GetUint32(resourceNode, "stopBit", &resource->stopBit, 0) != HDF_SUCCESS) {
  51. HDF_LOGE("uart config read stopBit fail");
  52. return HDF_FAILURE;
  53. }
  54. if (dri->GetUint32(resourceNode, "uartClk", &resource->uartClk, 0) != HDF_SUCCESS) {
  55. HDF_LOGE("uart config read uartClk fail");
  56. return HDF_FAILURE;
  57. }
  58. return HDF_SUCCESS;
  59. }

  60. // 将UART驱动的配置和接口附加到HDF驱动框架

  61. static int32_t SampleAttach(struct UartHost host, struct HdfDeviceObject device)
  62. {
  63. int32_t ret;
  64. struct UartDevice *uartDevice = NULL;
  65. if (device->property == NULL) {
  66. HDF_LOGE("%s: property is NULL", func);
  67. return HDF_FAILURE;
  68. }
  69. uartDevice = (struct UartDevice *) OsalMemCalloc(sizeof(struct UartDevice));
  70. if (uartDevice == NULL) {
  71. HDF_LOGE("%s: OsalMemCalloc uartDevice error", func);
  72. return HDF_ERR_MALLOC_FAIL;
  73. }
  74. ret = UartDeviceGetResource(uartDevice, device->property);
  75. if (ret != HDF_SUCCESS) {
  76. (void) OsalMemFree(uartDevice);
  77. return HDF_FAILURE;
  78. }
  79. host->num = uartDevice->resource.num;
  80. host->priv = uartDevice;
  81. UartSampleAddDev(host); // 添加用户态UART设备节点,具体实现见源码uart_dev_sample
  82. return UartDeviceInit(uartDevice); // 初始化UART PL011,具体实现见源码uart_pl011_sample
  83. }

  84. // 初始化UART驱动

  85. static int32_t HdfUartSampleInit(struct HdfDeviceObject *device)
  86. {
  87. int32_t ret;
  88. struct UartHost *host = NULL;

  89. if (device == NULL) {

  90. HDF_LOGE("%s: device is NULL", func);
  91. return HDF_ERR_INVALID_OBJECT;
  92. }
  93. HDF_LOGI("Enter %s:", func);
  94. host = UartHostFromDevice(device);
  95. if (host == NULL) {
  96. HDF_LOGE("%s: host is NULL", func);
  97. return HDF_FAILURE;
  98. }
  99. ret = SampleAttach(host, device);
  100. if (ret != HDF_SUCCESS) {
  101. HDF_LOGE("%s: attach error", func);
  102. return HDF_FAILURE;
  103. }
  104. host->method = &g_uartSampleHostMethod;
  105. return ret;
  106. }

  107. static void UartDeviceDeinit(struct UartDevice *device)

  108. {
  109. struct UartRegisterMap regMap = (struct UartRegisterMap ) device->resource.physBase;
  110. / wait for uart enter idle. /
  111. while (UartPl011IsBusy(regMap));
  112. UartPl011ResetRegisters(regMap);
  113. uart_clk_cfg(0, false);
  114. OsalIoUnmap((void *) device->resource.physBase);
  115. device->state = UART_DEVICE_UNINITIALIZED;
  116. }

  117. // 解绑并释放UART驱动

  118. static void SampleDetach(struct UartHost *host)
  119. {
  120. struct UartDevice *uartDevice = NULL;

  121. if (host->priv == NULL) {

  122. HDF_LOGE("%s: invalid parameter", func);
  123. return;
  124. }
  125. uartDevice = host->priv;
  126. UartDeviceDeinit(uartDevice);
  127. (void) OsalMemFree(uartDevice);
  128. host->priv = NULL;
  129. }

  130. // 释放UART驱动

  131. static void HdfUartSampleRelease(struct HdfDeviceObject *device)
  132. {
  133. struct UartHost *host = NULL;
  134. HDF_LOGI("Enter %s:", func);

  135. if (device == NULL) {

  136. HDF_LOGE("%s: device is null", func);
  137. return;
  138. }
  139. host = UartHostFromDevice(device);
  140. if (host == NULL) {
  141. HDF_LOGE("%s: host is null", func);
  142. return;
  143. }
  144. if (host->priv != NULL) {
  145. SampleDetach(host);
  146. }
  147. UartHostDestroy(host);
  148. }

  149. struct HdfDriverEntry g_hdfUartSample = {

  150. .moduleVersion = 1,
  151. .moduleName = "UART_SAMPLE",
  152. .Bind = HdfUartSampleBind,
  153. .Init = HdfUartSampleInit,
  154. .Release = HdfUartSampleRelease,
  155. };

  156. HDF_INIT(g_hdfUartSample); ```

  157. 注册UART驱动接口。

HDF框架提供了UART驱动接口的模板方法UartHostMethod,实现UART驱动接口的代码如下:

```

  1. static int32_t SampleInit(struct UartHost *host)
  2. {
  3. HDF_LOGI("%s: Enter", func);
  4. if (host == NULL) {
  5. HDF_LOGE("%s: invalid parameter", func);
  6. return HDF_ERR_INVALID_PARAM;
  7. }
  8. return HDF_SUCCESS;
  9. }

  10. static int32_t SampleDeinit(struct UartHost *host)

  11. {
  12. HDF_LOGI("%s: Enter", func);
  13. if (host == NULL) {
  14. HDF_LOGE("%s: invalid parameter", func);
  15. return HDF_ERR_INVALID_PARAM;
  16. }
  17. return HDF_SUCCESS;
  18. }

  19. // 向UART中写入数据

  20. static int32_t SampleWrite(struct UartHost host, uint8_t data, uint32_t size)
  21. {
  22. HDF_LOGI("%s: Enter", func);
  23. uint32_t idx;
  24. struct UartRegisterMap *regMap = NULL;
  25. struct UartDevice *device = NULL;

  26. if (host == NULL || data == NULL || size == 0) {

  27. HDF_LOGE("%s: invalid parameter", func);
  28. return HDF_ERR_INVALID_PARAM;
  29. }
  30. device = (struct UartDevice *) host->priv;
  31. if (device == NULL) {
  32. HDF_LOGE("%s: device is NULL", func);
  33. return HDF_ERR_INVALID_PARAM;
  34. }
  35. regMap = (struct UartRegisterMap *) device->resource.physBase;
  36. for (idx = 0; idx < size; idx++) {
  37. while (UartPl011IsBusy(regMap));
  38. UartPl011Write(regMap, data[idx]);
  39. }
  40. return HDF_SUCCESS;
  41. }

  42. // 设置UART的波特率

  43. static int32_t SampleSetBaud(struct UartHost *host, uint32_t baudRate)
  44. {
  45. HDF_LOGI("%s: Enter", func);
  46. struct UartDevice *device = NULL;
  47. struct UartRegisterMap *regMap = NULL;
  48. UartPl011Error err;

  49. if (host == NULL) {

  50. HDF_LOGE("%s: invalid parameter", func);
  51. return HDF_ERR_INVALID_PARAM;
  52. }
  53. device = (struct UartDevice *) host->priv;
  54. if (device == NULL) {
  55. HDF_LOGE("%s: device is NULL", func);
  56. return HDF_ERR_INVALID_PARAM;
  57. }
  58. regMap = (struct UartRegisterMap *) device->resource.physBase;
  59. if (device->state != UART_DEVICE_INITIALIZED) {
  60. return UART_PL011_ERR_NOT_INIT;
  61. }
  62. if (baudRate == 0) {
  63. return UART_PL011_ERR_INVALID_BAUD;
  64. }
  65. err = UartPl011SetBaudrate(regMap, device->uartClk, baudRate);
  66. if (err == UART_PL011_ERR_NONE) {
  67. device->baudrate = baudRate;
  68. }
  69. return err;
  70. }

  71. // 获取UART的波特率

  72. static int32_t SampleGetBaud(struct UartHost host, uint32_t baudRate)
  73. {
  74. HDF_LOGI("%s: Enter", func);
  75. struct UartDevice *device = NULL;

  76. if (host == NULL) {

  77. HDF_LOGE("%s: invalid parameter", func);
  78. return HDF_ERR_INVALID_PARAM;
  79. }
  80. device = (struct UartDevice *) host->priv;
  81. if (device == NULL) {
  82. HDF_LOGE("%s: device is NULL", func);
  83. return HDF_ERR_INVALID_PARAM;
  84. }
  85. *baudRate = device->baudrate;
  86. return HDF_SUCCESS;
  87. }

  88. // 在HdfUartSampleInit方法中绑定

  89. struct UartHostMethod g_uartSampleHostMethod = {
  90. .Init = SampleInit,
  91. .Deinit = SampleDeinit,
  92. .Read = NULL,
  93. .Write = SampleWrite,
  94. .SetBaud = SampleSetBaud,
  95. .GetBaud = SampleGetBaud,
  96. .SetAttribute = NULL,
  97. .GetAttribute = NULL,
  98. .SetTransMode = NULL,
  99. }; ```

在vendor/huawei/hdf/hdf_vendor.mk编译脚本中增加示例UART驱动模块,代码如下:

```

  1. LITEOS_BASELIB += -lhdf_uart_sample
  2. LIB_SUBDIRS += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/uart ```
  3. 用户程序和驱动交互代码。

UART驱动成功初始化后,会创建/dev/uartdev-5设备节点,通过设备节点与UART驱动交互的代码如下:

```

  1. include

  2. include

  3. include

  4. include "hdf_log.h"

  5. define HDF_LOG_TAG "hello_uart"

  6. define INFO_SIZE 16

  7. int main(void)

  8. {
  9. int ret;
  10. int fd;
  11. const char info[INFO_SIZE] = {" HELLO UART! "};

  12. fd = open("/dev/uartdev-5", O_RDWR);

  13. if (fd < 0) {
  14. HDF_LOGE("hello_uart uartdev-5 open failed %d", fd);
  15. return -1;
  16. }
  17. ret = write(fd, info, INFO_SIZE);
  18. if (ret != 0) {
  19. HDF_LOGE("hello_uart write uartdev-5 ret is %d", ret);
  20. }
  21. ret = close(fd);
  22. if (ret != 0) {
  23. HDF_LOGE("hello_uart uartdev-5 close failed %d", fd);
  24. return -1;
  25. }
  26. return ret;
  27. } ```

在build/lite/product/ipcamera_hi3516dv300.json产品配置的hdf子系统下增加hello_uart_sample组件,代码如下:

```

  1. {
  2. "subsystem": [
  3. {
  4. "name": "hdf",
  5. "component": [
  6. { "name": "hdf_sample", "dir": "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample", "features":[] }
  7. ]
  8. }
  9. ]
  10. } ```

开发Hi3516第一个驱动程序示例 - 图2 说明: 如上代码均为示例代码,完整代码可以在vendor/huawei/hdf/sample查看。 示例代码默认不参与编译,需要手动添加到编译脚本中。

编译和烧写

参考示例1进行编译和烧写:编译烧写

镜像运行

  1. 连接串口。

图 1 连接串口图


1. ![](/projects/openharmony-1.0-zh-cn/quick-start/figures/chuankou1.png)

3. 1.  单击**Serial port**打开串口。
4. 2.  输入"com11"串口编号并连续输入回车直到串口显示"hisillicon"。
5. 3.  单板初次启动或修改启动参数,请进入步骤2,否则进入步骤3。

  1. (单板初次启动必选)修改U-boot的bootcmd及bootargs内容:该步骤为固化操作,若不修改参数只需执行一次。每次复位单板均会自动进入系统。

开发Hi3516第一个驱动程序示例 - 图3 须知: U-boot引导程序默认会有2秒的等待时间,用户可使用回车打断等待并显示”hisillicon”,通过reset命令可再次启动系统。

表 1 U-boot启动参数

执行命令 setenv bootcmd “sf probe 0;mmc read 0x0 0x80000000 0x800 0x4800; go 0x80000000”; setenv bootargs “console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw”; saveenv reset
命令解释 setenv bootcmd “mmc read 0x0 0x80000000 0x800 0x4800;go 0x80000000”; 表示选择FLASH器件0,读取FLASH起始地址为0x800(单位为512B,即1MB),大小为0x4800(单位为512B,即9MB)的内容到0x80000000的内存地址。 setenv bootargs “console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=10M rootsize=15M rw”; 表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat, “rootaddr=10M rootsize=15M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与IDE中新增rootfs.img文件时所填大小必须相同saveenv;表示保存当前配置。 reset;表示复位单板 [可选]“go 0x80000000”默认配置已将指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用”回车”打断自动启动。
2. 输入“reset”指令并回车,重启单板,启动成功如下图,输入回车串口显示OHOS字样。

图 2 系统启动图


1. ![](/projects/openharmony-1.0-zh-cn/quick-start/figures/qi1.png)

  1. 根目录下,在命令行输入指令“./bin/hello_uart”执行写入的demo程序,显示成功结果如下图所示。

```

  1. OHOS # ./bin/hello_uart
  2. OHOS # HELLO UART! ```

下一步学习

恭喜,您已完成Hi3516 快速上手!建议您下一步进入带屏摄像头产品开发的学习 。