开发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);
  3. 注册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
  4. 用户程序和驱动交互代码。

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

    1. #include <stdlib.h>
    2. #include <unistd.h>
    3. #include <fcntl.h>
    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)
  2. 1. 单击**Serial port**打开串口。
  3. 2. 输入"com11"串口编号并连续输入回车直到串口显示"hisillicon"
  4. 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 快速上手!建议您下一步进入带屏摄像头产品开发的学习 。