当前位置: 首页 > news >正文

RK3568DAYU开发板-平台驱动开发--UART

1、程序介绍

本程序是基于OpenHarmony标准系统编写的平台驱动案例:UART

系统版本:openharmony5.0.0

开发板:dayu200

编译环境:ubuntu22

部署路径: //sample/06_platform_uart

2、基础知识

2.1、UART简介

UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter),是通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输。

两个UART设备的连接示意图如下,UART与其他模块一般用2线(图1)或4线(图2)相连,它们分别是:

  • TX:发送数据端,和对端的RX相连。
  • RX:接收数据端,和对端的TX相连。
  • RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。
  • CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。

图1 UART的2线相连

在这里插入图片描述

图2 UART的4线相连

在这里插入图片描述

UART通信之前,收发双方需要约定好一些参数:波特率、数据格式(起始位、数据位、校验位、停止位)等。通信过程中,UART通过TX发送给对端数据,通过RX接收对端发送的数据。当UART接收缓存达到预定的门限值时,RTS变为不可发送数据,对端的CTS检测到不可发送数据,则停止发送数据。

2.2、UART驱动开发

2.2.1、UART驱动开发接口

为了保证上层在调用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结构体成员的回调函数功能说明:

函数入参出参返回值功能
Inithost:结构体指针,核心层UART控制器HDF_STATUS相关状态初始化Uart设备
Deinithost:结构体指针,核心层UART控制器HDF_STATUS相关状态去初始化Uart设备
Readhost:结构体指针,核心层UART控制器 size:uint32_t类型,接收数据大小data:uint8_t类型指针,接收的数据HDF_STATUS相关状态接收数据RX
Writehost:结构体指针,核心层UART控制器 data:uint8_t类型指针,传入数据 size:uint32_t类型,发送数据大小HDF_STATUS相关状态发送数据TX
SetBaudhost:结构体指针,核心层UART控制器 baudRate:uint32_t类型,波特率传入值HDF_STATUS相关状态设置波特率
GetBaudhost:结构体指针,核心层UART控制器baudRate:uint32_t类型指针,传出的波特率HDF_STATUS相关状态获取当前设置的波特率
GetAttributehost:结构体指针,核心层UART控制器attribute:结构体指针,传出的属性值(见uart_if.h中UartAttribute定义)HDF_STATUS相关状态获取设备uart相关属性
SetAttributehost:结构体指针,核心层UART控制器 attribute:结构体指针,属性传入值HDF_STATUS相关状态设置设备UART相关属性
SetTransModehost:结构体指针,核心层UART控制器 mode:枚举值(见uart_if.h中UartTransMode定义),传输模式HDF_STATUS相关状态设置传输模式
PollEventhost:结构体指针,核心层UART控制器 filep:void类型指针file table:void类型指针tableHDF_STATUS相关状态poll轮询机制
2.2.2、UART驱动开发步骤

UART模块适配HDF框架包含以下四个步骤:

  • 实例化驱动入口。
  • 配置属性文件。
  • 实例化UART控制器对象。
  • 驱动调试。

2.3、UART应用开发

UART模块应用比较广泛,主要用于实现设备之间的低速串行通信,例如输出打印信息,当然也可以外接各种模块,如GPS、蓝牙等。

2.3.1、接口说明

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参数定义如下:

参数参数描述
portUART设备号

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参数定义如下:

参数参数描述
handleUART设备句柄
baudRate待设置的波特率值

UartSetBaud返回值定义如下:

返回值返回值描述
HDF_SUCCESSUART设置波特率成功
负数UART设置波特率失败

(3)UartGetBaud

设置UART的波特率后,可以通过获取波特率接口来查看UART当前的波特率。

int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate);

UartGetBaud参数定义如下:

参数参数描述
handleUART设备句柄
baudRate接收波特率值的指针

UartGetBaud返回值定义如下:

返回值返回值描述
HDF_SUCCESSUART获取波特率成功
负数UART获取波特率失败

(4)UartSetAttribute

在通信之前,需要设置UART的设备属性。

int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute);

UartSetAttribute参数定义如下:

参数参数描述
handleUART设备句柄
attribute待设置的设备属性

UartSetAttribute返回值定义如下:

返回值返回值描述
HDF_SUCCESSUART设置设备属性成功
负数UART设置设备属性失败

(5)UartGetAttribute

设置UART的设备属性后,可以通过获取设备属性接口来查看UART当前的设备属性。

int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute);

UartGetAttribute参数定义如下:

参数参数描述
handleUART设备句柄
attribute接收UART设备属性的指针

UartGetAttribute返回值定义如下:

