本程序是基于OpenHarmony标准系统编写的平台驱动案例:UART
详细资料请参考官网:
UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter),是通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输。
两个UART设备的连接示意图如下,UART与其他模块一般用2线(图1)或4线(图2)相连,它们分别是:
图1 UART的2线相连
图2 UART的4线相连
UART通信之前,收发双方需要约定好一些参数:波特率、数据格式(起始位、数据位、校验位、停止位)等。通信过程中,UART通过TX发送给对端数据,通过RX接收对端发送的数据。当UART接收缓存达到预定的门限值时,RTS变为不可发送数据,对端的CTS检测到不可发送数据,则停止发送数据。
为了保证上层在调用UART接口时能够正确的操作UART控制器,核心层在//drivers/hdf_core/framework/support/platform/include/uart/uart_core.h中定义了以下钩子函数,驱动适配者需要在适配层实现这些函数的具体功能,并与钩子函数挂接,从而完成适配层与核心层的交互。
UartHostMethod定义:
struct UartHostMethod {
int32_t (*Init)(struct UartHost *host);
int32_t (*Deinit)(struct UartHost *host);
int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size);
int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size);
int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate);
int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate);
int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute);
int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute);
int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode);
int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table);
};
UartHostMethod结构体成员的回调函数功能说明:
函数 | 入参 | 出参 | 返回值 | 功能 |
---|---|---|---|---|
Init | host:结构体指针,核心层UART控制器 | 无 | HDF_STATUS相关状态 | 初始化Uart设备 |
Deinit | host:结构体指针,核心层UART控制器 | 无 | HDF_STATUS相关状态 | 去初始化Uart设备 |
Read | host:结构体指针,核心层UART控制器 size:uint32_t类型,接收数据大小 | data:uint8_t类型指针,接收的数据 | HDF_STATUS相关状态 | 接收数据RX |
Write | host:结构体指针,核心层UART控制器 data:uint8_t类型指针,传入数据 size:uint32_t类型,发送数据大小 | 无 | HDF_STATUS相关状态 | 发送数据TX |
SetBaud | host:结构体指针,核心层UART控制器 baudRate:uint32_t类型,波特率传入值 | 无 | HDF_STATUS相关状态 | 设置波特率 |
GetBaud | host:结构体指针,核心层UART控制器 | baudRate:uint32_t类型指针,传出的波特率 | HDF_STATUS相关状态 | 获取当前设置的波特率 |
GetAttribute | host:结构体指针,核心层UART控制器 | attribute:结构体指针,传出的属性值(见uart_if.h中UartAttribute定义) | HDF_STATUS相关状态 | 获取设备uart相关属性 |
SetAttribute | host:结构体指针,核心层UART控制器 attribute:结构体指针,属性传入值 | 无 | HDF_STATUS相关状态 | 设置设备UART相关属性 |
SetTransMode | host:结构体指针,核心层UART控制器 mode:枚举值(见uart_if.h中UartTransMode定义),传输模式 | 无 | HDF_STATUS相关状态 | 设置传输模式 |
PollEvent | host:结构体指针,核心层UART控制器 filep:void类型指针file table:void类型指针table | 无 | HDF_STATUS相关状态 | poll轮询机制 |
UART模块适配HDF框架包含以下四个步骤:
我们以///drivers/hdf_core/adapter/khdf/linux/platform/uart/uart_adapter.c为例(该UART驱动是建立于Linux UART子系统基础上创建)。
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
UART驱动入口开发参考:
struct HdfDriverEntry g_hdfUartchdog = {
.moduleVersion = 1,
.moduleName = "HDF_PLATFORM_UART",
.Bind = HdfUartBind,
.Init = HdfUartInit,
.Release = HdfUartRelease,
};
HDF_INIT(g_hdfUartchdog);
完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个UART控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层UartDev成员的默认值或限制范围有密切关系,比如Uart设备号,需要在uart_config.hcs文件中增加对应的器件属性。
本次案例以rk3568为案例(即文件//vendor/lockzhiner/lmpi_rk3568/hdf_config/khdf/device_info/device_info.hcs),添加deviceNode描述,具体修改如下:
device_uart :: device {
device0 :: deviceNode {
policy = 1; // 驱动服务发布的策略,policy大于等于1(用户态可见为2,仅内核态可见为1)
priority = 40; // 驱动启动优先级
permission = 0644; // 驱动创建设备节点权限
moduleName = "HDF_PLATFORM_UART"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
serviceName = "HDF_PLATFORM_UART_0"; // 驱动对外发布服务的名称,必须唯一,必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号
deviceMatchAttr = "rockchip_rk3568_uart_0"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致
}
device1 :: deviceNode {
policy = 2;
permission = 0644;
priority = 40;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_1";
deviceMatchAttr = "rockchip_rk3568_uart_1";
}
}
uart_config.hcs 配置参考//vendor/lockzhiner/lmpi_rk3568/hdf_config/khdf/platform/rk3568_uart_config.hcs,具体修改如下:
root {
platform {
uart_config {
template uart_device {
serviceName = "";
match_attr = "";
driver_name = "ttyS";
num = 0;
}
device_uart_0x0000 :: uart_device {
match_attr = "rockchip_rk3568_uart_0";
}
device_uart_0x0001 :: uart_device {
num = 1; // 序号,是Linux的/dev/ttySXXX,XXX即是num
match_attr = "rockchip_rk3568_uart_1"; // 必须和device_info.hcs中对应的设备的deviceMatchAttr值一致
// 注意rockchip_rk3568_uart_XX,XX表示的OH的UART序号,必须从0开始,顺序递增
}
}
}
}
完成驱动入口注册之后,下一步就是以核心层UartDev对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化UartDev成员UartHostMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
// 定义UartHostMethod成员g_uartHostMethod,实现uart相应接口
static struct UartHostMethod g_uartHostMethod = {
.Init = UartAdapterInit,
.Deinit = UartAdapterDeInit,
.Read = UartAdapterRead,
.Write = UartAdapterWrite,
.SetBaud = UartAdapterSetBaud,
.GetBaud = UartAdapterGetBaud,
.SetAttribute = UartAdapterSetAttribute,
.GetAttribute = UartAdapterGetAttribute,
.SetTransMode = UartAdapterSetTransMode,
};
static int32_t HdfUartBind(struct HdfDeviceObject *obj);
static int32_t HdfUartInit(struct HdfDeviceObject *obj);
static void HdfUartRelease(struct HdfDeviceObject *obj);
struct HdfDriverEntry g_hdfUartchdog = {
.moduleVersion = 1,
.moduleName = "HDF_PLATFORM_UART",
.Bind = HdfUartBind,
.Init = HdfUartInit,
.Release = HdfUartRelease,
};
HDF_INIT(g_hdfUartchdog);
建议先在Linux下修改确认,再移植到OpenHarmony。
UART模块应用比较广泛,主要用于实现设备之间的低速串行通信,例如输出打印信息,当然也可以外接各种模块,如GPS、蓝牙等。
UART模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/uart_if.h。
UART驱动API接口功能介绍如下所示:
接口名 | 接口描述 |
---|---|
DevHandle UartOpen(uint32_t port) | UART获取设备句柄 |
void UartClose(DevHandle handle) | UART释放设备句柄 |
int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size) | 从UART设备中读取指定长度的数据 |
int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size) | 向UART设备中写入指定长度的数据 |
int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate) | UART获取波特率 |
int32_t UartSetBaud(DevHandle handle, uint32_t baudRate) | UART设置波特率 |
int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute) | UART获取设备属性 |
int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute) | UART设置设备属性 |
int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode) | UART设置传输模式 |
(1)UartOpen
在使用UART进行通信时,首先要调用UartOpen获取UART设备句柄,该函数会返回指定端口号的UART设备句柄。
DevHandle UartOpen(uint32_t port);
UartOpen参数定义如下:
参数 | 参数描述 |
---|---|
port | UART设备号 |
UartOpen返回值定义如下:
返回值 | 返回值描述 |
---|---|
NULL | 获取UART设备句柄失败 |
设备句柄 | UART设备句柄 |
假设系统中的UART端口号为1,获取该UART设备句柄的示例如下:
DevHandle handle = NULL; // UART设备句柄
uint32_t port = 1; // UART设备端口号
handle = UartOpen(port);
if (handle == NULL) {
HDF_LOGE("UartOpen: open uart_%u failed!\n", port);
return;
}
(2)UartSetBaud
在通信之前,需要设置UART的波特率。
int32_t UartSetBaud(DevHandle handle, uint32_t baudRate);
UartSetBaud参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
baudRate | 待设置的波特率值 |
UartSetBaud返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART设置波特率成功 |
负数 | UART设置波特率失败 |
(3)UartGetBaud
设置UART的波特率后,可以通过获取波特率接口来查看UART当前的波特率。
int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate);
UartGetBaud参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
baudRate | 接收波特率值的指针 |
UartGetBaud返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART获取波特率成功 |
负数 | UART获取波特率失败 |
(4)UartSetAttribute
在通信之前,需要设置UART的设备属性。
int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute);
UartSetAttribute参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
attribute | 待设置的设备属性 |
UartSetAttribute返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART设置设备属性成功 |
负数 | UART设置设备属性失败 |
(5)UartGetAttribute
设置UART的设备属性后,可以通过获取设备属性接口来查看UART当前的设备属性。
int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute);
UartGetAttribute参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
attribute | 接收UART设备属性的指针 |
UartGetAttribute返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART获取设备属性成功 |
负数 | UART获取设备属性失败 |
(6)UartSetTransMode
在通信之前,需要设置UART的传输模式。
int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode);
UartSetTransMode参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
mode | 待设置的传输模式 |
UartSetTransMode返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART设置传输模式成功 |
负数 | UART设置传输模式失败 |
(7)UartWrite
向UART设备写入指定长度的数据。
int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size);
UartWrite参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
data | 待写入数据的指针 |
size | 待写入数据的长度 |
UartWrite返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART写数据成功 |
负数 | UART写数据失败 |
(8)UartRead
从UART设备中读取指定长度的数据。
int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size);
UartRead参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
data | 接收读取数据的指针 |
size | 待读取数据的长度 |
UartRead返回值定义如下:
返回值 | 返回值描述 |
---|---|
非负数 | UART读取到的数据长度 |
负数 | UART读取数据失败 |
(9)UartClose
UART通信完成之后,需要销毁UART设备句柄。
void UartClose(DevHandle handle);
UartClose参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
使用UART的一般流程如下图所示:
查看《凌蒙派-RK3568开发板_排针说明表_》(即Git仓库的//docs/board/凌蒙派-RK3568开发板_排针说明表_v1.0.xlsx),具体如下:
排针名称 | GPIO引脚 | 复用功能 |
---|---|---|
3_C3 | GPIO3_C3 | UART5_TX_M1 |
3_C2 | GPIO3_C2 | UART5_RX_M1 |
请参考《OpenHarmony如何为内核打patch》(即Git仓库的//docs/OpenHarmony如何为内核打patch.docx)。
修改//arch/arm64/boot/dts/rockchip/rk3568-lockzhiner-x0.dtsi(即该目录是指已打Patch后的Linux内核,不是OpenHarmony主目录),具体如下所示:
&uart5 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart5m1_xfer>;
};
请参考《OpenHarmony如何为内核打patch》(即Git仓库的//docs/OpenHarmony如何为内核打patch.docx)。
将制作出的kernel.patch替换到//kernel/linux/patches/linux-5.10/rk3568_patch/kernel.patch即可。
//vendor/lockzhiner/lmpi_rk3568/hdf_config/khdf/device_info/device_info.hcs已定义好,具体如下:
device_uart :: device {
device0 :: deviceNode {
policy = 1;
priority = 40;
permission = 0644;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_0";
deviceMatchAttr = "rockchip_rk3568_uart_0";
}
device1 :: deviceNode {
policy = 2;
permission = 0644;
priority = 40;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_1";
deviceMatchAttr = "rockchip_rk3568_uart_1";
}
device2 :: deviceNode {
policy = 2;
permission = 0644;
priority = 40;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_2";
deviceMatchAttr = "rockchip_rk3568_uart_2";
}
}
注意:
//vendor/lockzhiner/lmpi_rk3568/hdf_config/khdf/platform/rk3568_uart_config.hcs,具体内容如下:
root {
platform {
uart_config {
template uart_device {
serviceName = "";
match_attr = "";
driver_name = "ttyS";
num = 0;
}
device_uart_0x0000 :: uart_device {
match_attr = "rockchip_rk3568_uart_0";
}
device_uart_0x0001 :: uart_device {
num = 1;
match_attr = "rockchip_rk3568_uart_1";
}
device_uart_0x0002 :: uart_device {
num = 5;
match_attr = "rockchip_rk3568_uart_2";
}
}
}
}
注意:
在//drivers/hdf_core/adapter/khdf/linux/platform/uart/uart_adapter.c已编写对接Linux UART驱动的相关代码,具体内容如下:
struct HdfDriverEntry g_hdfUartchdog = {
.moduleVersion = 1,
.moduleName = "HDF_PLATFORM_UART",
.Bind = HdfUartBind,
.Init = HdfUartInit,
.Release = HdfUartRelease,
};
HDF_INIT(g_hdfUartchdog);
该部分代码不细述,感兴趣的读者可以去详读。
uart相关头文件如下所示:
#include "uart_if.h" // UART标准接口头文件
主函数定义UART接口调用,具体如下:
int main(int argc, char* argv[])
{
DevHandle handle = NULL;
struct UartAttribute attribute;
int32_t ret = 0;
uint8_t wbuff[STRING_MAXSIZE] = "HelloWorld";
uint8_t rbuff[STRING_MAXSIZE] = { 0 };
......
attribute.dataBits = UART_ATTR_DATABIT_8; // UART传输数据位宽,一次传输7个bit
attribute.parity = UART_ATTR_PARITY_NONE; // UART传输数据无校检
attribute.stopBits = UART_ATTR_STOPBIT_1; // UART传输数据停止位为1位
attribute.rts = UART_ATTR_RTS_DIS; // UART禁用RTS
attribute.cts = UART_ATTR_CTS_DIS; // UART禁用CTS
attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; // UART使能RX FIFO
attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; // UART使能TX FIFO
handle = UartOpen(m_uart_port);
if (handle == NULL) {
PRINT_ERROR("UartOpen: open uart port %u failed!\n", m_uart_port);
return -1;
}
PRINT_INFO("UartOpen successful and uart port = %d\n", m_uart_port);
// 设置UART波特率
ret = UartSetBaud(handle, m_uart_baudrate);
if (ret != HDF_SUCCESS) {
PRINT_ERROR("UartSetBaud: set baud failed, ret %d\n", ret);
goto ERR;
}
PRINT_INFO("UartSetBaud successful and uart baudrate = %d\n", m_uart_baudrate);
// 设置UART设备属性
ret = UartSetAttribute(handle, &attribute);
if (ret != HDF_SUCCESS) {
PRINT_ERROR("UartSetAttribute: set attribute failed, ret %d\n", ret);
goto ERR;
}
PRINT_INFO("UartSetAttribute successful\n");
// 获取UART设备属性
ret = UartGetAttribute(handle, &attribute);
if (ret != HDF_SUCCESS) {
PRINT_ERROR("UartGetAttribute: get attribute failed, ret %d\n", ret);
goto ERR;
}
PRINT_INFO("UartGetAttribute successful\n");
// 设置UART传输模式为非阻塞模式
ret = UartSetTransMode(handle, UART_MODE_RD_NONBLOCK);
if (ret != HDF_SUCCESS) {
PRINT_ERROR("UartSetTransMode: set trans mode failed, ret %d\n", ret);
goto ERR;
}
PRINT_INFO("UartSetTransMode successful\n");
// 向UART设备写入数据
ret = UartWrite(handle, wbuff, (uint32_t)strlen((char *)wbuff));
if (ret != HDF_SUCCESS) {
PRINT_ERROR("UartWrite: write data failed, ret %d\n", ret);
goto ERR;
}
PRINT_INFO("UartWrite successful and wbuff = %s\n", wbuff);
// 从UART设备读取5字节的数据
ret = UartRead(handle, rbuff, STRING_MAXSIZE);
if (ret < 0) {
PRINT_ERROR("UartRead: read data failed, ret %d\n", ret);
goto ERR;
}
PRINT_INFO("UartRead successful and rbuff = %s\n", rbuff);
ERR:
// 销毁UART设备句柄
UartClose(handle);
return ret;
}
编写应用程序的BUILD.gn,具体内容如下:
import("//build/ohos.gni")
import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")
print("samples: compile rk3568_uart_test")
ohos_executable("rk3568_uart_test") {
sources = [ "uart_test.c" ]
include_dirs = [
"$hdf_framework_path/include",
"$hdf_framework_path/include/core",
"$hdf_framework_path/include/osal",
"$hdf_framework_path/include/platform",
"$hdf_framework_path/include/utils",
"$hdf_uhdf_path/osal/include",
"$hdf_uhdf_path/ipc/include",
"//base/hiviewdfx/hilog/interfaces/native/kits/include",
"//third_party/bounds_checking_function/include",
]
deps = [
"$hdf_uhdf_path/platform:libhdf_platform",
"$hdf_uhdf_path/utils:libhdf_utils",
"//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog",
]
cflags = [
"-Wall",
"-Wextra",
"-Werror",
"-Wno-format",
"-Wno-format-extra-args",
]
part_name = "product_lmpi_rk3568"
install_enable = true
}
编辑//vendor/lockzhiner/lmpi_rk3568/samples/BUILD.gn,开启编译选项。具体如下:
"b08_platform_device_uart/app:rk3568_uart_test",
编辑//build/compile_standard_whitelist.json,添加本程序编译。具体如下:
"external_deps_bundle_not_add": [
......
"//vendor/lockzhiner/lmpi_rk3568/samples/b08_platform_device_uart/app:rk3568_uart_test",
......
],
建议使用docker编译方法,运行如下:
hb set -root .
hb set
# 选择lockzhiner下的lmpi_rk3568编译分支。
hb build -f
运行如下:
# rk3568_uart_test -b 115200 -p 2
uart port: 2
uart baud rate: 115200
../../vendor/lockzhiner/rk3568/samples/b08_platform_device_uart/app/uart_test.c, main, 127, info: UartOpen successful and uart port = 2
../../vendor/lockzhiner/rk3568/samples/b08_platform_device_uart/app/uart_test.c, main, 135, info: UartSetBaud successful and uart baudrate = 115200
../../vendor/lockzhiner/rk3568/samples/b08_platform_device_uart/app/uart_test.c, main, 143, info: UartSetAttribute successful
../../vendor/lockzhiner/rk3568/samples/b08_platform_device_uart/app/uart_test.c, main, 151, info: UartGetAttribute successful
../../vendor/lockzhiner/rk3568/samples/b08_platform_device_uart/app/uart_test.c, main, 159, info: UartSetTransMode successful
../../vendor/lockzhiner/rk3568/samples/b08_platform_device_uart/app/uart_test.c, main, 167, info: UartWrite successful and wbuff = HelloWorld
../../vendor/lockzhiner/rk3568/samples/b08_platform_device_uart/app/uart_test.c, main, 175, info: UartRead successful and rbuff =
#
注意:
建议:
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )