开发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. staticint32_tHdfUartSampleBind(structHdfDeviceObject*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. staticuint32_tUartDeviceGetResource(
    12. structUartDevice*device,conststructDeviceResourceNode*resourceNode)
    13. {
    14. structUartResource*resource =&device->resource;
    15. structDeviceResourceIface*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 =(unsignedlong)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. staticint32_tSampleAttach(structUartHost*host,structHdfDeviceObject*device)
    62. {
    63. int32_t ret;
    64. structUartDevice*uartDevice = NULL;
    65. if(device->property== NULL){
    66. HDF_LOGE("%s: property is NULL", __func__);
    67. return HDF_FAILURE;
    68. }
    69. uartDevice =(structUartDevice*)OsalMemCalloc(sizeof(structUartDevice));
    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. returnUartDeviceInit(uartDevice);// 初始化UART PL011,具体实现见源码uart_pl011_sample
    83. }
    84. // 初始化UART驱动
    85. staticint32_tHdfUartSampleInit(structHdfDeviceObject*device)
    86. {
    87. int32_t ret;
    88. structUartHost*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. staticvoidUartDeviceDeinit(structUartDevice*device)
    108. {
    109. structUartRegisterMap*regMap =(structUartRegisterMap*) 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. staticvoidSampleDetach(structUartHost*host)
    119. {
    120. structUartDevice*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. staticvoidHdfUartSampleRelease(structHdfDeviceObject*device)
    132. {
    133. structUartHost*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. structHdfDriverEntry 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. staticint32_tSampleInit(structUartHost*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. staticint32_tSampleDeinit(structUartHost*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. staticint32_tSampleWrite(structUartHost*host,uint8_t*data,uint32_t size)
    21. {
    22. HDF_LOGI("%s: Enter", __func__);
    23. uint32_t idx;
    24. structUartRegisterMap*regMap = NULL;
    25. structUartDevice*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 =(structUartDevice*) host->priv;
    31. if(device == NULL){
    32. HDF_LOGE("%s: device is NULL", __func__);
    33. return HDF_ERR_INVALID_PARAM;
    34. }
    35. regMap =(structUartRegisterMap*) 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. staticint32_tSampleSetBaud(structUartHost*host,uint32_t baudRate)
    44. {
    45. HDF_LOGI("%s: Enter", __func__);
    46. structUartDevice*device = NULL;
    47. structUartRegisterMap*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 =(structUartDevice*) host->priv;
    54. if(device == NULL){
    55. HDF_LOGE("%s: device is NULL", __func__);
    56. return HDF_ERR_INVALID_PARAM;
    57. }
    58. regMap =(structUartRegisterMap*) 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. staticint32_tSampleGetBaud(structUartHost*host,uint32_t*baudRate)
    73. {
    74. HDF_LOGI("%s: Enter", __func__);
    75. structUartDevice*device = NULL;
    76. if(host == NULL){
    77. HDF_LOGE("%s: invalid parameter", __func__);
    78. return HDF_ERR_INVALID_PARAM;
    79. }
    80. device =(structUartDevice*) 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. structUartHostMethod 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. constchar 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 快速上手!建议您下一步进入带屏摄像头产品开发的学习 。