返回值返回值描述
HDF_SUCCESSUART获取设备属性成功
负数UART获取设备属性失败

(6)UartSetTransMode

在通信之前,需要设置UART的传输模式。

int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode);

UartSetTransMode参数定义如下:

参数参数描述
handleUART设备句柄
mode待设置的传输模式

UartSetTransMode返回值定义如下:

返回值返回值描述
HDF_SUCCESSUART设置传输模式成功
负数UART设置传输模式失败

(7)UartWrite

向UART设备写入指定长度的数据。

int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size);

UartWrite参数定义如下:

参数参数描述
handleUART设备句柄
data待写入数据的指针
size待写入数据的长度

UartWrite返回值定义如下:

返回值返回值描述
HDF_SUCCESSUART写数据成功
负数UART写数据失败

(8)UartRead

从UART设备中读取指定长度的数据。

int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size);

UartRead参数定义如下:

参数参数描述
handleUART设备句柄
data接收读取数据的指针
size待读取数据的长度

UartRead返回值定义如下:

返回值返回值描述
非负数UART读取到的数据长度
负数UART读取数据失败

(9)UartClose

UART通信完成之后,需要销毁UART设备句柄。

void UartClose(DevHandle handle);

UartClose参数定义如下:

参数参数描述
handleUART设备句柄
2.2.2、开发流程

使用UART的一般流程如下图所示:

在这里插入图片描述

3、程序解析

3.1、代码目录

在这里插入图片描述

3.2、接口流程梳理

  • UartOpen执行流程
//drivers\hdf_core\framework\support\platform\src\uart\uart_if.c
DevHandle UartOpen(uint32_t port)
|-->handle = UartGetObjGetByBusNum(port)|-->ret = snprintf_s(name, UART_HOST_NAME_LEN + 1, UART_HOST_NAME_LEN, "HDF_PLATFORM_UART_%u", num);|-->return (void *)DevSvcManagerClntGetService(name);//获取设备服务管理实例,属于hdf驱动框架的部分,drivers\hdf_core\framework\core\host\src\devsvc_manager_clnt.c
|-->ret = UartHostRequest((struct UartHost *)handle)|-->ret = host->method->Init(host);
|-->(DevHandle)handle;
  • UartHostRead执行流程
//drivers\hdf_core\framework\support\platform\include\uart\uart_core.h
static inline int32_t UartHostRead(struct UartHost *host, uint8_t *data, uint32_t size)
|-->return host->method->Read(host, data, size);

由上可见设备节点由全局变量g_i2cManager提供,此变量由下文中平台驱动linux_uart_adapter(drivers\hdf_core\adapter\khdf\linux\platform\uart\uart_adapter.c)进行设置。

3.3、OpenHarmony UART平台驱动

3.3.1、驱动实例化驱动入口

驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。

drivers/hdf_core/adapter/khdf/linux/platform/uart/uart_adapter.c
struct HdfDriverEntry g_hdfUartchdog = {.moduleVersion = 1,.moduleName = "HDF_PLATFORM_UART",.Bind = HdfUartBind,.Init = HdfUartInit,.Release = HdfUartRelease,
};
HDF_INIT(g_hdfUartchdog);
3.3.2、配置属性文件

完成驱动入口注册之后**,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关**。在//vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs中可以看到如下信息:

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";}
}
  • device2是我们新增的设备节点,给uart5使用。
  • policy必须为2,表示对内核态和用户态提供服务。否则,应用程序无法调用。
  • HDF_PLATFORM_UART_2,后面跟着的数据“2”,是UartOpen()的端口号。
  • HDF_PLATFORM_UART_2,后面跟着的数据“2”,必须是递增的。

如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层UartDev成员的默认值或限制范围有密切关系,比如Uart设备号,需要在uart_config.hcs文件中增加对应的器件属性

uart_config.hcs 配置参考//vendor/hihope/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即是nummatch_attr = "rockchip_rk3568_uart_1";	// 必须和device_info.hcs中对应的设备的deviceMatchAttr值一致// 注意rockchip_rk3568_uart_XX,XX表示的OH的UART序号,必须从0开始,顺序递增}}}
}
  • device_uart_0x0002是新增的,为uart5准备的。
  • match_attr的名称必须是rockchip_rk3568_uart_2。
  • UartOpen函数参数port,则表示上述uart设备排列序号。比如:uart num = 5的UartOpen函数port = 2。
3.3.3、实例化UART控制器对象

