一、程序介绍
本程序是基于OpenHarmony标准系统编写的平台驱动案例:UART
详细资料请参考官网:
二、基础知识
1、UART简介
UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter),是通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输。
两个UART设备的连接示意图如下,UART与其他模块一般用2线(图1)或4线(图2)相连,它们分别是:
- TX:发送数据端,和对端的RX相连。
- RX:接收数据端,和对端的TX相连。
- RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。
- CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。
UART的2线相连:
UART的4线相连:
UART通信之前,收发双方需要约定好一些参数:波特率、数据格式(起始位、数据位、校验位、停止位)等。通信过程中,UART通过TX发送给对端数据,通过RX接收对端发送的数据。当UART接收缓存达到预定的门限值时,RTS变为不可发送数据,对端的CTS检测到不可发送数据,则停止发送数据。
2、UART驱动开发
(1)UART驱动开发接口
为了保证上层在调用UART接口时能够正确的操作UART控制器,核心层在//drivers/hdf_core/framework/support/platform/include/uart/uart_core.h中定义了以下钩子函数,驱动适配者需要在适配层实现这些函数的具体功能,并与钩子函数挂接,从而完成适配层与核心层的交互。
UartHostMethod定义:
UartHostMethod结构体成员的回调函数功能说明:
(2)UART驱动开发步骤
UART模块适配HDF框架包含以下四个步骤:
- 实例化驱动入口。
- 配置属性文件。
- 实例化UART控制器对象。
- 驱动调试。
我们以///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驱动入口开发参考:
配置属性文件
完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个UART控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层UartDev成员的默认值或限制范围有密切关系,比如Uart设备号,需要在uart_config.hcs文件中增加对应的器件属性。
本次案例以rk3568为案例(即文件//vendor/lockzhiner/rk3568/hdf_config/khdf/device_info/device_info.hcs),添加deviceNode描述,具体修改如下:
uart_config.hcs 配置参考//vendor/lockzhiner/rk3568/hdf_config/khdf/platform/rk3568_uart_config.hcs,具体修改如下:
(3)实例化UART控制器对象
完成驱动入口注册之后,下一步就是以核心层UartDev对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化UartDev成员UartHostMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
(4)驱动调试
建议先在Linux下修改确认,再移植到OpenHarmony。
3、UART应用开发
UART模块应用比较广泛,主要用于实现设备之间的低速串行通信,例如输出打印信息,当然也可以外接各种模块,如GPS、蓝牙等。
(1)接口说明
UART模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/uart_if.h。
UART驱动API接口功能介绍如下所示:
UartOpen
在使用UART进行通信时,首先要调用UartOpen获取UART设备句柄,该函数会返回指定端口号的UART设备句柄。
UartOpen参数定义如下:
UartOpen返回值定义如下:
假设系统中的UART端口号为1,获取该UART设备句柄的示例如下:
UartSetBaud
在通信之前,需要设置UART的波特率。
UartSetBaud参数定义如下:
UartSetBaud返回值定义如下:
UartGetBaud
设置UART的波特率后,可以通过获取波特率接口来查看UART当前的波特率。
UartGetBaud参数定义如下:
UartGetBaud返回值定义如下:
UartSetAttribute
在通信之前,需要设置UART的设备属性。
UartSetAttribute参数定义如下:
UartGetAttribute返回值定义如下:
UartGetAttribute
设置UART的设备属性后,可以通过获取设备属性接口来查看UART当前的设备属性。
UartGetAttribute参数定义如下:
UartGetAttribute返回值定义如下:
UartSetTransMode
在通信之前,需要设置UART的传输模式。
UartSetTransMode参数定义如下:
UartSetTransMode返回值定义如下:
UartWrite
向UART设备写入指定长度的数据。
UartWrite参数定义如下:
UartWrite返回值定义如下:
UartRead
从UART设备中读取指定长度的数据。
UartRead参数定义如下:
UartRead返回值定义如下:
UartClose
UART通信完成之后,需要销毁UART设备句柄。
UartClose参数定义如下:
(2)开发流程
使用UART的一般流程如下图所示:
三、程序解析
1、准备工作
查看《凌蒙派-RK3568开发板_排针说明表_》(即Git仓库的//docs/board/凌蒙派-RK3568开发板_排针说明表_v1.0.xlsx),具体如下:
2、Linux内核解析
(1)创建Linux内核Git
请参考《OpenHarmony如何为内核打patch》(即Git仓库的//docs/OpenHarmony如何为内核打patch.docx)。
(2)修改设备树PWM7配置
修改//arch/arm64/boot/dts/rockchip/rk3568-lockzhiner-x0.dtsi(即该目录是指已打Patch后的Linux内核,不是OpenHarmony主目录),具体如下所示:
(3)创建内核patch
请参考《OpenHarmony如何为内核打patch》(即Git仓库的//docs/OpenHarmony如何为内核打patch.docx)。
(4)替换OpenHarmony的内核patch
将制作出的kernel.patch替换到//kernel/linux/patches/linux-5.10/rk3568_patch/kernel.patch即可。
3、OpenHarmony配置树配置
(1)device_info.hcs
//vendor/lockzhiner/rk3568/hdf_config/khdf/device_info/device_info.hcs已定义好,具体如下:
注意:
- device2是我们新增的设备节点,给uart5使用。
- policy必须为2,表示对内核态和用户态提供服务。否则,应用程序无法调用。
- HDF_PLATFORM_UART_2,后面跟着的数据“2”,是UartOpen()的端口号。
- HDF_PLATFORM_UART_2,后面跟着的数据“2”,必须是递增的。
(2)rk3568_uart_config.hcs
//vendor/lockzhiner/rk3568/hdf_config/khdf/platform/rk3568_uart_config.hcs,具体内容如下:
注意:
- device_uart_0x0002是新增的,为uart5准备的。
- match_attr的名称必须是rockchip_rk3568_uart_2。
4、OpenHarmony UART平台驱动
在//drivers/hdf_core/adapter/khdf/linux/platform/uart/uart_adapter.c已编写对接Linux PWM驱动的相关代码,具体内容如下:
该部分代码不细述,感兴趣的读者可以去详读。
5、应用程序
(1)uart_test.c
uart相关头文件如下所示:
主函数定义UART接口调用,具体如下:
(2)BUILD.gn
编写应用程序的BUILD.gn,具体内容如下:
(3)参与应用程序编译
编辑//vendor/lockzhiner/rk3568/samples/BUILD.gn,开启编译选项。具体如下:
四、程序编译
建议使用docker编译方法,运行如下:
五、运行结果
运行如下:
注意:
- rbuff获取的时候可能为空。因为本次案例是基于非阻塞,电脑端发送的串口可能没有获取到数据。
建议: - 读者可以尝试使用堵塞方式,再测试看看。