一、前言
本文主要介绍hi3516上串口的驱动配置和测试,以及主体驱动代码的跟踪。所涉及的驱动程序源码位于vendor/huawei/hdf/sample目录。
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/src/uart_sample.c
二、参考文档:
$ vi ~/harmony/sdk/docs/docs-en/quick-start/developing-the-first-driver-running-on-hi3516.md
$ vi ~/harmony/sdk/docs/docs-en/guide/developing-the-first-driver-program-running-on-the-hi3516-development-board.md
三、驱动修改和跟踪
修改HDF框架的驱动配置文件:
$ vi ~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/uart/uart_config.hcs
修改HDF框架的设备配置文件
$ vi ~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs
基于HDF框架注册UART驱动的入口HdfDriverEntry,代码如下:
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/src/uart_sample.c
// 修改HDF框架的驱动配置文件
$ vi ~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/uart/uart_config.hcs
root {
platform {
+++
// 平台配置:串口控制器参数配置
uart_sample {
num = 5;
base = 0x120a0000; // UART base register address
irqNum = 38;
baudrate = 115200;
uartClk = 24000000; // 24 M
wlen = 0x60; // 8 bit width
parity = 0;
stopBit = 0;
match_attr = "sample_uart_5"; // uart_config.hcs和device_info.hcs通过deviceMatchAttr匹配
}
+++
}
}
// 修改HDF框架的设备配置文件
$ vi ~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs
root {
device_info {
platform :: host {
hostName = "platform_host";
priority = 50;
device_uart :: device {
+++
// 描述设备节点信息
device5 :: deviceNode {
policy = 2;
priority = 10;
permission = 0660;
moduleName = "UART_SAMPLE"; // 设备和驱动源码uart_sample.c通过moduleName匹配
serviceName = "HDF_PLATFORM_UART_5";
deviceMatchAttr = "sample_uart_5"; // uart_config.hcs和device_info.hcs通过deviceMatchAttr匹配
}
+++
}
}
}
}
// 系统启动过程中设备和驱动的加载(绑定)
$ vi ~/harmony/sdk/drivers/hdf/frameworks/core/host/src/hdf_driver_loader.c
static struct HdfDeviceNode *HdfDriverLoaderLoadNode(struct IDriverLoader *loader, const struct HdfDeviceInfo *deviceInfo)
struct HdfDriverEntry *driverEntry = NULL;
struct HdfDeviceNode *devNode = NULL;
driverEntry = loader->GetDriverEntry(deviceInfo);
devNode = HdfDeviceNodeNewInstance();
devNode->deviceObject.property = HcsGetNodeByMatchAttr(HcsGetRootNode(), deviceInfo->deviceMatchAttr);
if (driverEntry->Bind(&devNode->deviceObject) != 0) { // 调用uart_sample.c中的Bind = HdfUartSampleBind函数
HDF_LOGE("bind driver faiLED");
HdfDeviceNodeFreeInstance(devNode);
}
// 串口驱动源码主入口!!!
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/src/uart_sample.c
/* HdfDriverEntry definitions */
struct HdfDriverEntry g_hdfUartSample = {
.moduleVersion = 1,
.moduleName = "UART_SAMPLE",
// $ vi ~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs
// 与描述设备节点信息的device5 :: deviceNode 中的 moduleName = "UART_SAMPLE"一致。
.Bind = HdfUartSampleBind, // 驱动的绑定
.Init = HdfUartSampleInit, // 驱动的初始化
.Release = HdfUartSampleRelease, // 驱动的释放
};
//
// Initialize HdfDriverEntry
HDF_INIT(g_hdfUartSample); // .Init对应的函数可能是在这里面做了统一处理,
// 这是一段带有特殊属性HDF_SECTION的代码段,
// 编译器应该会将驱动操作函数放到特定的代码段,
// 以便启动的时候依次初始化所有设备的驱动。
// Registers the driver with the HDF
// $ vi ~/harmony/sdk/drivers/hdf/frameworks/include/core/hdf_device_desc.h
#define HDF_INIT(module) HDF_DRIVER_INIT(module)
// $ vi ~/harmony/sdk/drivers/hdf/lite/include/host/hdf_device_section.h
#define HDF_DRIVER_INIT(module) const size_t USED_ATTR module##HdfEntry HDF_SECTION = (size_t)(&(module))
// 绑定UART驱动接口到HDF框架
static int32_t HdfUartSampleBind(struct HdfDeviceObject *device)
UartHostCreate(device)
// 初始化UART
static int32_t HdfUartSampleInit(struct HdfDeviceObject *device)
host = UartHostFromDevice(device);
ret = SampleAttach(host, device); // 将UART驱动的配置和接口附加到HDF驱动框架
uartDevice = (struct UartDevice *)OsalMemCalloc(sizeof(struct UartDevice)); // 为串口设备创建新的资源空间
SampleDispatchConstruct(uartDevice);
// 从UART驱动的HCS中获取平台配置信息:串口控制器参数配置
ret = UartDeviceGetResource(uartDevice, device->property);
dri = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
dri->GetUint32(resourceNode, "num", &resource->num, 0)
dri->GetUint32(resourceNode, "base", &resource->base, 0)
dri->GetUint32(resourceNode, "irqNum", &resource->irqNum, 0)
dri->GetUint32(resourceNode, "baudrate", &resource->baudrate, 0)
dri->GetUint32(resourceNode, "wlen", &resource->wlen, 0)
dri->GetUint32(resourceNode, "parity", &resource->parity, 0)
dri->GetUint32(resourceNode, "stopBit", &resource->stopBit, 0)
dri->GetUint32(resourceNode, "uartClk", &resource->uartClk, 0)
UartSampleAddDev(host);
UartDeviceInit(uartDevice);
struct UartResource *resource = &device->resource;
struct UartRegisterMap *regMap = (struct UartRegisterMap *)resource->physBase; // 获取物理寄存器地址,以便后续读写和设置
/* Updating the system clock */
device->uartClk = resource->uartClk;
/* clear and reset registers. */
UartPl011ResetRegisters(regMap); // 复位寄存器
/* set baud rate as device config */
err = UartPl011SetBaudrate(regMap, resource->uartClk, resource->baudrate); // 设置寄存器
/* set the data format as device config */
UartPl011SetDataFormat(regMap, resource->wlen, resource->parity, resource->stopBit);
/* Enabling the FIFOs */
BuffeRFifoInit(&device->rxFifo, g_fifoBuffer, UART_RX_FIFO_SIZE);
device->state = UART_DEVICE_INITIALIZED; // 设置初始化完成标志位
host->method = &g_uartSampleHostMethod; // 注册串口驱动接口,给用户层提供操作接口
.Init = SampleInit,
.Deinit = SampleDeinit,
.Read = NULL,
.Write = SampleWrite,
device = (struct UartDevice *)host->priv; // 通过私有数据获得设备接口,和linux很像
regMap = (struct UartRegisterMap *)device->resource.physBase; // 获取串口的物理寄存器基地址
UartPl011Write(regMap, data[idx]); // 操作串口的底层接口
.SetBaud = SampleSetBaud,
device = (struct UartDevice *)host->priv;
regMap = (struct UartRegisterMap *)device->resource.physBase;
err = UartPl011SetBaudrate(regMap, device->uartClk, baudRate);
device->baudrate = baudRate;
.GetBaud = SampleGetBaud,
device = (struct UartDevice *)host->priv;
*baudRate = device->baudrate;
.SetAttribute = NULL,
.GetAttribute = NULL,
.SetTransMode = NULL,
// 释放UART
static void HdfUartSampleRelease(struct HdfDeviceObject *device)
host = UartHostFromDevice(device);
SampleDetach(host);
UartDeviceDeinit(uartDevice); // 解绑并释放UART驱动
while (UartPl011IsBusy(regMap));
UartPl011ResetRegisters(regMap);
uart_clk_cfg(0, false);
OsalIoUnmap((void *)device->resource.physBase);
device->state = UART_DEVICE_UNINITIALIZED; // 设置设备释放完成标志位
(void)OsalMemFree(uartDevice);
UartHostDestroy(host);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
目前SDK代码已经和官方文档里的代码不太一样了,主要是函数名称变了,大部分底层接口的函数名没有变化。通过跟踪串口驱动源码,可以发现harmony系统的很多框架和方法(驱动和设备树匹配,数据的传递等)都是参考Linux来设计的,不过具体的细节是不一样的。
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/include/uart_pl011_sample.h
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/src/uart_pl011_sample.c
$ vi ~/harmony/sdk/drivers/hdf/frameworks/support/platform/src/uart_core.c
$ vi ~/harmony/sdk/drivers/hdf/frameworks/support/platform/include/uart_core.h
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/src/uart_dev_sample.c
// 添加串口设备驱动
void UartSampleAddDev(struct UartHost *host)
UartSampleAddRemoveDev(host, true); // (struct UartHost *host, bool add)
devName = (char *)OsalMemCalloc(sizeof(char) * (MAX_DEV_NAME_SIZE + 1));
ret = snprintf_s(devName, MAX_DEV_NAME_SIZE + 1, MAX_DEV_NAME_SIZE, "/dev/uartdev-%d", host->num);
if (add) {
register_driver(devName, &g_uartSampleDevFops, HDF_UART_FS_MODE, host)
} else {
unregister_driver(devName)
}
OsalMemFree(devName);
// 提供给用户空间的统一系统调用接口
const struct file_operations_vfs g_uartSampleDevFops = {
.open = UartSampleDevOpen,
.close = UartSampleRelease,
.read = UartSampleRead,
.write = UartSampleWrite,
.ioctl = UartSampleDevIoctl,
};
static int32_t UartSampleDevOpen(FAR struct file *filep)
inode = (struct inode *)filep->f_inode;
host = (struct UartHost *)inode->i_private;
static int32_t UartSampleRelease(FAR struct file *filep)
inode = (struct inode *)filep->f_inode;
host = (struct UartHost *)inode->i_private;
static ssize_t UartSampleRead(FAR struct file *filep, FAR char *buf, size_t count)
inode = (struct inode *)filep->f_inode;
host = (struct UartHost *)inode->i_private;
if (LOS_IsUserAddressRange((vaddr_t)buf, count)) {
tmpBuf = (uint8_t *)OsalMemCalloc(count);
ret = UartHostRead(host, tmpBuf, count);
ret = LOS_ArchCopyToUser(buf, tmpBuf, count);
} else {
return UartHostRead(host, (uint8_t *)buf, count);
}
}
static ssize_t UartSampleWrite(struct file *filep, const char *buf, size_t count)
inode = (struct inode *)filep->f_inode;
host = (struct UartHost *)inode->i_private;
if (LOS_IsUserAddressRange((vaddr_t)buf, count)) {
tmpBuf = (uint8_t *)OsalMemCalloc(count);
ret = LOS_ArchCopyFromUser(tmpBuf, buf, count);
ret = UartHostWrite(host, tmpBuf, count);
} else {
return UartHostWrite(host, (uint8_t *)buf, count);
}
static int32_t UartSampleDevIoctl(FAR struct file *filep, int32_t cmd, unsigned long arg)
inode = (struct inode *)filep->f_inode;
host = (struct UartHost *)inode->i_private;
switch (cmd) {
case UART_CFG_BAUDRATE:
ret = UartHostSetBaud(host, arg);
default:
ret = HDF_ERR_NOT_SUPPORT;
}
// 下面三个函数就是之前在uart_sample.c中注册的g_uartSampleHostMethod提供的具体的函数
// $ vi ~/harmony/sdk/drivers/hdf/frameworks/support/platform/include/uart_core.h
static inline int32_t UartHostRead(struct UartHost *host, uint8_t *data, uint32_t size)
return host->method->Read(host, data, size);
static inline int32_t UartHostWrite(struct UartHost *host, uint8_t *data, uint32_t size)
return host->method->Write(host, data, size);
static inline int32_t UartHostSetBaud(struct UartHost *host, uint32_t baudRate)
return host->method->SetBaud(host, baudRate);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
$ vi ~/harmony/sdk/third_party/NuttX/fs/driver/fs_registerdriver.c
$ vi ~/harmony/sdk/third_party/NuttX/fs/inode/fs_inode.c
$ vi ~/harmony/sdk/third_party/NuttX/fs/inode/fs_inodereserve.c
在编译脚本中增加示例UART驱动模块
$ vi ~/harmony/sdk/vendor/huawei/hdf/hdf_vendor.mk
四、用户程序和驱动交互代码
UART驱动成功初始化后,会创建/dev/uartdev-5设备节点,通过设备节点与UART驱动交互的代码如下:
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/dev/hello_uart_dev.c
用户空间的应用开发和Linux基本一样。
在产品配置的hdf子系统下增加hello_uart_sample组件:
$ vi ~/harmony/sdk/build/lite/product/ipcamera_hi3516dv300.json
如上代码均为示例代码,完整代码可以在vendor/huawei/hdf/sample查看。示例代码默认不参与编译,需要手动添加到编译脚本中。
五、小结
总共修改六个部分代码(驱动和应用示例由官方写好了,主要添加四项配置):
驱动源码:~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/src/uart_sample.c
HDF框架驱动配置:~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/uart/uart_config.hcs
HDF框架设备配置:~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs
驱动模块编译配置:~/harmony/sdk/vendor/huawei/hdf/hdf_vendor.mk
应用源码:~/harmony/sdk/vendor/huawei/hdf/sample/platform/uart/dev/hello_uart_dev.c
应用配置:~/harmony/sdk/build/lite/product/ipcamera_hi3516dv300.json
六 、编译
$ ./build.sh
拷贝到Windows共享目录中:
$ rm /mnt/hgfs/proj-harmony/images/out/ -rf
$ cp -rf out/ /mnt/hgfs/proj-harmony/images/
七 、烧录
下面烧写新增了uart5驱动和串口测试应用的系统:

hisilicon # setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x3000; go 0x80000000";
hisilicon # setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=7M rootsize=15M rw";
hisilicon # saveenv
hisilicon # reset
重启后进入系统。
八、测试
在根目录下,在命令行输入指令“./bin/hello_uart”执行写入的demo程序,显示成功结果如下所示:
本文结束,感谢您的阅读!