完成驱动入口注册之后,下一步就是以核心层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)|-->return (UartHostCreate(obj) == NULL) ? HDF_FAILURE : HDF_SUCCESS;//为uarthost分配内存并与device进行绑定,drivers\hdf_core\framework\support\platform\src\uart\uart_core.c|-->host = (struct UartHost *)OsalMemCalloc(sizeof(*host));|-->host->device = device;|-->device->service = &(host->service);//在上文提到的函数(DevSvcManagerClntGetService)返回,此为uarthost类的第一个成员函数,可强制类型指针转换。
static int32_t HdfUartInit(struct HdfDeviceObject *obj);|-->host = UartHostFromDevice(obj);//获取host->service,作为指针头|-->iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE)//获取设备信息的单实例|-->if (iface->GetUint32(obj->property, "num", &host->num, 0) != HDF_SUCCESS)//解析设备树配置文件
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);

3.5、应用程序

3.5.1、uart_test.c

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个bitattribute.parity = UART_ATTR_PARITY_NONE;   // UART传输数据无校检attribute.stopBits = UART_ATTR_STOPBIT_1;   // UART传输数据停止位为1位attribute.rts = UART_ATTR_RTS_DIS;          // UART禁用RTSattribute.cts = UART_ATTR_CTS_DIS;          // UART禁用CTSattribute.fifoRxEn = UART_ATTR_RX_FIFO_EN;  // UART使能RX FIFOattribute.fifoTxEn = UART_ATTR_TX_FIFO_EN;  // UART使能TX FIFOhandle = 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;
}
3.5.2、BUILD.gn

编写应用程序的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 = "rk3568_uart_test"install_enable = true
}
3.4.3、bundle.json

编写应用程序的bundle.json,具体内容如下:

{"name": "@ohos/uart_test","description": "rk3568_uart_test example.","version": "3.1","license": "Apache License 2.0","publishAs": "code-segment","segment": {"destPath": "sample/06_platform_uart"},"dirs": {},"scripts": {},"component": {"name": "rk3568_uart_test","subsystem": "sample","syscap": [],"features": [],"adapted_system_type": ["mini","small","standard"],"rom": "10KB","ram": "10KB","deps": {"components": ["hdf_core","hilog"],"third_party": []},"build": {"sub_component": ["//sample/06_platform_uart:rk3568_uart_test"],"inner_kits": [],"test": []}}
}

4、程序编译

sudo ./build.sh --product-name rk3568 --build-target rk3568_uart_test

5、运行结果

在这里插入图片描述

运行如下:

在这里插入图片描述

注意:

  • rbuff获取的时候可能为空。因为本次案例是基于非阻塞,电脑端发送的串口可能没有获取到数据。

建议:

  • 读者可以尝试使用堵塞方式,再测试看看。

6、参考资料

  • UART平台驱动开发
  • UART应用程序开发
  • OpenHarmony平台驱动案例–UART

相关文章:

  • STP配置
  • 【ConvLSTM第一期】ConvLSTM原理
  • day13 leetcode-hot100-24(链表3)
  • c++ opencv 形态学操作腐蚀和膨胀
  • OpenCV CUDA模块结构分析与形状描述符------在 GPU 上计算图像的原始矩(spatial moments)函数spatialMoments()
  • RV1126-OPENCV Mat理解
  • 基于React和TypeScript的金融市场模拟器开发与模式分析
  • 从 SWT Browser 迁移到 JxBrowser
  • C#·常用快捷键
  • kibana解析Excel文件,生成mapping es导入Excel
  • 职坐标AI算法实战:TensorFlow/PyTorch深度模型
  • Typora-macOS 风格代码块
  • K8S查看pod资源占用和物理机器IP对应关系
  • 小白的进阶之路系列之七----人工智能从初步到精通pytorch自动微分优化以及载入和保存模型
  • 【C++项目】:仿 muduo 库 One-Thread-One-Loop 式并发服务器
  • 解释滚动更新的过程,如何通过`kubectl set image`命令触发更新? 版本回滚的命令是什么?如何查看Deployment的更新历史?
  • 《Python 应用中的蓝绿部署与滚动更新:持续集成中的实践与优化》
  • 【VSCode-Qt】Docker远程连接的项目UI文件在 VSCode 上无法预览
  • 【基于SpringBoot的图书购买系统】操作Jedis对图书图书的增-删-改:从设计到实战的全栈开发指南
  • 聊聊 Metasploit 免杀
  • 重庆住建厅网站官网/有效的网站推广方式
  • 南宁做网站公司必荐云尚网络/google年度关键词
  • 国产地图软件哪个好用/苏州seo网站公司
  • .net开发微信网站/品牌推广与传播方案
  • 开发公司顶名字购买房子/长沙专业seo优化推荐
  • 珠海新盈科技 网站建设/怎么做网络宣传推广