• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    >![](../public_sys-resources/icon-note.gif) **说明:**
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
359device/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
403build/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    >![](../public_sys-resources/icon-note.gif) **说明:**
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    >![](../public_sys-resources/icon-notice.gif) **须知:**
433    >若无法连接串口,请参考[常见问题](quickstart-lite-steps-hi3516-faqs.md#section14871149155911)进行排查。
434
435    **图 1**  连接串口图<a name="fig124315964718"></a>
436    ![](figure/连接串口图-4.png "连接串口图-4")
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    >![](../public_sys-resources/icon-notice.gif) **须知:**
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    >![](../public_sys-resources/icon-notice.gif) **须知:**
481    >**“go 0x80000000”**为可选指令,默认配置已将该指令固化在启动参数中,单板复位后可自动启动。若想切换为手动启动,可在U-boot启动倒数阶段使用"回车"打断自动启动。
482
4833.  输入**“reset”**指令并回车,重启单板,启动成功如下图,输入回车串口显示OHOS字样。
484
485    **图 2**  系统启动图<a name="fig14618415485"></a>
486    ![](figure/系统启动图-5.png "系统启动图-5")
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