diff --git a/zh-cn/device-dev/driver/driver-platform-adc-des.md b/zh-cn/device-dev/driver/driver-platform-adc-des.md
index d478bd0a69d0a4dfd188541e8139799eb0d7caf2..6711f7e39e7ed7a5e73ae2e9102b873a368fc766 100755
--- a/zh-cn/device-dev/driver/driver-platform-adc-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-adc-des.md
@@ -4,29 +4,30 @@
### 功能简介
-ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1:
+ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1所示:
**图 1** ADC物理连线示意图
-![](figures/ADC物理连线示意图.png "ADC物理连线示意图")
+![ADC物理连线示意图](figures/ADC物理连线示意图.png)
ADC接口定义了完成AD转换的通用方法集合,包括:
- ADC设备管理:打开或关闭ADC设备。
+
- ADC读取转换结果:读取AD转换结果。
### 基本概念
- 分辨率
- 分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。
+ 分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。
- 转换误差
- 转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。
+ 转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。
- 转换时间
- 转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。
+ 转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。
### 运作机制
@@ -50,18 +51,18 @@ ADC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
-| 接口名 | 接口描述 |
+| 接口名 | 接口描述 |
| -------- | ---------------- |
-| DevHandle AdcOpen(uint32_t number) | 打开ADC设备 |
-| void AdcClose(DevHandle handle) | 关闭ADC设备 |
-| int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t \*val) | 读取AD转换结果值 |
+| DevHandle AdcOpen(uint32_t number) | 打开ADC设备 |
+| void AdcClose(DevHandle handle) | 关闭ADC设备 |
+| int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t \*val) | 读取AD转换结果值 |
### 开发步骤
使用ADC设备的一般流程如图2所示。
- **图 2** ADC使用流程图
-![](figures/ADC使用流程图.png "ADC使用流程图")
+**图 2** ADC使用流程图
+![ADC使用流程图](figures/ADC使用流程图.png)
#### 打开ADC设备
@@ -76,23 +77,23 @@ DevHandle AdcOpen(int16_t number);
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | ----------------- |
-| number | ADC设备号 |
-| **返回值** | **返回值描述** |
-| NULL | 打开ADC设备失败 |
-| 设备句柄 | 打开的ADC设备句柄 |
+| number | int16_t类型,ADC设备号 |
+| **返回值** | **返回值描述** |
+| NULL | 打开ADC设备失败 |
+| 设备句柄 | 打开的ADC设备句柄 |
假设系统中存在2个ADC设备,编号从0到1,那么我们现在打开1号设备。
```c
-DevHandle adcHandle = NULL; /* ADC设备句柄 /
+DevHandle adcHandle = NULL; // ADC设备句柄
-/* 打开ADC设备 */
+// 打开ADC设备
adcHandle = AdcOpen(1);
if (adcHandle == NULL) {
HDF_LOGE("AdcOpen: fail\n");
- return;
+ return NULL;
}
```
@@ -106,14 +107,14 @@ int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t *val);
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | ADC设备句柄 |
-| channel | ADC设备通道号 |
-| val | AD转换结果 |
+| handle | DevHandle类型,ADC设备句柄 |
+| channel| uint32_t类型,ADC设备通道号 |
+| val | uint32_t类型指针,AD转换结果 |
| **返回值** | **返回值描述** |
-| 0 | 读取成功 |
-| 负数 | 读取失败 |
+| HDF_SUCCESS | 读取成功 |
+| 负数 | 读取失败 |
读取转换结果示例(以通道1为例):
@@ -122,9 +123,9 @@ uint32_t value;
int32_t ret;
ret = AdcRead(adcHandle, 1, &value);
-if (ret != 0) {
+if (ret != HDF_SUCCESS) {
HDF_LOGE("ADC read fail!\n");
- return;
+ return ret;
}
```
@@ -138,16 +139,16 @@ void AdcClose(DevHandle handle);
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ------ | ----------- |
-| handle | ADC设备句柄 |
+| handle | DevHandle类型,ADC设备句柄 |
| 返回值 | 返回值描述 |
-| 无 | 无 |
+| 无 | 无 |
关闭ADC设备示例:
```c
-AdcClose(adcHandle); /* 关闭ADC设备 */
+AdcClose(adcHandle); // 关闭ADC设备
```
### 使用实例
@@ -163,15 +164,15 @@ AdcClose(adcHandle); /* 关闭ADC设备 */
示例如下:
```c
-#include "adc_if.h" /* ADC标准接口头文件 */
-#include "hdf_log.h" /* 标准日志打印头文件 */
+#include "adc_if.h" // ADC标准接口头文件
+#include "hdf_log.h" // 标准日志打印头文件
-/* 设备号0,通道号1 */
+/// 设备号0,通道号1
#define ADC_DEVICE_NUM 0
#define ADC_CHANNEL_NUM 1
#define ADC_TEST_NUM 30
-/* ADC例程总入口 */
+// ADC例程总入口
static int32_t TestCaseAdc(void)
{
int32_t i;
@@ -179,14 +180,14 @@ static int32_t TestCaseAdc(void)
DevHandle adcHandle = NULL;
uint32_t readBuf[ADC_TEST_NUM] = {0};
- /* 打开ADC设备 */
+ // 打开ADC设备
adcHandle = AdcOpen(ADC_DEVICE_NUM);
if (adcHandle == NULL) {
HDF_LOGE("%s: Open ADC%u fail!", __func__, ADC_DEVICE_NUM);
return -1;
}
- /* 连续进行30次AD转换并读取转换结果 */
+ // 连续进行30次AD转换并读取转换结果
for (i = 0; i < ADC_TEST_NUM; i++) {
ret = AdcRead(adcHandle, ADC_CHANNEL_NUM, &readBuf[i]);
if (ret != HDF_SUCCESS) {
@@ -197,7 +198,7 @@ static int32_t TestCaseAdc(void)
}
HDF_LOGI("%s: ADC read successful!", __func__);
- /* 访问完毕关闭ADC设备 */
+ // 访问完毕关闭ADC设备
AdcClose(adcHandle);
return 0;
diff --git a/zh-cn/device-dev/driver/driver-platform-adc-develop.md b/zh-cn/device-dev/driver/driver-platform-adc-develop.md
index 21843636451da3b08f8d2d0cfaf437ee2da1bf8f..c254b470ada7afbc80604809f91d79dbe2a195bb 100755
--- a/zh-cn/device-dev/driver/driver-platform-adc-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-adc-develop.md
@@ -4,37 +4,42 @@
### 功能简介
-ADC(Analog to Digital Converter),即模拟-数字转换器,是一种将模拟信号转换成对应数字信号的设备。
+ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1所示:
+
+**图 1** ADC物理连线示意图
+![ADC物理连线示意图](figures/ADC物理连线示意图.png)
### 基本概念
- 分辨率
- 分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。
+ 分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。
- 转换误差
- 转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。
+ 转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。
- 转换时间
- 转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。
+ 转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。
### 运作机制
-在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。ADC模块即采用统一服务模式(如图1)。
+在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。ADC模块即采用统一服务模式(如图2所示)。
ADC模块各分层的作用为:
- 接口层:提供打开设备,写入数据,关闭设备的能力。
+
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。
+
- 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
-**图1** ADC统一服务模式结构图
-![image](figures/统一服务模式结构图.png "ADC统一服务模式结构图")
+**图 2** ADC统一服务模式结构图
+![ADC统一服务模式结构图](figures/统一服务模式结构图.png)
## 使用指导
@@ -90,348 +95,374 @@ static const struct AdcLockMethod g_adcLockOpsDefault = {
若实际情况不允许使用Spinlock,驱动适配者可以考虑使用其他类型的锁来实现一个自定义的AdcLockMethod。一旦实现了自定义的AdcLockMethod,默认的AdcLockMethod将被覆盖。
- **表1** AdcMethod结构体成员的钩子函数功能说明
+**表 1** AdcMethod结构体成员的钩子函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
-| read | device:结构体指针,核心层ADC控制器
channel:uint32_t,传入的通道号 | val:uint32_t指针,要传出的信号数据 | HDF_STATUS相关状态 | 读取ADC采样的信号数据 |
+| read | device:结构体指针,核心层ADC控制器
channel:uint32_t类型,传入的通道号 | val:uint32_t类型指针,要传出的信号数据 | HDF_STATUS相关状态 | 读取ADC采样的信号数据 |
| stop | device:结构体指针,核心层ADC控制器 | 无 | HDF_STATUS相关状态 | 关闭ADC设备 |
| start | device:结构体指针,核心层ADC控制器 | 无 | HDF_STATUS相关状态 | 开启ADC设备 |
-**表2** AdcLockMethod结构体成员函数功能说明
+**表 2** AdcLockMethod结构体成员函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
| lock | device:结构体指针,核心层ADC设备对象。 | 无 | HDF_STATUS相关状态 | 获取临界区锁 |
-| unlock | devicie:结构体指针,核心层ADC设备对象。 | 无 | HDF_STATUS相关状态 | 释放临界区锁 |
+| unlock | device:结构体指针,核心层ADC设备对象。 | 无 | HDF_STATUS相关状态 | 释放临界区锁 |
### 开发步骤
-ADC模块适配必选的三个环节是实例化驱动入口,配置属性文件,以及实例化核心层接口函数。
+ADC模块适配包含以下四个步骤:
1. 实例化驱动入口
- - 实例化HdfDriverEntry结构体成员。
- - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
+
+ - 实例化HdfDriverEntry结构体成员。
+
+ - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2. 配置属性文件
- - 在device_info.hcs文件中添加deviceNode描述。
- - 【可选】添加adc_config.hcs器件属性文件。
+
+ - 在device_info.hcs文件中添加deviceNode描述。
+
+ - 【可选】添加adc_config.hcs器件属性文件。
3. 实例化核心层接口函数
- - 初始化AdcDevice成员。
- - 实例化AdcDevice成员AdcMethod。
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化AdcDevice成员AdcMethod,其定义和成员说明见[接口说明](#接口说明)。
+
+ - 初始化AdcDevice成员。
+
+ - 实例化AdcDevice成员AdcMethod。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化AdcDevice成员AdcMethod,其定义和成员说明见[接口说明](#接口说明)。
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。
### 开发实例
- 接下来以Hi3516DV300的驱动//device/soc/hisilicon/common/platform/adc/adc_hi35xx.c为例, 展示需要驱动适配者提供哪些内容来完整实现设备功能。
+下方将基于Hi3516DV300开发板以//device/soc/hisilicon/common/platform/adc/adc_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 驱动开发首先需要实例化驱动入口。
+1. 实例化驱动入口
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+ 驱动入口必须为HdfDriverEntry(在//drivers/hdf_core/interfaces/inner_api/host/shared/hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
- ADC驱动入口参考:
+ ADC驱动入口参考:
- ADC控制器会出现多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象。这样,需要打开某个设备时,管理器对象会根据指定参数查找到指定设备。
+ ADC控制器会出现多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象。这样,需要打开某个设备时,管理器对象会根据指定参数查找到指定设备。
- ADC管理器的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的AdcDeviceAdd函数,它会实现相应功能。
+ ADC管理器的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的AdcDeviceAdd函数,它会实现相应功能。
```c
static struct HdfDriverEntry g_hi35xxAdcDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxAdcInit,
.Release = Hi35xxAdcRelease,
- .moduleName = "hi35xx_adc_driver", //【必要且与device_info.hcs文件内的模块名匹配】
+ .moduleName = "hi35xx_adc_driver", // 【必要且与device_info.hcs文件内的模块名匹配】
};
HDF_INIT(g_hi35xxAdcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
-
- /* 核心层adc_core.c管理器服务的驱动入口 */
+
+ // 核心层adc_core.c管理器服务的驱动入口
struct HdfDriverEntry g_adcManagerEntry = {
.moduleVersion = 1,
- .Init = AdcManagerInit,
- .Release = AdcManagerRelease,
+ .Bind = AdcManagerBind, // ADC不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作
+ .Init = AdcManagerInit, // 见Init参考
+ .Release = AdcManagerRelease, // 见Release参考
.moduleName = "HDF_PLATFORM_ADC_MANAGER", // 这与device_info.hcs文件中device0对应
};
- HDF_INIT(g_adcManagerEntry);
+ HDF_INIT(g_adcManagerEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
```
-2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在adc_config.hcs中配置器件属性。
+2. 配置属性文件
+
+ 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在adc_config.hcs中配置器件属性。
deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层AdcDevice相关成员的默认值或限制范围有密切关系。
- 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为ADC管理器,其各项参数必须如下设置:
+ 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为ADC管理器,其各项参数如表3所示:
+
+ **表 3** device_info.hcs节点参数说明
| 成员名 | 值 |
| -------- | -------- |
- | moduleName | 固定为HDF_PLATFORM_ADC_MANAGER |
- | serviceName | 无 |
- | policy | 具体配置为0,不发布服务 |
- | deviceMatchAttr | 没有使用,可忽略 |
-
- 从第二个节点开始配置具体ADC控制器信息,第一个节点并不表示某一路ADC控制器,而是代表一个资源性质设备,用于描述一类ADC控制器的信息。本例只有一个ADC设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在adc_config文件中增加对应的器件属性。
-
- - device_info.hcs配置参考
-
- ```c
- root {
- device_info {
- platform :: host {
- device_adc :: device {
- device0 :: deviceNode {
- policy = 0;
- priority = 50;
- permission = 0644;
- moduleName = "HDF_PLATFORM_ADC_MANAGER";
- serviceName = "HDF_PLATFORM_ADC_MANAGER";
- }
- device1 :: deviceNode {
- policy = 0; // 等于0,不需要发布服务。
- priority = 55; // 驱动启动优先级。
- permission = 0644; // 驱动创建设备节点权限。
- moduleName = "hi35xx_adc_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
- serviceName = "HI35XX_ADC_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。
- deviceMatchAttr = "hisilicon_hi35xx_adc"; //【必要】用于配置控制器私有数据,要与adc_config.hcs中对应控制器保持一致,
- // 具体的控制器信息在adc_config.hcs中。
- }
- }
- }
- }
- }
- ```
-
- - adc_config.hcs配置参考
-
- 此处以Hi3516DV300为例,给出HCS配置参考。其中部分字段为Hi3516DV300特有功能,驱动适配者可根据需要进行删除或添加字段。
-
- ```c
- root {
- platform {
- adc_config_hi35xx {
- match_attr = "hisilicon_hi35xx_adc";
- template adc_device {
- regBasePhy = 0x120e0000; // 寄存器物理基地址
- regSize = 0x34; // 寄存器位宽
- deviceNum = 0; // 设备号
- validChannel = 0x1; // 有效通道
- dataWidth = 10; // AD转换后的数据位宽,即分辨率
- scanMode = 1; // 扫描模式
- delta = 0; // 转换结果误差范围
- deglitch = 0; // 滤毛刺开关
- glitchSample = 5000; // 滤毛刺时间窗口
- rate = 20000; // 转换速率
- }
- device_0 :: adc_device {
- deviceNum = 0;
- validChannel = 0x2;
- }
- }
- }
- }
- ```
-
- 需要注意的是,新增adc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
-
- 例如:本例中adc_config.hcs所在路径为//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs" // 配置文件相对路径
- ```
-
- 本例基于Hi3516DV300开发板的小型系统LiteOS内核运行,对应的hdf.hcs文件路径为vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs以及//device/hisilicon/hispark_taurus/sdk_liteos/hdf_config/hdf.hcs。驱动适配者需根据实际情况选择对应路径下的文件进行修改。
-
-3. 完成驱动入口注册之后,下一步就是以核心层AdcDevice对象的初始化为核心,包括初始化驱动适配者自定义结构体(传递参数和数据),实例化AdcDevice成员AdcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
-
- - 自定义结构体参考。
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,而且adc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值(例如设备号、总线号等)也会传递给核心层AdcDevice对象。
-
- ```c
- struct Hi35xxAdcDevice {
- struct AdcDevice device; //【必要】是核心层控制对象,必须作为自定义结构体的首个成员,其具体描述见下方。
- volatile unsigned char *regBase; //【必要】寄存器基地址
- volatile unsigned char *pinCtrlBase;
- uint32_t regBasePhy; //【必要】寄存器物理基地址
- uint32_t regSize; //【必要】寄存器位宽
- uint32_t deviceNum; //【必要】设备号
- uint32_t dataWidth; //【必要】信号接收的数据位宽
- uint32_t validChannel; //【必要】有效通道
- uint32_t scanMode; //【必要】扫描模式
- uint32_t delta;
- uint32_t deglitch;
- uint32_t glitchSample;
- uint32_t rate; //【必要】采样率
- };
-
- /* AdcDevice是核心层控制器结构体,其中的成员在Init函数中会被赋值。*/
- struct AdcDevice {
- const struct AdcMethod *ops;
- OsalSpinlock spin;
- uint32_t devNum;
- uint32_t chanNum;
- const struct AdcLockMethod *lockOps;
- void *priv;
- };
- ```
-
- - AdcDevice成员钩子函数结构体AdcMethod的实例化。
-
- AdcLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发,其他成员在Init函数中初始化。
-
- ```c
- static const struct AdcMethod g_method = {
- .read = Hi35xxAdcRead,
- .stop = Hi35xxAdcStop,
- .start = Hi35xxAdcStart,
- };
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_IO | I/O错误 |
- | HDF_SUCCESS | 传输成功 |
- | HDF_FAILURE | 传输失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化AdcDevice成员,并调用核心层AdcDeviceAdd函数。
-
- ```c
- static int32_t Hi35xxAdcInit(struct HdfDeviceObject *device)
- {
- int32_t ret;
- struct DeviceResourceNode *childNode = NULL;
- ...
- /* 遍历、解析adc_config.hcs中的所有配置节点,并分别调用Hi35xxAdcParseInit函数来初始化device。*/
- DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
- ret = Hi35xxAdcParseInit(device, childNode); // 函数定义见下方
- ...
- }
- return ret;
- }
-
- static int32_t Hi35xxAdcParseInit(struct HdfDeviceObject *device, struct DeviceResourceNode *node)
- {
- int32_t ret;
- struct Hi35xxAdcDevice *hi35xx = NULL; //【必要】自定义结构体对象
- (void)device;
-
- hi35xx = (struct Hi35xxAdcDevice *)OsalMemCalloc(sizeof(*hi35xx)); //【必要】内存分配
- ...
- ret = Hi35xxAdcReadDrs(hi35xx, node); //【必要】将adc_config文件的默认值填充到结构体中,函数定义见下方
- ...
- hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); //【必要】地址映射
- ...
- hi35xx->pinCtrlBase = OsalIoRemap(HI35XX_ADC_IO_CONFIG_BASE, HI35XX_ADC_IO_CONFIG_SIZE);
- ...
- Hi35xxAdcDeviceInit(hi35xx); //【必要】ADC设备的初始化
- hi35xx->device.priv = (void *)node; //【必要】存储设备属性
- hi35xx->device.devNum = hi35xx->deviceNum; //【必要】初始化AdcDevice成员
- hi35xx->device.ops = &g_method; //【必要】AdcMethod的实例化对象的挂载
- ret = AdcDeviceAdd(&hi35xx->device); //【必要且重要】调用此函数填充核心层结构体,返回成功信号后驱动才完全接入平台核心层。
- ...
- return HDF_SUCCESS;
-
- __ERR__:
- if (hi35xx != NULL) { // 若不成功,需要执行去初始化相关函数。
- if (hi35xx->regBase != NULL) {
- OsalIoUnmap((void *)hi35xx->regBase);
- hi35xx->regBase = NULL;
- }
- AdcDeviceRemove(&hi35xx->device);
- OsalMemFree(hi35xx);
- }
- return ret;
- }
-
- static int32_t Hi35xxAdcReadDrs(struct Hi35xxAdcDevice *hi35xx, const struct DeviceResourceNode *node)
- {
- int32_t ret;
- struct DeviceResourceIface *drsOps = NULL;
+ | policy | 驱动服务发布的策略,ADC管理器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。ADC管理器具体配置为50 |
+ | permission | 驱动创建设备节点权限,ADC管理器具体配置为0664 |
+ | moduleName | 驱动名称,ADC管理器固定为HDF_PLATFORM_ADC_MANAGER |
+ | serviceName | 驱动对外发布服务的名称,ADC管理器服务名设置为HDF_PLATFORM_ADC_MANAGER |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,ADC管理器没有使用,可忽略 |
+
+ 从第二个节点开始配置具体ADC控制器信息,第一个节点并不表示某一路ADC控制器,而是代表一个资源性质设备,用于描述一类ADC控制器的信息。本例只有一个ADC设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在adc_config.hcs文件中增加对应的器件属性。
+
+ - device_info.hcs配置参考
+
+ ```c
+ root {
+ device_info {
+ platform :: host {
+ device_adc :: device {
+ device0 :: deviceNode {
+ policy = 2;
+ priority = 50;
+ permission = 0644;
+ moduleName = "HDF_PLATFORM_ADC_MANAGER";
+ serviceName = "HDF_PLATFORM_ADC_MANAGER";
+ }
+ device1 :: deviceNode {
+ policy = 0; // 等于0,不需要发布服务。
+ priority = 55; // 驱动启动优先级。
+ permission = 0644; // 驱动创建设备节点权限。
+ moduleName = "hi35xx_adc_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
+ serviceName = "HI35XX_ADC_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。
+ deviceMatchAttr = "hisilicon_hi35xx_adc"; //【必要】用于配置控制器私有数据,要与adc_config.hcs中对应控制器保持一致,具体的控制器信息在adc_config.hcs中。
+ }
+ }
+ }
+ }
+ }
+ ```
+
+ - adc_config.hcs配置参考
+
+ 此处以Hi3516DV300为例,给出HCS配置参考。其中部分字段为Hi3516DV300特有功能,驱动适配者可根据需要进行删除或添加字段。
+
+ ```c
+ root {
+ platform {
+ adc_config_hi35xx {
+ match_attr = "hisilicon_hi35xx_adc";
+ template adc_device {
+ regBasePhy = 0x120e0000; // 寄存器物理基地址
+ regSize = 0x34; // 寄存器位宽
+ deviceNum = 0; // 设备号
+ validChannel = 0x1; // 有效通道
+ dataWidth = 10; // AD转换后的数据位宽,即分辨率
+ scanMode = 1; // 扫描模式
+ delta = 0; // 转换结果误差范围
+ deglitch = 0; // 滤毛刺开关
+ glitchSample = 5000; // 滤毛刺时间窗口
+ rate = 20000; // 转换速率
+ }
+ device_0 :: adc_device {
+ deviceNum = 0;
+ validChannel = 0x2;
+ }
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增adc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
+
+ 例如:本例中adc_config.hcs所在路径为//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs" // 配置文件相对路径
+ ```
+
+ 本例基于Hi3516DV300开发板的小型系统LiteOS内核运行,对应的hdf.hcs文件路径为vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs以及//device/hisilicon/hispark_taurus/sdk_liteos/hdf_config/hdf.hcs。驱动适配者需根据实际情况选择对应路径下的文件进行修改。
+
+3. 实例化核心层函数
+
+ 完成驱动入口注册之后,下一步就是以核心层AdcDevice对象的初始化为核心,包括初始化驱动适配者自定义结构体(传递参数和数据),实例化AdcDevice成员AdcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
+
+ - 自定义结构体参考。
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,而且adc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值(例如设备号、总线号等)也会传递给核心层AdcDevice对象。
+
+ ```c
+ struct Hi35xxAdcDevice {
+ struct AdcDevice device; // 【必要】是核心层控制对象,必须作为自定义结构体的首个成员,其具体描述见下方。
+ volatile unsigned char *regBase; // 【必要】寄存器基地址
+ volatile unsigned char *pinCtrlBase;
+ uint32_t regBasePhy; // 【必要】寄存器物理基地址
+ uint32_t regSize; // 【必要】寄存器位宽
+ uint32_t deviceNum; // 【必要】设备号
+ uint32_t dataWidth; // 【必要】信号接收的数据位宽
+ uint32_t validChannel; // 【必要】有效通道
+ uint32_t scanMode; // 【必要】扫描模式
+ uint32_t delta;
+ uint32_t deglitch;
+ uint32_t glitchSample;
+ uint32_t rate; // 【必要】采样率
+ };
+
+ // AdcDevice是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct AdcDevice {
+ const struct AdcMethod *ops;
+ OsalSpinlock spin;
+ uint32_t devNum;
+ uint32_t chanNum;
+ const struct AdcLockMethod *lockOps;
+ void *priv;
+ };
+ ```
+
+ - AdcDevice成员钩子函数结构体AdcMethod的实例化。
+
+ AdcLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发。
+
+ ```c
+ static const struct AdcMethod g_method = {
+ .read = Hi35xxAdcRead,
+ .stop = Hi35xxAdcStop,
+ .start = Hi35xxAdcStart,
+ };
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表4为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 4** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_IO | I/O错误 |
+ | HDF_SUCCESS | 传输成功 |
+ | HDF_FAILURE | 传输失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化AdcDevice成员,并调用核心层AdcDeviceAdd函数。
- /* 获取drsOps方法 */
- drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- if (drsOps == NULL || drsOps->GetUint32 == NULL) {
- HDF_LOGE("%s: invalid drs ops", __func__);
- return HDF_ERR_NOT_SUPPORT;
- }
- /* 将配置参数依次读出,并填充至结构体中 */
- ret = drsOps->GetUint32(node, "regBasePhy", &hi35xx->regBasePhy, 0);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read regBasePhy failed", __func__);
- return ret;
- }
- ret = drsOps->GetUint32(node, "regSize", &hi35xx->regSize, 0);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read regSize failed", __func__);
- return ret;
- }
- ···
- return HDF_SUCCESS;
- }
- ```
-
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
-
- ```c
- static void Hi35xxAdcRelease(struct HdfDeviceObject *device)
- {
- const struct DeviceResourceNode *childNode = NULL;
- ...
- /* 遍历、解析adc_config.hcs中的所有配置节点,并分别进行Release操作。*/
- DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
- Hi35xxAdcRemoveByNode(childNode);// 函数定义见下
- }
- }
+ ```c
+ static int32_t Hi35xxAdcInit(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ struct DeviceResourceNode *childNode = NULL;
+ ......
+ // 遍历、解析adc_config.hcs中的所有配置节点,并分别调用Hi35xxAdcParseInit函数来初始化device。
+ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
+ ret = Hi35xxAdcParseInit(device, childNode); // 函数定义见下方
+ ......
+ }
+ HDF_LOGI("%s: adc init success.", __func__);
+ return ret;
+ }
+
+ static int32_t Hi35xxAdcParseInit(struct HdfDeviceObject *device, struct DeviceResourceNode *node)
+ {
+ int32_t ret;
+ struct Hi35xxAdcDevice *hi35xx = NULL; //【必要】自定义结构体对象
+
+ (void)device;
+ hi35xx = (struct Hi35xxAdcDevice *)OsalMemCalloc(sizeof(*hi35xx)); //【必要】内存分配
+ ......
+ ret = Hi35xxAdcReadDrs(hi35xx, node); //【必要】将adc_config文件的默认值填充到结构体中,函数定义见下方
+ ......
+ hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); //【必要】地址映射
+ ......
+ hi35xx->pinCtrlBase = OsalIoRemap(HI35XX_ADC_IO_CONFIG_BASE, HI35XX_ADC_IO_CONFIG_SIZE);
+ ......
+ Hi35xxAdcDeviceInit(hi35xx); // 【必要】ADC设备的初始化
+ hi35xx->device.priv = (void *)node; // 【必要】存储设备属性
+ hi35xx->device.devNum = hi35xx->deviceNum; // 【必要】初始化AdcDevice成员
+ hi35xx->device.ops = &g_method; // 【必要】AdcMethod的实例化对象的挂载
+ ret = AdcDeviceAdd(&hi35xx->device); // 【必要且重要】调用此函数填充核心层结构体,返回成功信号后驱动才完全接入平台核心层。
+ ......
+ return HDF_SUCCESS;
+
+ __ERR__:
+ if (hi35xx != NULL) { // 若不成功,需要执行去初始化相关函数。
+ if (hi35xx->regBase != NULL) {
+ OsalIoUnmap((void *)hi35xx->regBase);
+ hi35xx->regBase = NULL;
+ }
+ AdcDeviceRemove(&hi35xx->device);
+ OsalMemFree(hi35xx);
+ }
+ return ret;
+ }
+
+ static int32_t Hi35xxAdcReadDrs(struct Hi35xxAdcDevice *hi35xx, const struct DeviceResourceNode *node)
+ {
+ int32_t ret;
+ struct DeviceResourceIface *drsOps = NULL;
+
+ // 获取drsOps方法
+ drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
+ if (drsOps == NULL || drsOps->GetUint32 == NULL) {
+ HDF_LOGE("%s: invalid drs ops", __func__);
+ return HDF_ERR_NOT_SUPPORT;
+ }
+ // 将配置参数依次读出,并填充至结构体中
+ ret = drsOps->GetUint32(node, "regBasePhy", &hi35xx->regBasePhy, 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read regBasePhy failed", __func__);
+ return ret;
+ }
+ ret = drsOps->GetUint32(node, "regSize", &hi35xx->regSize, 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read regSize failed", __func__);
+ return ret;
+ }
+ ......
+ return HDF_SUCCESS;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+
+ ```c
+ static void Hi35xxAdcRelease(struct HdfDeviceObject *device)
+ {
+ const struct DeviceResourceNode *childNode = NULL;
+ ......
+ // 遍历、解析adc_config.hcs中的所有配置节点,并分别进行Release操作。
+ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
+ Hi35xxAdcRemoveByNode(childNode);// 函数定义见下
+ }
+ }
- static void Hi35xxAdcRemoveByNode(const struct DeviceResourceNode *node)
- {
- int32_t ret;
- int32_t deviceNum;
- struct AdcDevice *device = NULL;
- struct Hi35xxAdcDevice *hi35xx = NULL;
- struct DeviceResourceIface *drsOps = NULL;
-
- drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- ...
- ret = drsOps->GetUint32(node, "deviceNum", (uint32_t *)&deviceNum, 0);
- ...
- /* 可以调用AdcDeviceGet函数通过设备的deviceNum获取AdcDevice对象,以及调用AdcDeviceRemove函数来释放AdcDevice对象的内容。*/
- device = AdcDeviceGet(deviceNum);
- if (device != NULL && device->priv == node) {
- AdcDevicePut(device);
- AdcDeviceRemove(device); //【必要】主要是从管理器驱动那边移除AdcDevice对象。
- hi35xx = (struct Hi35xxAdcDevice *)device; //【必要】通过强制转换获取自定义的对象并进行Release操作。这一步的前提是device必须作为自定义结构体的首个成员。
- OsalIoUnmap((void *)hi35xx->regBase);
- OsalMemFree(hi35xx);
- }
- return;
- }
- ```
+ static void Hi35xxAdcRemoveByNode(const struct DeviceResourceNode *node)
+ {
+ int32_t ret;
+ int32_t deviceNum;
+ struct AdcDevice *device = NULL;
+ struct Hi35xxAdcDevice *hi35xx = NULL;
+ struct DeviceResourceIface *drsOps = NULL;
+
+ drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
+ ......
+ ret = drsOps->GetUint32(node, "deviceNum", (uint32_t *)&deviceNum, 0);
+ ......
+ // 可以调用AdcDeviceGet函数通过设备的deviceNum获取AdcDevice对象,以及调用AdcDeviceRemove函数来释放AdcDevice对象的内容。
+ device = AdcDeviceGet(deviceNum);
+ if (device != NULL && device->priv == node) {
+ AdcDevicePut(device);
+ AdcDeviceRemove(device); //【必要】主要是从管理器驱动那边移除AdcDevice对象。
+ hi35xx = (struct Hi35xxAdcDevice *)device; //【必要】通过强制转换获取自定义的对象并进行Release操作。这一步的前提是device必须作为自定义结构体的首个成员。
+ OsalIoUnmap((void *)hi35xx->regBase);
+ OsalMemFree(hi35xx);
+ }
+ return;
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。
diff --git a/zh-cn/device-dev/driver/driver-platform-dac-des.md b/zh-cn/device-dev/driver/driver-platform-dac-des.md
index 4f4e4b47f12e04e5559af1794dc0ac2dde1e2b4b..6922c00e85ea14558147ca2b278785d5783a4a57 100644
--- a/zh-cn/device-dev/driver/driver-platform-dac-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-dac-des.md
@@ -7,29 +7,32 @@
DAC(Digital to Analog Converter)是一种通过电流、电压或电荷的形式将数字信号转换为模拟信号的设备,主要用于:
- 作为过程控制计算机系统的输出通道,与执行器相连,实现对生产过程的自动控制。
+
- 在利用反馈技术的模数转换器设计中,作为重要的功能模块呈现。
DAC接口定义了完成DAC传输的通用方法集合,包括:
+
- DAC设备管理:打开或关闭DAC设备。
+
- DAC设置目标值:设置DAC设备需要将数字信号转成模拟信号的目标值。
### 基本概念
- 分辨率
- 分辨率指的是DAC模块能够转换的二进制位数,位数越多分辨率越高。
+ 分辨率指的是DAC模块能够转换的二进制位数,位数越多分辨率越高。
- 转换精度
- 精度是指输入端加有最大数值时,DAC的实际输出值和理论计算值之差,DAC转换器的转换精度与DAC转换器的集成芯片结构和接口电路配置有关。理想情况下,DAC的转换精度越小越好,因此为了获得更高精度的DAC转换结果,首先要保证选择的DAC转换器具备足够高的分辨率。其次,接口电路的器件或电源存在误差时,会造成DAC转换的误差,若这些误差超过一定程度,就会导致DAC转换错误。
+ 精度是指输入端加有最大数值时,DAC的实际输出值和理论计算值之差,DAC转换器的转换精度与DAC转换器的集成芯片结构和接口电路配置有关。理想情况下,DAC的转换精度越小越好,因此为了获得更高精度的DAC转换结果,首先要保证选择的DAC转换器具备足够高的分辨率。其次,接口电路的器件或电源存在误差时,会造成DAC转换的误差,若这些误差超过一定程度,就会导致DAC转换错误。
- 转换速度
- 转换速度一般由建立时间决定。从输入由全0突变为全1时开始,到输出电压稳定在FSR±½LSB范围(或以FSR±x%FSR指明范围)内为止,这段时间称为建立时间,它是DAC的最大响应时间,所以用它衡量转换速度的快慢。
+ 转换速度一般由建立时间决定。从输入由全0突变为全1时开始,到输出电压稳定在FSR±½LSB范围(或以FSR±x%FSR指明范围)内为止,这段时间称为建立时间,它是DAC的最大响应时间,所以用它衡量转换速度的快慢。
- 满量程范围FSR(Full Scale Range),是指DAC输出信号幅度的最大范围,不同的DAC有不同的满量程范围,该范围可以用正、负电流或者正、负电压来限制。
+ 满量程范围FSR(Full Scale Range),是指DAC输出信号幅度的最大范围,不同的DAC有不同的满量程范围,该范围可以用正、负电流或者正、负电压来限制。
- 最低有效位LSB(Least Significant Byte),指的是一个二进制数字中的第0位(即最低位)。
+ 最低有效位LSB(Least Significant Byte),指的是一个二进制数字中的第0位(即最低位)。
### 运作机制
@@ -59,10 +62,10 @@ DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/f
**表 1** DAC驱动API接口功能介绍
-| 接口名 | 接口描述 |
+| 接口名 | 接口描述 |
| ------------------------------------------------------------------ | ------------ |
-| DevHandle DacOpen(uint32_t number) | 打开DAC设备。 |
-| void DacClose(DevHandle handle) | 关闭DAC设备。 |
+| DevHandle DacOpen(uint32_t number) | 打开DAC设备。|
+| void DacClose(DevHandle handle) | 关闭DAC设备。|
| int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val) | 设置DA目标值。 |
### 开发步骤
@@ -76,78 +79,80 @@ DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/f
在进行DA转换之前,首先要调用DacOpen打开DAC设备,打开函数如下所示:
-```c++
+```c
DevHandle DacOpen(uint32_t number);
```
**表 2** DacOpen参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| --------- | ---------------- |
-| number | DAC设备号。 |
-| **返回值** | **返回值描述** |
-| NULL | 打开DAC设备失败。 |
-| 设备句柄 | 打开的DAC设备句柄。 |
+| number | uint32_t类型,DAC设备号。 |
+| **返回值** | **返回值描述** |
+| NULL | 打开DAC设备失败。 |
+| 设备句柄 | 打开的DAC设备句柄。 |
假设系统中存在2个DAC设备,编号从0到1,现在打开1号设备。
-```c++
-DevHandle dacHandle = NULL; // DAC设备句柄
+```c
+DevHandle dacHandle = NULL; // DAC设备句柄
-/* 打开DAC设备 */
+// 打开DAC设备
dacHandle = DacOpen(1);
if (dacHandle == NULL) {
- HDF_LOGE("DacOpen: failed\n");
- return;
+ HDF_LOGE("DacOpen: open dac fail.\n");
+ return NULL;
}
```
#### 设置DA目标值
-```c++
+```c
int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val);
```
**表 3** DacWrite参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| --------- | ------------ |
-| handle | DAC设备句柄。 |
-| channel | DAC设备通道号。|
-| val | 设置DA的值。 |
+| handle | DevHandle类型,DAC设备句柄。 |
+| channel | uint32_t类型,DAC设备通道号。 |
+| val | uint32_t类型,设置DA的值。 |
| **返回值** | **返回值描述** |
-| 0 | 写入成功。 |
-| 负数 | 写入失败。 |
+| HDF_SUCCESS | 写入DA目标值成功 |
+| 负数 | 写入DA目标值失败 |
-```c++
-/* 通过DAC_CHANNEL_NUM设备通道写入目标val值 */
+```c
+// 通过DAC_CHANNEL_NUM设备通道写入目标val值
+int32_t ret;
ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret);
+ HDF_LOGE("DacWrite: tp DAC write reg fail!,ret:%d", ret);
DacClose(dacHandle);
- return -1;
+ return ret;
}
```
#### 关闭DAC设备
DAC通信完成之后,需要关闭DAC设备,关闭函数如下所示:
-```c++
+
+```c
void DacClose(DevHandle handle);
```
**表 4** DacClose参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| --------- | ------------ |
-| handle | DAC设备句柄。 |
+| handle | DAC设备句柄。 |
| **返回值** | **返回值描述** |
-| void | 无 |
+| 无 | 无 |
关闭DAC设备示例:
-```c++
-DacClose(dacHandle); /* 关闭DAC设备 */
+```c
+DacClose(dacHandle); // 关闭DAC设备
```
## 使用实例
@@ -155,20 +160,22 @@ DacClose(dacHandle); /* 关闭DAC设备 */
DAC设备的具体使用方式可以参考如下示例代码,示例代码步骤主要如下:
1. 根据设备号DAC_DEVICE_NUM打开DAC设备得到设备句柄。
+
2. 通过DAC的设备号以及设备通道设置val的值,如果写入失败则关闭设备句柄。
+
3. 访问完毕DAC设备后,则关闭该设备句柄。
运行结果:根据输入的val通过打印日志得到输出的结果。
-```c++
-#include "dac_if.h" /* DAC标准接口头文件 */
-#include "hdf_log.h" /* 标准日志打印头文件 */
+```c
+#include "dac_if.h" // DAC标准接口头文件
+#include "hdf_log.h" // 标准日志打印头文件
-/* 设备号0,通道号1 */
+// 设备号0,通道号1
#define DAC_DEVICE_NUM 0
#define DAC_CHANNEL_NUM 1
-/* DAC例程总入口 */
+// DAC例程总入口
static int32_t TestCaseDac(void)
{
// 设置要写入的val值
@@ -176,14 +183,14 @@ static int32_t TestCaseDac(void)
int32_t ret;
DevHandle dacHandle;
- /* 打开DAC设备 */
+ // 打开DAC设备
dacHandle = DacOpen(DAC_DEVICE_NUM);
if (dacHandle == NULL) {
HDF_LOGE("%s: Open DAC%u fail!", __func__, DAC_DEVICE_NUM);
return -1;
}
- /* 写入数据 */
+ // 写入数据
ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret);
@@ -191,9 +198,9 @@ static int32_t TestCaseDac(void)
return -1;
}
- /* 访问完毕关闭DAC设备 */
+ // 访问完毕关闭DAC设备
DacClose(dacHandle);
-
+ HDF_LOGI("%s: function tests end.", __func__);
return 0;
}
-```
\ No newline at end of file
+```
diff --git a/zh-cn/device-dev/driver/driver-platform-dac-develop.md b/zh-cn/device-dev/driver/driver-platform-dac-develop.md
index b2d5bfb0cb57d7910bbdc5dc26a616b24c569f26..93f6fd2463cefe8e4c47aa5dcc7808f8fd439ab8 100644
--- a/zh-cn/device-dev/driver/driver-platform-dac-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-dac-develop.md
@@ -9,42 +9,46 @@ DAC(Digital to Analog Converter)是一种通过电流、电压或电荷的
DAC模块支持数模转换的开发。它主要用于:
- 作为过程控制计算机系统的输出通道,与执行器相连,实现对生产过程的自动控制。
+
- 在利用反馈技术的模数转换器设计中,作为重要的功能模块呈现。
### 基本概念
- 分辨率
- 分辨率指的是D/A转换器能够转换的二进制位数,位数越多分辨率越高。
+ 分辨率指的是D/A转换器能够转换的二进制位数,位数越多分辨率越高。
- 转换精度
- 精度是指输入端加有最大数值时,DAC的实际输出值和理论计算值之差,DAC转换器的转换精度与DAC转换器的集成芯片结构和接口电路配置有关。理想情况下,DAC的转换精度越小越好,因此为了获得更高精度的DAC转换结果,首先要保证选择的DAC转换器具备足够高的分辨率。其次,要保证接口电路的器件或电源误差最小或者不存在误差,否则会造成DAC转换的误差,若这些误差超过一定程度,就会导致DAC转换错误。
+ 精度是指输入端加有最大数值时,DAC的实际输出值和理论计算值之差,DAC转换器的转换精度与DAC转换器的集成芯片结构和接口电路配置有关。理想情况下,DAC的转换精度越小越好,因此为了获得更高精度的DAC转换结果,首先要保证选择的DAC转换器具备足够高的分辨率。其次,要保证接口电路的器件或电源误差最小或者不存在误差,否则会造成DAC转换的误差,若这些误差超过一定程度,就会导致DAC转换错误。
- 转换速度
- 转换速度一般由建立时间决定。从输入由全0突变为全1时开始,到输出电压稳定在FSR±½LSB范围(或以FSR±x%FSR指明范围)内为止,这段时间称为建立时间,它是DAC的最大响应时间,所以用它衡量转换速度的快慢。
+ 转换速度一般由建立时间决定。从输入由全0突变为全1时开始,到输出电压稳定在FSR±½LSB范围(或以FSR±x%FSR指明范围)内为止,这段时间称为建立时间,它是DAC的最大响应时间,所以用它衡量转换速度的快慢。
- - 满量程范围FSR(Full Scale Range),是指DAC输出信号幅度的最大范围,不同的DAC有不同的满量程范围,该范围可以用正、负电流或者正、负电压来限制。
+ - 满量程范围FSR(Full Scale Range),是指DAC输出信号幅度的最大范围,不同的DAC有不同的满量程范围,该范围可以用正、负电流或者正、负电压来限制。
- - 最低有效位LSB(Least Significant Byte),指的是一个二进制数字中的第0位(即最低位)。
+ - 最低有效位LSB(Least Significant Byte),指的是一个二进制数字中的第0位(即最低位)。
### 运作机制
-在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。DAC模块即采用统一服务模式(如图1)。
+在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。DAC模块即采用统一服务模式(如图1所示)。
DAC模块各分层的作用为:
+
- 接口层:提供打开设备、写入数据和关闭设备接口的能力。
+
- 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。
+
- 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
![](../public_sys-resources/icon-note.gif) 说明:
核心层可以调用接口层的函数,也可以通过钩子函数调用适配层函数,从而使得适配层间接的可以调用接口层函数,但是不可逆转接口层调用适配层函数。
-**图 1** 统一服务模式结构图
+**图 1** DAC统一服务模式结构图
-![](figures/统一服务模式结构图.png "DAC统一服务模式")
+![DAC统一服务模式结构图](figures/统一服务模式结构图.png)
### 约束与限制
@@ -62,13 +66,13 @@ DAC模块主要在设备中数模转换、音频输出和电机控制等设备
DacMethod和DacLockMethod定义:
-```c++
+```c
struct DacMethod {
- /* 写入数据的钩子函数 */
+ // 写入数据的钩子函数
int32_t (*write)(struct DacDevice *device, uint32_t channel, uint32_t val);
- /* 启动DAC设备的钩子函数 */
+ // 启动DAC设备的钩子函数
int32_t (*start)(struct DacDevice *device);
- /* 停止DAC设备的钩子函数 */
+ // 停止DAC设备的钩子函数
int32_t (*stop)(struct DacDevice *device);
};
@@ -77,6 +81,7 @@ struct DacLockMethod {
void (*unlock)(struct DacDevice *device);
};
```
+
在适配层中,DacMethod必须被实现,DacLockMethod可根据实际情况考虑是否实现。核心层提供了默认的DacLockMethod,其中使用Spinlock作为保护临界区的锁:
```c
@@ -106,76 +111,91 @@ static const struct DacLockMethod g_dacLockOpsDefault = {
若实际情况不允许使用Spinlock,驱动适配者可以考虑使用其他类型的锁来实现一个自定义的DacLockMethod。一旦实现了自定义的DacLockMethod,默认的DacLockMethod将被覆盖。
-**表 1** DacMethod结构体成员的钩子函数功能说明
+**表 1** DacMethod结构体成员的钩子函数功能说明
-| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
+| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | ------------------------------------------------------------ | ---- | ------------------ | -------------- |
-| write | device:结构体指针,核心层DAC控制器
channel:uint32_t,传入的通道号
val:uint32_t,要传入的数据 | 无 | HDF_STATUS相关状态 | 写入DA的目标值 |
-| start | device:结构体指针,核心层DAC控制器 | 无 | HDF_STATUS相关状态 | 开启DAC设备 |
-| stop | device:结构体指针,核心层DAC控制器 | 无 | HDF_STATUS相关状态 | 关闭DAC设备 |
+| write | device:结构体指针,核心层DAC控制器
channel:uint32_t类型,传入的通道号
val:uint32_t类型,要传入的数据 | 无 | HDF_STATUS相关状态 | 写入DA的目标值 |
+| start | device:结构体指针,核心层DAC控制器 | 无 | HDF_STATUS相关状态 | 开启DAC设备 |
+| stop | device:结构体指针,核心层DAC控制器 | 无 | HDF_STATUS相关状态 | 关闭DAC设备 |
-**表2** DacLockMethod结构体成员函数功能说明
+**表 2** DacLockMethod结构体成员函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
| lock | device:结构体指针,核心层DAC设备对象。 | 无 | HDF_STATUS相关状态 | 获取临界区锁 |
-| unlock | devicie:结构体指针,核心层DAC设备对象。 | 无 | HDF_STATUS相关状态 | 释放临界区锁 |
+| unlock | device:结构体指针,核心层DAC设备对象。 | 无 | HDF_STATUS相关状态 | 释放临界区锁 |
### 开发步骤
DAC模块适配包含以下四个步骤:
-1. 实例化驱动入口。
-2. 配置属性文件。
-3. 实例化核心层接口函数。
-4. 驱动调试。
+1. 实例化驱动入口
+
+2. 配置属性文件
+
+3. 实例化核心层函数
+
+4. 驱动调试
### 开发实例
下方将Hi3516DV300的驱动//device/soc/hisilicon/common/platform/dac/dac_hi35xx.c为例,展示驱动适配者需要提供哪些内容来完整实现设备功能。
-1. 实例化驱动入口:
+1. 实例化驱动入口
驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs中保持一致。HDF框架会汇总所有加载的驱动的HdfDriverEntry对象入口,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
- ```c++
+ ```c
static struct HdfDriverEntry g_dacDriverEntry = {
.moduleVersion = 1,
.Init = VirtualDacInit,
.Release = VirtualDacRelease,
- .moduleName = "virtual_dac_driver", //【必要且与HCS里面的名字匹配】
+ .moduleName = "virtual_dac_driver", //【必要且与device_info.hcs文件内的模块名匹配】
};
- HDF_INIT(g_dacDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ HDF_INIT(g_dacDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+
+ // 核心层dac_core.c管理器服务的驱动入口
+ struct HdfDriverEntry g_dacManagerEntry = {
+ .moduleVersion = 1,
+ .Bind = DacManagerBind, // DAC不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作
+ .Init = DacManagerInit, // 见Init参考
+ .Release = DacManagerRelease, // 见Release参考
+ .moduleName = "HDF_PLATFORM_DAC_MANAGER", // 这与device_info.hcs文件中device0对应
+ };
+ HDF_INIT(g_dacManagerEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
```
-2. 配置属性文件:
+2. 配置属性文件
+
+ - 添加//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs器件属性文件。
- - 添加//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs器件属性文件。
+ 器件属性值对于驱动适配者的驱动实现以及核心层DacDevice相关成员的默认值或限制范围有密切关系,比如设备通道的个数以及传输速率的最大值,会影响DacDevice相关成员的默认值。
- 器件属性值对于驱动适配者的驱动实现以及核心层DacDevice相关成员的默认值或限制范围有密切关系,比如设备通道的个数以及传输速率的最大值,会影响DacDevice相关成员的默认值。
+ 由于采用了统一服务模式,device_info.hcs文件中第一个设备节点必须为DAC管理器,其各项参数如表3所示:
- 由于采用了统一服务模式,device_info.hcs文件中第一个设备节点必须为DAC管理器,其各项参数必须如下设置:
+ **表 3** device_info.hcs节点参数说明
- | 成员名 | 值 |
- | --------------- | ------------------------------------------------------------------- |
- | policy | 具体配置为0,不发布服务 |
- | priority | 驱动启动优先级(0-200),值越大优先级越低,优先级相同则不保证device的加载顺序。|
- | permission | 驱动权限 |
- | moduleName | 固定为HDF_PLATFORM_DAC_MANAGER |
- | serviceName | 固定为HDF_PLATFORM_DAC_MANAGER |
- | deviceMatchAttr | 没有使用,可忽略 |
+ | 成员名 | 值 |
+ | --------------- | ------------------------------------------------------------------- |
+ | policy | 驱动服务发布的策略,DAC管理器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低,优先级相同则不保证device的加载顺序。DAC管理器具体配置为52 |
+ | permission | 驱动创建设备节点权限,DAC管理器具体配置为0664 |
+ | moduleName | 驱动名称,DAC管理器固定为HDF_PLATFORM_DAC_MANAGER |
+ | serviceName | 驱动对外发布服务的名称,DAC管理器服务名设置为HDF_PLATFORM_DAC_MANAGER |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,DAC管理器没有使用,可忽略 |
- 从第二个节点开始配置具体DAC控制器信息,此节点并不表示某一路DAC控制器,而是代表一个资源性质设备,用于描述一类DAC控制器的信息。本例只有一个DAC设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在dac_config.hcs文件中增加对应的器件属性。
+ 从第二个节点开始配置具体DAC控制器信息,此节点并不表示某一路DAC控制器,而是代表一个资源性质设备,用于描述一类DAC控制器的信息。本例只有一个DAC设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在dac_config.hcs文件中增加对应的器件属性。
- device_info.hcs配置参考:
+ - device_info.hcs配置参考
```hcs
root {
device_dac :: device {
- /* device0是DAC管理器 */
+ // device0是DAC管理器
device0 :: deviceNode {
policy = 0;
priority = 52;
@@ -184,27 +204,27 @@ DAC模块适配包含以下四个步骤:
moduleName = "HDF_PLATFORM_DAC_MANAGER";
}
}
- /* dac_virtual是DAC控制器 */
+ // dac_virtual是DAC控制器
dac_virtual :: deviceNode {
policy = 0;
priority = 56;
permission = 0644;
- moduleName = "virtual_dac_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
- serviceName = "VIRTUAL_DAC_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。
- deviceMatchAttr = "virtual_dac"; //【必要】用于配置控制器私有数据,要与dac_config.hcs中对应控制器保持一致。
- }
+ moduleName = "virtual_dac_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
+ serviceName = "VIRTUAL_DAC_DRIVER"; // 【必要】驱动对外发布服务的名称,必须唯一。
+ deviceMatchAttr = "virtual_dac"; // 【必要】用于配置控制器私有数据,要与dac_config.hcs中对应控制器保持一致。
+ }
}
```
- 添加dac_test_config.hcs器件属性文件。
- 在具体产品对应目录下新增文件用于驱动配置参数(例如hispark_taurus开发板:vendor/hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs),其中配置参数如下:
+ 在具体产品对应目录下新增文件用于驱动配置参数(例如hispark_taurus开发板:vendor/hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs),其中配置参数如下:
```hcs
root {
platform {
dac_config {
- match_attr = "virtual_dac"; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致
+ match_attr = "virtual_dac"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
template dac_device {
deviceNum = 0; // 设备号
validChannel = 0x1; // 有效通道1
@@ -219,117 +239,117 @@ DAC模块适配包含以下四个步骤:
}
```
- 需要注意的是,新增dac_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
+ 需要注意的是,新增dac_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
- 例如:本例中dac_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
+ 例如:本例中dac_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
```c
#include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs" // 配置文件相对路径
```
-3. 实例化核心层接口函数:
+3. 实例化核心层函数
- 初始化DacDevice成员。
在VirtualDacParseAndInit函数中对DacDevice成员进行初始化操作。
- ```c++
- /* 虚拟驱动自定义结构体 */
+ ```c
+ // 虚拟驱动自定义结构体
struct VirtualDacDevice {
- /*DAC设备结构体 */
+ // DAC设备结构体
struct DacDevice device;
- /* DAC设备号 */
+ // DAC设备号
uint32_t deviceNum;
- /* 有效通道 */
+ // 有效通道
uint32_t validChannel;
- /* DAC速率 */
+ // DAC速率
uint32_t rate;
};
- /* 解析并且初始化核心层DacDevice对象 */
+ // 解析并且初始化核心层DacDevice对象
static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
{
- /* 定义返回值 */
+ // 定义返回值
int32_t ret;
- /* DAC设备虚拟指针 */
+ // DAC设备虚拟指针
struct VirtualDacDevice *virtual = NULL;
(void)device;
- /* 给virtual指针开辟空间 */
+ // 给virtual指针开辟空间
virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual));
- if (virtual == NULL) {
- /*为空则返回错误参数 */
- HDF_LOGE("%s: Malloc virtual fail!", __func__);
- return HDF_ERR_MALLOC_FAIL;
- }
- /* 读取属性文件配置参数 */
- ret = VirtualDacReadDrs(virtual, node);
- if (ret != HDF_SUCCESS) {
- /* 读取失败 */
- HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
- /* 释放virtual空间 */
- OsalMemFree(virtual);
- /* 指针置为0 */
- virtual = NULL;
- return ret;
- }
- /* 初始化虚拟指针 */
- VirtualDacDeviceInit(virtual);
- /* 对DacDevice中priv对象初始化 */
- virtual->device.priv = (void *)node;
- /* 对DacDevice中devNum对象初始化 */
- virtual->device.devNum = virtual->deviceNum;
- /* 对DacDevice中ops对象初始化 */
- virtual->device.ops = &g_method;
- /* 添加DAC设备 */
- ret = DacDeviceAdd(&virtual->device);
- if (ret != HDF_SUCCESS) {
- /* 添加设备失败 */
- HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret);
- /* 释放virtual空间 */
- OsalMemFree(virtual);
- /* 虚拟指针置空 */
- virtual = NULL;
- return ret;
- }
-
- return HDF_SUCCESS;
+ if (virtual == NULL) {
+ // 为空则返回错误参数
+ HDF_LOGE("%s: Malloc virtual fail!", __func__);
+ return HDF_ERR_MALLOC_FAIL;
+ }
+ // 读取属性文件配置参数
+ ret = VirtualDacReadDrs(virtual, node);
+ if (ret != HDF_SUCCESS) {
+ // 读取失败
+ HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
+ // 释放virtual空间
+ OsalMemFree(virtual);
+ // 指针置为NULL
+ virtual = NULL;
+ return ret;
+ }
+ // 初始化虚拟指针
+ VirtualDacDeviceInit(virtual);
+ // 对DacDevice中priv对象初始化
+ virtual->device.priv = (void *)node;
+ // 对DacDevice中devNum对象初始化
+ virtual->device.devNum = virtual->deviceNum;
+ // 对DacDevice中ops对象初始化
+ virtual->device.ops = &g_method;
+ // 添加DAC设备
+ ret = DacDeviceAdd(&virtual->device);
+ if (ret != HDF_SUCCESS) {
+ // 添加设备失败
+ HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret);
+ // 释放virtual空间
+ OsalMemFree(virtual);
+ // 虚拟指针置空
+ virtual = NULL;
+ return ret;
+ }
+
+ return HDF_SUCCESS;
}
```
- 自定义结构体参考。
- 通过自定义结构体定义DAC数模转换必要的参数,在定义结构体时需要根据设备的功能参数来实现自定义结构体,从驱动的角度看,自定义结构体是参数和数据的载体,dac_config.hcs文件中传递的参数和数据会被HDF驱动模块的DacTestReadConfig函数读入,通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层DacDevice对象,例如设备号、总线号等。
+ 通过自定义结构体定义DAC数模转换必要的参数,在定义结构体时需要根据设备的功能参数来实现自定义结构体,从驱动的角度看,自定义结构体是参数和数据的载体,dac_config.hcs文件中传递的参数和数据会被HDF驱动模块的DacTestReadConfig函数读入,通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层DacDevice对象,例如设备号、总线号等。
- ```c++
- struct VirtualDacDevice {
- struct DacDevice device; //【必要】是核心层控制对象,具体描述见下面
- uint32_t deviceNum; //【必要】设备号
- uint32_t validChannel; //【必要】有效通道
- uint32_t rate; //【必要】采样率
- };
-
- /* DacDevice是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
- struct DacDevice {
- const struct DacMethod *ops;
- OsalSpinlock spin; // 自旋锁
- uint32_t devNum; // 设备号
- uint32_t chanNum; // 设备通道号
- const struct DacLockMethod *lockOps;
- void *priv;
- };
- ```
+ ```c
+ struct VirtualDacDevice {
+ struct DacDevice device; //【必要】是核心层控制对象,具体描述见下面
+ uint32_t deviceNum; //【必要】设备号
+ uint32_t validChannel; //【必要】有效通道
+ uint32_t rate; //【必要】采样率
+ };
+
+ // DacDevice是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct DacDevice {
+ const struct DacMethod *ops;
+ OsalSpinlock spin; // 自旋锁
+ uint32_t devNum; // 设备号
+ uint32_t chanNum; // 设备通道号
+ const struct DacLockMethod *lockOps;
+ void *priv;
+ };
+ ```
- 实例化DacDevice成员DacMethod。
-
+
VirtualDacWrite、VirtualDacStop、VirtualDacStart函数会在dac_virtual.c文件中进行模块功能的实例化。
-
- ```c++
+
+ ```c
static const struct DacMethod g_method = {
.write = VirtualDacWrite, // DAC设备写入值
.stop = VirtualDacStop, // 停止DAC设备
.start = VirtualDacStart, // 开始启动DAC设备
};
```
-
+
![](../public_sys-resources/icon-note.gif) **说明:**
DacDevice成员DacMethod的定义和成员说明见[接口说明](#接口说明)。
@@ -341,180 +361,182 @@ DAC模块适配包含以下四个步骤:
返回值:
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- | 状态(值) | 问题描述 |
+ HDF_STATUS相关状态(表4为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 4** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
| ---------------------- | ------------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法。 |
- | HDF_ERR_INVALID_PARAM | 参数非法。 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败。 |
- | HDF_ERR_IO | I/O 错误。 |
- | HDF_SUCCESS | 传输成功。 |
- | HDF_FAILURE | 传输失败。 |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法。|
+ | HDF_ERR_INVALID_PARAM | 参数非法。|
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败。|
+ | HDF_ERR_IO | I/O 错误。|
+ | HDF_SUCCESS | 传输成功。|
+ | HDF_FAILURE | 传输失败。|
函数说明:
- 初始化自定义结构体对象,初始化DacDevice成员,并调用核心层DacDeviceAdd函数。
+ 初始化自定义结构体对象,初始化DacDevice成员,并调用核心层DacDeviceAdd函数。
- ```c++
- static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
- {
- /* 定义返回值参数 */
- int32_t ret;
- /* DAC设备的结构体指针 */
- struct VirtualDacDevice *virtual = NULL;
- (void)device;
- /* 分配指定大小的内存 */
- virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual));
- if (virtual == NULL) {
- /* 分配内存失败 */
- HDF_LOGE("%s: Malloc virtual fail!", __func__);
- return HDF_ERR_MALLOC_FAIL;
- }
- /* 读取hcs中的node节点参数,函数定义见下方 */
- ret = VirtualDacReadDrs(virtual, node);
- if (ret != HDF_SUCCESS) {
- /* 读取节点失败 */
- HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
- goto __ERR__;
- }
- /* 初始化DAC设备指针 */
- VirtualDacDeviceInit(virtual);
- /* 节点数据传入私有数据 */
- virtual->device.priv = (void *)node;
- /* 传入设备号 */
- virtual->device.devNum = virtual->deviceNum;
- /* 传入方法 */
- virtual->device.ops = &g_method;
- /* 添加DAC设备 */
- ret = DacDeviceAdd(&virtual->device);
- if (ret != HDF_SUCCESS) {
- /* 添加DAC设备失败 */
- HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret);
- goto __ERR__;
- }
- /* 成功添加DAC设备 */
- return HDF_SUCCESS;
- __ERR__:
- /* 如果指针为空 */
- if (virtual != NULL) {
- /* 释放内存 */
- OsalMemFree(virtual);
- /* 指针置空 */
- virtual = NULL;
- }
-
- return ret;
- }
-
- static int32_t VirtualDacInit(struct HdfDeviceObject *device)
- {
- /* 定义返回值参数 */
- int32_t ret;
- /* 设备结构体子节点 */
- const struct DeviceResourceNode *childNode = NULL;
- /* 入参指针进行判断 */
- if (device == NULL || device->property == NULL) {
- /* 入参指针为空 */
- HDF_LOGE("%s: device or property is NULL", __func__);
- return HDF_ERR_INVALID_OBJECT;
- }
- /* 入参指针不为空 */
- ret = HDF_SUCCESS;
- DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
- /* 解析子节点 */
- ret = VirtualDacParseAndInit(device, childNode);
- if (ret != HDF_SUCCESS) {
- /* 解析失败 */
- break;
- }
- }
- /* 解析成功 */
- return ret;
- }
+ ```c
+ static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
+ {
+ // 定义返回值参数
+ int32_t ret;
+ // DAC设备的结构体指针
+ struct VirtualDacDevice *virtual = NULL;
+ (void)device;
+ // 分配指定大小的内存
+ virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual));
+ if (virtual == NULL) {
+ // 分配内存失败
+ HDF_LOGE("%s: Malloc virtual fail!", __func__);
+ return HDF_ERR_MALLOC_FAIL;
+ }
+ // 读取hcs中的node节点参数,函数定义见下方
+ ret = VirtualDacReadDrs(virtual, node);
+ if (ret != HDF_SUCCESS) {
+ // 读取节点失败
+ HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
+ goto __ERR__;
+ }
+ // 初始化DAC设备指针
+ VirtualDacDeviceInit(virtual);
+ // 节点数据传入私有数据
+ virtual->device.priv = (void *)node;
+ // 传入设备号
+ virtual->device.devNum = virtual->deviceNum;
+ // 传入方法
+ virtual->device.ops = &g_method;
+ // 添加DAC设备
+ ret = DacDeviceAdd(&virtual->device);
+ if (ret != HDF_SUCCESS) {
+ // 添加DAC设备失败
+ HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret);
+ goto __ERR__;
+ }
+ // 成功添加DAC设备
+ return HDF_SUCCESS;
+ __ERR__:
+ // 如果指针为空
+ if (virtual != NULL) {
+ // 释放内存
+ OsalMemFree(virtual);
+ // 指针置空
+ virtual = NULL;
+ }
+
+ return ret;
+ }
- static int32_t VirtualDacReadDrs(struct VirtualDacDevice *virtual, const struct DeviceResourceNode *node)
- {
- struct DeviceResourceIface *drsOps = NULL;
+ static int32_t VirtualDacInit(struct HdfDeviceObject *device)
+ {
+ // 定义返回值参数
+ int32_t ret;
+ // 设备结构体子节点
+ const struct DeviceResourceNode *childNode = NULL;
+ // 入参指针进行判断
+ if (device == NULL || device->property == NULL) {
+ // 入参指针为空
+ HDF_LOGE("%s: device or property is NULL", __func__);
+ return HDF_ERR_INVALID_OBJECT;
+ }
+ // 入参指针不为空
+ ret = HDF_SUCCESS;
+ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
+ // 解析子节点
+ ret = VirtualDacParseAndInit(device, childNode);
+ if (ret != HDF_SUCCESS) {
+ // 解析失败
+ break;
+ }
+ }
+ // 解析成功
+ return ret;
+ }
- /* 获取drsOps方法 */
- drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
- HDF_LOGE("%s: Invalid drs ops fail!", __func__);
- return HDF_FAILURE;
- }
- /* 将配置参数依次读出,并填充至结构体中 */
- if (drsOps->GetUint32(node, "deviceNum", &virtual->deviceNum, 0) != HDF_SUCCESS) {
- HDF_LOGE("%s: Read deviceNum fail!", __func__);
- return HDF_ERR_IO;
- }
- if (drsOps->GetUint32(node, "validChannel", &virtual->validChannel, 0) != HDF_SUCCESS) {
- HDF_LOGE("%s: Read validChannel fail!", __func__);
- return HDF_ERR_IO;
- }
- if (drsOps->GetUint32(node, "rate", &virtual->rate, 0) != HDF_SUCCESS) {
- HDF_LOGE("%s: Read rate fail!", __func__);
- return HDF_ERR_IO;
- }
- return HDF_SUCCESS;
- }
- ```
+ static int32_t VirtualDacReadDrs(struct VirtualDacDevice *virtual, const struct DeviceResourceNode *node)
+ {
+ struct DeviceResourceIface *drsOps = NULL;
+
+ // 获取drsOps方法
+ drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
+ if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
+ HDF_LOGE("%s: Invalid drs ops fail!", __func__);
+ return HDF_FAILURE;
+ }
+ // 将配置参数依次读出,并填充至结构体中
+ if (drsOps->GetUint32(node, "deviceNum", &virtual->deviceNum, 0) != HDF_SUCCESS) {
+ HDF_LOGE("%s: Read deviceNum fail!", __func__);
+ return HDF_ERR_IO;
+ }
+ if (drsOps->GetUint32(node, "validChannel", &virtual->validChannel, 0) != HDF_SUCCESS) {
+ HDF_LOGE("%s: Read validChannel fail!", __func__);
+ return HDF_ERR_IO;
+ }
+ if (drsOps->GetUint32(node, "rate", &virtual->rate, 0) != HDF_SUCCESS) {
+ HDF_LOGE("%s: Read rate fail!", __func__);
+ return HDF_ERR_IO;
+ }
+ return HDF_SUCCESS;
+ }
+ ```
- - Release函数开发参考
+ - Release函数开发参考
- 入参:
+ 入参:
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
- 返回值:
+ 返回值:
- 无。
+ 无。
- 函数说明:
+ 函数说明:
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
- ![](../public_sys-resources/icon-note.gif) **说明:**
- 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
+ ![](../public_sys-resources/icon-note.gif) **说明:**
+ 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
- ```c++
+ ```c
static void VirtualDacRemoveByNode(const struct DeviceResourceNode *node)
{
- /* 定义返回值参数 */
+ // 定义返回值参数
int32_t ret;
- /* 定义DAC设备号 */
+ // 定义DAC设备号
int16_t devNum;
- /* DAC设备结构体指针 */
+ // DAC设备结构体指针
struct DacDevice *device = NULL;
- /* DAC虚拟结构体指针 */
+ // DAC虚拟结构体指针
struct VirtualDacDevice *virtual = NULL;
- /* 设备资源接口结构体指针 */
+ // 设备资源接口结构体指针
struct DeviceResourceIface *drsOps = NULL;
- /* 通过实例入口获取设备资源 */
+ // 通过实例入口获取设备资源
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- /* 入参指判空 */
+ // 入参指判空
if (drsOps == NULL || drsOps->GetUint32 == NULL) {
- /* 指针为空 */
+ // 指针为空
HDF_LOGE("%s: invalid drs ops fail!", __func__);
return;
}
- /* 获取devNum节点的数据 */
+ // 获取devNum节点的数据
ret = drsOps->GetUint16(node, "devNum", (uint16_t *)&devNum, 0);
if (ret != HDF_SUCCESS) {
- /* 获取失败 */
+ // 获取失败
HDF_LOGE("%s: read devNum fail!", __func__);
return;
}
- /* 获取DAC设备号 */
+ // 获取DAC设备号
device = DacDeviceGet(devNum);
- /* 判断DAC设备号以及数据是否为空 */
+ // 判断DAC设备号以及数据是否为空
if (device != NULL && device->priv == node) {
- /* 为空释放DAC设备号 */
+ // 为空释放DAC设备号
DacDevicePut(device);
- /* 移除DAC设备号 */
+ // 移除DAC设备号
DacDeviceRemove(device);
virtual = (struct VirtualDacDevice *)device;
- /* 释放虚拟指针 */
+ // 释放虚拟指针
OsalMemFree(virtual);
}
return;
@@ -522,22 +544,22 @@ DAC模块适配包含以下四个步骤:
static void VirtualDacRelease(struct HdfDeviceObject *device)
{
- /* 定义设备资源子节点结构体指针 */
+ // 定义设备资源子节点结构体指
const struct DeviceResourceNode *childNode = NULL;
- /* 入参指针判空 */
+ // 入参指针判空
if (device == NULL || device->property == NULL) {
- /* 入参指针为空 */
+ // 入参指针为空
HDF_LOGE("%s: device or property is NULL", __func__);
return;
}
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
- /* 通过节点移除DAC */
+ // 通过节点移除DAC
VirtualDacRemoveByNode(childNode);
}
}
```
-4. 驱动调试:
+4. 驱动调试
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。
\ No newline at end of file
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。
diff --git a/zh-cn/device-dev/driver/driver-platform-gpio-des.md b/zh-cn/device-dev/driver/driver-platform-gpio-des.md
index a92baaada7ff503ab66fb1b10d95ee2a6e4c73e8..0ae9512755d4fc16c6c8f32d75f55e60b36a79bd 100644
--- a/zh-cn/device-dev/driver/driver-platform-gpio-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-gpio-des.md
@@ -8,10 +8,13 @@ GPIO(General-purpose input/output)即通用型输入输出。通常,GPIO
GPIO接口定义了操作GPIO管脚的标准方法集合,包括:
-- 设置管脚方向:方向可以是输入或者输出(暂不支持高阻态)。
-- 读写管脚电平值:电平值可以是低电平或高电平。
-- 设置管脚中断服务函数:设置一个管脚的中断响应函数,以及中断触发方式。
-- 使能和禁止管脚中断:禁止或使能管脚中断。
+- 设置、获取管脚方向:方向可以是输入或者输出(暂不支持高阻态)。
+
+- 读、写管脚电平值:电平值可以是低电平或高电平。
+
+- 设置、取消管脚中断服务函数:设置一个管脚的中断响应函数,以及中断触发方式。取消一个管脚的中断服务函数。
+
+- 使能、禁止管脚中断:禁止或使能管脚中断。
### 基本概念
@@ -19,11 +22,11 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。
- GPIO输入
- 输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。
+ 输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。
- GPIO输出
- 输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。
+ 输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。
### 运作机制
@@ -34,7 +37,9 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。
GPIO模块各分层作用:
- 接口层提供操作GPIO管脚的标准方法。
+
- 核心层主要提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图 1** GPIO统一服务模式结构图
@@ -45,36 +50,36 @@ GPIO模块各分层作用:
### 场景介绍
-GPIO仅是一个软件层面的概念,主要工作是GPIO管脚资源管理。开发者可以使用提供的GPIO操作接口,实现对管脚控制。
+GPIO主要是对GPIO管脚资源进行管理。开发者可以使用提供的GPIO操作接口,实现对管脚控制的具体控制。
### 接口说明
-GPIO模块提供的主要接口如表1所示。
+GPIO模块提供的主要接口如表1所示。具体API详见//drivers/hdf_core/framework/include/platform/gpio_if.h。
-**表1** GPIO驱动API接口功能介绍
+**表 1** GPIO驱动API接口功能介绍
-| 接口名 | 描述 |
+| 接口名 | 描述 |
| ------------------------------------------------------------ | ------------------------------ |
-| GpioGetByName(const char *gpioName) | 获取GPIO管脚ID |
-| int32_t GpioRead(uint16_t gpio, uint16_t *val) | 读GPIO管脚电平值 |
-| int32_t GpioWrite(uint16_t gpio, uint16_t val) | 写GPIO管脚电平值 |
-| int32_t GpioGetDir(uint16_t gpio, uint16_t *dir) | 获取GPIO管脚方向 |
-| int32_t GpioSetDir(uint16_t gpio, uint16_t dir) | 设置GPIO管脚方向 |
-| int32_t GpioUnsetIrq(uint16_t gpio, void *arg); | 取消GPIO管脚对应的中断服务函数 |
-| int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg) | 设置GPIO管脚对应的中断服务函数 |
-| int32_t GpioEnableIrq(uint16_t gpio) | 使能GPIO管脚中断 |
-| int32_t GpioDisableIrq(uint16_t gpio) | 禁止GPIO管脚中断 |
+| GpioGetByName(const char \*gpioName) | 获取GPIO管脚ID |
+| int32_t GpioRead(uint16_t gpio, uint16_t \*val) | 读GPIO管脚电平值 |
+| int32_t GpioWrite(uint16_t gpio, uint16_t val) | 写GPIO管脚电平值 |
+| int32_t GpioGetDir(uint16_t gpio, uint16_t \*dir) | 获取GPIO管脚方向 |
+| int32_t GpioSetDir(uint16_t gpio, uint16_t dir) | 设置GPIO管脚方向 |
+| int32_t GpioUnsetIrq(uint16_t gpio, void \*arg) | 取消GPIO管脚对应的中断服务函数 |
+| int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void \*arg) | 设置GPIO管脚对应的中断服务函数 |
+| int32_t GpioEnableIrq(uint16_t gpio) | 使能GPIO管脚中断 |
+| int32_t GpioDisableIrq(uint16_t gpio) | 禁止GPIO管脚中断 |
>![](../public_sys-resources/icon-note.gif) **说明:**
>本文涉及GPIO的所有接口,支持内核态及用户态使用。
### 开发步骤
-GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流程如下图所示。
+GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流程如图2所示。
-**图1** GPIO使用流程图
+**图 2** GPIO使用流程图
-![image](figures/GPIO使用流程图.png "GPIO使用流程图")
+![GPIO使用流程图](figures/GPIO使用流程图.png)
#### 确定GPIO管脚号
@@ -82,31 +87,31 @@ GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流
- 根据SOC芯片规则进行计算
- 不同SOC芯片由于其GPIO控制器型号、参数、以及控制器驱动的不同,GPIO管脚号的换算方式不一样。
+ 不同SOC芯片由于其GPIO控制器型号、参数、以及控制器驱动的不同,GPIO管脚号的换算方式不一样。
- - Hi3516DV300
+ - Hi3516DV300
- 控制器管理12组GPIO管脚,每组8个。
+ 控制器管理12组GPIO管脚,每组8个。
- GPIO号 = GPIO组索引 (0~11) \* 每组GPIO管脚数(8) + 组内偏移
+ GPIO号 = GPIO组索引 (0~11) \* 每组GPIO管脚数(8) + 组内偏移
- 举例:GPIO10_3的GPIO号 = 10 \* 8 + 3 = 83
+ 举例:GPIO10_3的GPIO号 = 10 \* 8 + 3 = 83
- - Hi3518EV300
+ - Hi3518EV300
- 控制器管理10组GPIO管脚,每组10个。
+ 控制器管理10组GPIO管脚,每组10个。
- GPIO号 = GPIO组索引 (0~9) \* 每组GPIO管脚数(10) + 组内偏移
+ GPIO号 = GPIO组索引 (0~9) \* 每组GPIO管脚数(10) + 组内偏移
- 举例:GPIO7_3的GPIO管脚号 = 7 \* 10 + 3 = 73
+ 举例:GPIO7_3的GPIO管脚号 = 7 \* 10 + 3 = 73
- 通过管脚别名获取
- 调用接口GpioGetByName进行获取,入参是该管脚的别名,接口返回值是管脚的全局ID。
+ 调用接口GpioGetByName进行获取,入参是该管脚的别名,接口返回值是管脚的全局ID。
- ```c
- GpioGetByName(const char *gpioName);
- ```
+ ```c
+ GpioGetByName(const char *gpioName);
+ ```
#### 设置GPIO管脚方向
@@ -116,15 +121,15 @@ GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流
int32_t GpioSetDir(uint16_t gpio, uint16_t dir);
```
-**表2** GpioSetDir参数和返回值描述
+**表 2** GpioSetDir参数和返回值描述
-| **参数** | **参数描述** |
+| **参数** | **参数描述** |
| ---------- | ------------------ |
-| gpio | GPIO管脚号 |
-| dir | 待设置的方向值 |
-| **返回值** | **返回值描述** |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| gpio | uint16_t类型,GPIO管脚号 |
+| dir | uint16_t类型,待设置的方向值 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 设置GPIO管脚方向成功 |
+| 负数 | 设置GPIO管脚方向失败 |
假设需要将GPIO管脚3的方向配置为输出,其使用示例如下:
@@ -132,8 +137,8 @@ int32_t GpioSetDir(uint16_t gpio, uint16_t dir);
int32_t ret;
ret = GpioSetDir(3, GPIO_DIR_OUT); // 将3号GPIO管脚配置为输出
-if (ret != 0) {
- HDF_LOGE("GpioSetDir: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("GpioSetDir: gpio set dir fail, ret:%d\n", ret);
return ret;
}
```
@@ -146,15 +151,15 @@ if (ret != 0) {
int32_t GpioGetDir(uint16_t gpio, uint16_t *dir);
```
-**表3** GpioGetDir参数和返回值描述
+**表 3** GpioGetDir参数和返回值描述
-| **参数** | **参数描述** |
+| **参数** | **参数描述** |
| ---------- | ------------------ |
-| gpio | GPIO管脚号 |
-| dir | 获取到的方向值指针 |
-| **返回值** | **返回值描述** |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| gpio | uint16_t类型,GPIO管脚号 |
+| dir | uint16_t类型指针,获取到的方向值 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 获取GPIO管脚方向成功 |
+| 负数 | 获取GPIO管脚方向失败 |
假设需要获取GPIO管脚3的方向,其使用示例如下:
@@ -163,8 +168,8 @@ int32_t ret;
uin16_t dir;
ret = GpioGetDir(3, &dir); // 获取3号GPIO管脚方向
-if (ret != 0) {
- HDF_LOGE("GpioGetDir: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("GpioGetDir: gpio get dir fail, ret:%d\n", ret);
return ret;
}
```
@@ -177,15 +182,15 @@ if (ret != 0) {
int32_t GpioRead(uint16_t gpio, uint16_t *val);
```
-**表4** GpioRead参数和返回值描述
+**表 4** GpioRead参数和返回值描述
-| **参数** | **参数描述** |
+| **参数** | **参数描述** |
| ---------- | -------------------- |
-| gpio | GPIO管脚号 |
-| val | 接收读取电平值的指针 |
-| **返回值** | **返回值描述** |
-| 0 | 读取成功 |
-| 负数 | 读取失败 |
+| gpio | uint16_t类型,GPIO管脚号 |
+| val | uint16_t类型指针,接收读取电平值 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 读取GPIO管脚电平值成功 |
+| 负数 | 读取GPIO管脚电平值失败 |
假设需要读取GPIO管脚3的电平值,其使用示例如下:
@@ -194,8 +199,8 @@ int32_t ret;
uint16_t val;
ret = GpioRead(3, &val); // 读取3号GPIO管脚电平值
-if (ret != 0) {
- HDF_LOGE("GpioRead: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("GpioRead: gpio read fail, ret:%d\n", ret);
return ret;
}
```
@@ -208,15 +213,15 @@ if (ret != 0) {
int32_t GpioWrite(uint16_t gpio, uint16_t val);
```
-**表5** GpioWrite参数和返回值描述
+**表 5** GpioWrite参数和返回值描述
-| **参数** | **参数描述** |
+| **参数** | **参数描述** |
| ---------- | ------------------ |
-| gpio | GPIO管脚号 |
-| val | 待写入的电平值 |
-| **返回值** | **返回值描述** |
-| 0 | 写入成功 |
-| 负数 | 写入失败 |
+| gpio | uint16_t类型,GPIO管脚号 |
+| val | uint16_t类型,待写入的电平值 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 写入GPIO管脚电平值成功 |
+| 负数 | 写入GPIO管脚电平值失败 |
假设需要给GPIO管脚3写入低电平值,其使用示例如下:
@@ -224,8 +229,8 @@ int32_t GpioWrite(uint16_t gpio, uint16_t val);
int32_t ret;
ret = GpioWrite(3, GPIO_VAL_LOW); // 给3号GPIO管脚写入低电平值
-if (ret != 0) {
- HDF_LOGE("GpioRead: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("GpioWrite: gpio write fail, ret:%d\n", ret);
return ret;
}
```
@@ -238,17 +243,17 @@ if (ret != 0) {
int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg);
```
-**表6** GpioSetIrq参数和返回值描述
+**表 6** GpioSetIrq参数和返回值描述
-| **参数** | **参数描述** |
+| **参数** | **参数描述** |
| ---------- | ------------------------ |
-| gpio | GPIO管脚号 |
-| mode | 中断触发模式 |
-| func | 中断服务程序 |
-| arg | 传递给中断服务程序的入参 |
-| **返回值** | **返回值描述** |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| gpio | uint16_t类型,GPIO管脚号 |
+| mode | uint16_t类型,中断触发模式 |
+| func | 函数指针,中断服务程序 |
+| arg | 无类型指针,传递给中断服务程序的入参 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 设置GPIO管脚中断成功 |
+| 负数 | 设置GPIO管脚中断失败 |
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**
> 同一时间,只能为某个GPIO管脚设置一个中断服务函数,如果重复调用GpioSetIrq函数,则之前设置的中断服务函数会被取代。
@@ -261,15 +266,15 @@ int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg);
int32_t GpioUnsetIrq(uint16_t gpio, void *arg);
```
-**表7** GpioUnsetIrq参数和返回值描述
+**表 7** GpioUnsetIrq参数和返回值描述
-| **参数** | **参数描述** |
+| **参数** | **参数描述** |
| ---------- | -------------- |
-| gpio | GPIO管脚号 |
-| arg | GPIO中断数据 |
+| gpio | uint16_t类型,GPIO管脚号 |
+| arg | 无类型指针,GPIO中断数据 |
| **返回值** | **返回值描述** |
-| 0 | 取消成功 |
-| 负数 | 取消失败 |
+| HDF_SUCCESS | 取消GPIO管脚中断成功 |
+| 负数 | 取消GPIO管脚中断失败 |
#### 使能GPIO管脚中断
@@ -279,14 +284,14 @@ int32_t GpioUnsetIrq(uint16_t gpio, void *arg);
int32_t GpioEnableIrq(uint16_t gpio);
```
-**表8** GpioEnableIrq参数和返回值描述
+**表 8** GpioEnableIrq参数和返回值描述
-| **参数** | **参数描述** |
+| **参数** | **参数描述** |
| ---------- | -------------- |
-| gpio | GPIO管脚号 |
+| gpio | uint16_t类型,GPIO管脚号 |
| **返回值** | **返回值描述** |
-| 0 | 使能成功 |
-| 负数 | 使能失败 |
+| HDF_SUCCESS | 使能GPIO管脚中断成功 |
+| 负数 | 使能GPIO管脚中断失败 |
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**
> 必须通过此函数使能管脚中断,之前设置的中断服务函数才能被正确响应。
@@ -298,51 +303,51 @@ int32_t GpioEnableIrq(uint16_t gpio);
```c
int32_t GpioDisableIrq(uint16_t gpio);
```
-**表9** GpioDisableIrq参数和返回值描述
+**表 9** GpioDisableIrq参数和返回值描述
-| **参数** | **参数描述** |
+| **参数** | **参数描述**|
| ---------- | -------------- |
-| gpio | GPIO管脚号 |
+| gpio | uint16_t类型,GPIO管脚号 |
| **返回值** | **返回值描述** |
-| 0 | 禁止成功 |
-| 负数 | 禁止失败 |
+| HDF_SUCCESS | 禁止GPIO管脚中断成功 |
+| 负数 | 禁止GPIO管脚中断失败 |
中断相关操作示例:
```c
-/* 中断服务函数*/
+// 中断服务函数
int32_t MyCallBackFunc(uint16_t gpio, void *data)
{
- HDF_LOGI("%s: gpio:%u interrupt service in data\n", __func__, gpio);
- return 0;
+ HDF_LOGI("MyCallBackFunc: gpio:%u interrupt service in data.\n", gpio);
+ return HDF_SUCCESS;
}
int32_t ret;
-/* 设置中断服务程序为MyCallBackFunc,入参为NULL,中断触发模式为上升沿触发 */
+// 设置中断服务程序为MyCallBackFunc,入参为NULL,中断触发模式为上升沿触发
ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL);
-if (ret != 0) {
- HDF_LOGE("GpioSetIrq: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("GpioSetIrq: gpio set irq fail, ret:%d\n", ret);
return ret;
}
-/* 使能3号GPIO管脚中断 */
+// 使能3号GPIO管脚中断
ret = GpioEnableIrq(3);
-if (ret != 0) {
- HDF_LOGE("GpioEnableIrq: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("GpioEnableIrq: gpio enable irq fail, ret:%d\n", ret);
return ret;
}
-/* 禁止3号GPIO管脚中断 */
+// 禁止3号GPIO管脚中断
ret = GpioDisableIrq(3);
-if (ret != 0) {
- HDF_LOGE("GpioDisableIrq: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("GpioDisableIrq: gpio disable irqfail, ret:%d\n", ret);
return ret;
}
-/* 取消3号GPIO管脚中断服务程序 */
+// 取消3号GPIO管脚中断服务程序
ret = GpioUnsetIrq(3, NULL);
-if (ret != 0) {
- HDF_LOGE("GpioUnSetIrq: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("GpioUnSetIrq: gpio unset irq fail, ret:%d\n", ret);
return ret;
}
```
@@ -363,65 +368,66 @@ if (ret != 0) {
static uint32_t g_irqCnt;
-/* 中断服务函数*/
+// 中断服务函数
static int32_t TestCaseGpioIrqHandler(uint16_t gpio, void *data)
{
- HDF_LOGE("%s: irq triggered! on gpio:%u, in data", __func__, gpio);
- g_irqCnt++; /* 如果中断服务函数触发执行,则将全局中断计数加1 */
+ HDF_LOGE("TestCaseGpioIrqHandler: irq triggered! on gpio:%u, in data", gpio);
+ g_irqCnt++; // 如果中断服务函数触发执行,则将全局中断计数加1
return GpioDisableIrq(gpio);
}
-/* 测试用例函数 */
+// 测试用例函数
static int32_t TestCaseGpioIrqEdge(void)
{
int32_t ret;
uint16_t valRead;
uint16_t mode;
- uint16_t gpio = 83; /* 待测试的GPIO管脚号 */
+ uint16_t gpio = 84; // 待测试的GPIO管脚号
uint32_t timeout;
- /* 将管脚方向设置为输出 */
+ // 将管脚方向设置为输出
ret = GpioSetDir(gpio, GPIO_DIR_OUT);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: set dir fail! ret:%d\n", __func__, ret);
+ HDF_LOGE("TestCaseGpioIrqEdge: set dir fail! ret:%d\n", ret);
return ret;
}
- /* 先禁止该管脚中断 */
+ // 先禁止该管脚中断
ret = GpioDisableIrq(gpio);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: disable irq fail! ret:%d\n", __func__, ret);
+ HDF_LOGE("TestCaseGpioIrqEdge: disable irq fail! ret:%d\n", ret);
return ret;
}
- /* 为管脚设置中断服务函数,触发模式为上升沿和下降沿共同触发 */
+ // 为管脚设置中断服务函数,触发模式为上升沿和下降沿共同触发
mode = OSAL_IRQF_TRIGGER_RISING | OSAL_IRQF_TRIGGER_FALLING;
- HDF_LOGE("%s: mode:%0x\n", __func__, mode);
+ HDF_LOGE("TestCaseGpioIrqEdge: mode:%0x\n", mode);
ret = GpioSetIrq(gpio, mode, TestCaseGpioIrqHandler, NULL);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: set irq fail! ret:%d\n", __func__, ret);
+ HDF_LOGE("TestCaseGpioIrqEdge: set irq fail! ret:%d\n", ret);
return ret;
}
- /* 使能此管脚中断 */
+ // 使能此管脚中断
ret = GpioEnableIrq(gpio);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: enable irq fail! ret:%d\n", __func__, ret);
+ HDF_LOGE("TestCaseGpioIrqEdge: enable irq fail! ret:%d\n", ret);
(void)GpioUnsetIrq(gpio, NULL);
return ret;
}
- g_irqCnt = 0; /* 清除全局计数器 */
- timeout = 0; /* 等待时间清零 */
- /* 等待此管脚中断服务函数触发,等待超时时间为1000毫秒 */
+ g_irqCnt = 0; // 清除全局计数器
+ timeout = 0; // 等待时间清零
+ // 等待此管脚中断服务函数触发,等待超时时间为1000毫秒
while (g_irqCnt <= 0 && timeout < 1000) {
(void)GpioRead(gpio, &valRead);
(void)GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW);
- HDF_LOGE("%s: wait irq timeout:%u\n", __func__, timeout);
- OsalMDelay(200); /* wait for irq trigger */
+ HDF_LOGE("TestCaseGpioIrqEdge: wait irq timeout:%u\n", timeout);
+ OsalMDelay(200); // 等待中断触发
timeout += 200;
}
(void)GpioUnsetIrq(gpio, NULL);
+ HDF_LOGI("TestCaseGpioIrqEdge: function tests end, g_irqCnt:%u", g_irqCnt);
return (g_irqCnt > 0) ? HDF_SUCCESS : HDF_FAILURE;
}
-```
\ No newline at end of file
+```
diff --git a/zh-cn/device-dev/driver/driver-platform-gpio-develop.md b/zh-cn/device-dev/driver/driver-platform-gpio-develop.md
index 7b15fc2935bfbde69ee7622b11ab9ad88d6f54f2..5c8a2feea00c28ed0493de269cadef113ea06f47 100755
--- a/zh-cn/device-dev/driver/driver-platform-gpio-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-gpio-develop.md
@@ -12,23 +12,25 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。
- GPIO输入
- 输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。
+ 输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。
- GPIO输出
- 输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。
+ 输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。
### 运作机制
-在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。GPIO模块接口适配模式采用统一服务模式(如图1所示)。
+在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。GPIO模块采用统一服务模式(如图1所示)。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
GPIO模块各分层作用:
-- 接口层提供操作GPIO管脚的标准方法。
-- 核心层主要提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。
-- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
+- 接口层:提供操作GPIO管脚的标准方法。
+
+- 核心层:提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。
+
+- 适配层:由驱动适配者将钩子函数的功能实例化,实现与硬件相关的具体功能。
**图 1** GPIO统一服务模式结构图
@@ -38,7 +40,7 @@ GPIO模块各分层作用:
### 场景介绍
-GPIO仅是一个软件层面的概念,主要工作是GPIO管脚资源管理。驱动开发者可以使用GPIO模块提供的操作接口,实现对管脚的控制。当驱动开发者需要将GPIO适配到OpenHarmony时,需要进行GPIO驱动适配。下文将介绍如何进行GPIO驱动适配。
+GPIO主要是对GPIO管脚资源进行管理。驱动开发者可以使用GPIO模块提供的操作接口,实现对管脚的具体控制。当驱动开发者需要将GPIO适配到OpenHarmony时,需要进行GPIO驱动适配。下文将介绍如何进行GPIO驱动适配。
### 接口说明
@@ -55,388 +57,424 @@ struct GpioMethod {
int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir);
int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir);
int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq); // 【预留】
- int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg);
+ int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode);
int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local);
int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local);
int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local);
}
```
-**表1** GpioMethod结构体成员的钩子函数功能说明
+**表 1** GpioMethod结构体成员的钩子函数功能说明
-| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
+| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
-| write | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
val:uint16_t类型,电平传入值 | 无 | HDF_STATUS相关状态 | GPIO引脚写入电平值 |
-| read | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识 | val:uint16_t类型指针,用于传出电平值。 | HDF_STATUS相关状态 | GPIO引脚读取电平值 |
-| setDir | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
dir:uint16_t类型,管脚方向传入值 | 无 | HDF_STATUS相关状态 | 设置GPIO引脚输入/输出方向 |
-| getDir | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | dir:uint16_t类型指针,用于传出管脚方向值 | HDF_STATUS相关状态 | 读GPIO引脚输入/输出方向 |
-| setIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
mode:uint16_t类型,表示触发模式(边沿或电平)
func:函数指针,中断服务程序;
arg:void指针,中断服务程序入参 | 无 | HDF_STATUS相关状态 | 将GPIO引脚设置为中断模式 |
-| unsetIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 取消GPIO中断设置 |
-| enableIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 使能GPIO管脚中断 |
-| disableIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 禁止GPIO管脚中断 |
+| write | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
val:uint16_t类型,电平传入值 | 无 | HDF_STATUS相关状态 | GPIO引脚写入电平值 |
+| read | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | val:uint16_t类型指针,用于传出电平值。 | HDF_STATUS相关状态 | GPIO引脚读取电平值 |
+| setDir | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
dir:uint16_t类型,管脚方向传入值 | 无 | HDF_STATUS相关状态 | 设置GPIO引脚输入/输出方向 |
+| getDir | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | dir:uint16_t类型指针,用于传出管脚方向值 | HDF_STATUS相关状态 | 读GPIO引脚输入/输出方向 |
+| setIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
mode:uint16_t类型,表示触发模式(边沿或电平) | 无 | HDF_STATUS相关状态 | 将GPIO引脚设置为中断模式 |
+| unsetIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 取消GPIO中断设置 |
+| enableIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 使能GPIO管脚中断 |
+| disableIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 禁止GPIO管脚中断 |
### 开发步骤
GPIO模块适配包含以下四个步骤:
-- 实例化驱动入口。
-- 配置属性文件。
-- 实例化GPIO控制器对象。
-- 驱动调试。
+- 实例化驱动入口
+
+- 配置属性文件
+
+- 实例化GPIO控制器对象
+
+- 驱动调试
### 开发实例
下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/gpio/gpio_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
- GPIO驱动入口开发参考:
-
- ```c
- struct HdfDriverEntry g_gpioDriverEntry = {
- .moduleVersion = 1,
- .Bind = Pl061GpioBind, // GPIO不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作
- .Init = Pl061GpioInit, // 见Init参考
- .Release = Pl061GpioRelease, // 见Release参考
- .moduleName = "hisi_pl061_driver", // 【必要且需要与HCS文件中里面的moduleName匹配】
- };
- HDF_INIT(g_gpioDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
- ```
-
-2. 配置属性文件。
-
- 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以一个GPIO控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息。器件属性值与核心层GpioCntlr成员的默认值或限制范围有密切关系,需要在gpio_config.hcs中配置器件属性。
-
- - device_info.hcs 配置参考:
-
- 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
-
- ```c
- root {
- device_info {
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_gpio :: device {
- device0 :: deviceNode {
- policy = 0; // 等于0,不需要发布服务
- priority = 10; // 驱动启动优先级
- permission = 0644; // 驱动创建设备节点权限
- moduleName = "hisi_pl061_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致
- deviceMatchAttr = "hisilicon_hi35xx_pl061"; // 【必要】用于配置控制器私有数据,要与gpio_config.hcs中
- // 对应控制器保持一致,其他控制器信息也在文件中
- }
- }
- }
- }
- }
- ```
-
- - gpio_config.hcs配置参考:
-
- 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs文件配置器件属性,其中配置参数如下:
-
- ```c
- root {
- platform {
- gpio_config {
- controller_0x120d0000 {
- match_attr = "hisilicon_hi35xx_pl061"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致
- groupNum = 12; // 【必要】GPIO组索引,需要根据设备情况填写
- bitNum = 8; // 【必要】每组GPIO管脚数
- regBase = 0x120d0000; // 【必要】物理基地址
- regStep = 0x1000; // 【必要】寄存器偏移步进
- irqStart = 48; // 【必要】开启中断
- irqShare = 0; // 【必要】共享中断
- }
- ...
- }
- }
- }
- ```
-
- 需要注意的是,新增gpio_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // 配置文件相对路径
- ```
-
-3. 实例化GPIO控制器对象。
-
- 完成驱动入口注册之后,下一步就是以核心层GpioCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化GpioCntlr成员GpioMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
-
- - 驱动适配者自定义结构体参考。
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,而且gpio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层GpioCntlr对象,例如索引、管脚数等。
-
- ```c
- //GPIO分组信息定义
- struct Pl061GpioGroup {
- struct GpioCntlr cntlr; // 【必要】是核心层控制对象,其成员定义见下面。
- volatile unsigned char *regBase; // 【必要】寄存器基地址。
- unsigned int index;
- unsigned int irq;
- OsalIRQHandle irqFunc;
- OsalSpinlock lock;
- uint32_t irqSave;
- bool irqShare;
- struct PlatformDumper *dumper;
- char *dumperName;
- };
-
- struct Pl061GpioData {
- volatile unsigned char *regBase; // 【必要】寄存器基地址。
- uint32_t phyBase; // 【必要】物理基址。
- uint32_t regStep; // 【必要】寄存器偏移步进。
- uint32_t irqStart; // 【必要】中断开启。
- uint16_t groupNum; // 【必要】用于描述厂商的GPIO端口号的参数。
- uint16_t bitNum; // 【必要】用于描述厂商的GPIO端口号的参数。
- uint8_t irqShare; // 【必要】共享中断。
- struct Pl061GpioGroup *groups; // 【可选】根据厂商需要设置。
- struct GpioInfo *gpioInfo;
- void *priv;
- };
-
- struct GpioInfo {
- struct GpioCntlr *cntlr;
- char name[GPIO_NAME_LEN];
- OsalSpinlock spin;
- uint32_t irqSave;
- struct GpioIrqRecord *irqRecord;
- };
- // GpioCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
- struct GpioCntlr {
- struct PlatformDevice device;
- struct GpioMethod *ops;
- uint16_t start;
- uint16_t count;
- struct GpioInfo *ginfos;
- bool isAutoAlloced;
- void *priv;
- };
- ```
-
- - GpioCntlr成员钩子函数结构体GpioMethod的实例化,其他成员在Init函数中初始化。
-
- ```c
- //GpioMethod结构体成员都是钩子函数,驱动适配者需要根据表1完成相应的函数功能。
- static struct GpioMethod g_method = {
- .request = NULL,
- .release = NULL,
- .write = Pl061GpioWrite, // 写管脚
- .read = Pl061GpioRead, // 读管脚
- .setDir = Pl061GpioSetDir, // 设置管脚方向
- .getDir = Pl061GpioGetDir, // 获取管脚方向
- .toIrq = NULL,
- .setIrq = Pl061GpioSetIrq, // 设置管脚中断,如不具备此能力可忽略
- .unsetIrq = Pl061GpioUnsetIrq, // 取消管脚中断设置,如不具备此能力可忽略
- .enableIrq = Pl061GpioEnableIrq, // 使能管脚中断,如不具备此能力可忽略
- .disableIrq = Pl061GpioDisableIrq, // 禁止管脚中断,如不具备此能力可忽略
- };
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- **表2** Init函数说明
-
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化GpioCntlr成员,调用核心层GpioCntlrAdd函数,接入VFS(可选)。
-
- ```c
- static struct Pl061GpioData g_pl061 = {
- .groups = NULL,
- .groupNum = PL061_GROUP_MAX,
- .bitNum = PL061_BIT_MAX,
- };
-
- static int32_t Pl061GpioInitGroups(struct Pl061GpioData *pl061)
- {
- int32_t ret;
- uint16_t i;
- struct Pl061GpioGroup *groups = NULL;
-
- if (pl061 == NULL) {
- return HDF_ERR_INVALID_PARAM;
- }
-
- groups = (struct Pl061GpioGroup *)OsalMemCalloc(sizeof(*groups) * pl061->groupNum);
- if (groups == NULL) {
- return HDF_ERR_MALLOC_FAIL;
- }
- pl061->groups = groups;
-
- for (i = 0; i < pl061->groupNum; i++) {
- // 相关信息初始化
- groups[i].index = i;
- groups[i].regBase = pl061->regBase + i * pl061->regStep;
- groups[i].irq = pl061->irqStart + i;
- groups[i].irqShare = pl061->irqShare;
- groups[i].cntlr.start = i * pl061->bitNum;
- groups[i].cntlr.count = pl061->bitNum;
- groups[i].cntlr.ops = &g_method;
- groups[i].cntlr.ginfos = &pl061->gpioInfo[i * pl061->bitNum];
-
- if ((ret = OsalSpinInit(&groups[i].lock)) != HDF_SUCCESS) {
- goto ERR_EXIT;
- }
-
- ret = GpioCntlrAdd(&groups[i].cntlr); // 向HDF core中添加相关信息
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: err add controller(%hu:%hu):%d", __func__,
- groups[i].cntlr.start, groups[i].cntlr.count, ret);
- (void)OsalSpinDestroy(&groups[i].lock);
- goto ERR_EXIT;
- }
- }
- return HDF_SUCCESS;
-
- ERR_EXIT:
- while (i-- > 0) {
- GpioCntlrRemove(&groups[i].cntlr);
- (void)OsalSpinDestroy(&groups[i].lock);
- }
- pl061->groups = NULL;
- OsalMemFree(groups);
- return ret;
- }
-
- static int32_t Pl061GpioInit(struct HdfDeviceObject *device)
- {
- int32_t ret;
- struct Pl061GpioData *pl061 = &g_pl061;
-
- if (device == NULL || device->property == NULL) {
- HDF_LOGE("%s: device or property null!", __func__);
- return HDF_ERR_INVALID_OBJECT;
- }
-
- pl061->gpioInfo = OsalMemCalloc(sizeof(struct GpioInfo) * GPIO_MAX_INFO_NUM);
- if (pl061->gpioInfo == NULL) {
- HDF_LOGE("%s: failed to calloc gpioInfo!", __func__);
- return HDF_ERR_MALLOC_FAIL;
- }
-
- ret = Pl061GpioReadDrs(pl061, device->property); // 利用从gpio_config.HCS文件读取的属性值来初始化自定义结构体对象成员
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: failed to read drs:%d", __func__, ret);
- return ret;
- }
-
- if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 ||
- pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) {
- HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl061->bitNum);
- return HDF_ERR_INVALID_PARAM;
- }
-
- pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep); //地址映射
- if (pl061->regBase == NULL) {
- HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase);
- return HDF_ERR_IO;
- }
-
- ret = Pl061GpioInitGroups(pl061); //group信息初始化,并添加到HDF核心层
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: err init groups:%d", __func__, ret);
- OsalIoUnmap((void *)pl061->regBase);
- pl061->regBase = NULL;
- return ret;
- }
- pl061->priv = (void *)device->property;
- device->priv = (void *)pl061;
- Pl061GpioDebug(pl061);
-
- #ifdef PL061_GPIO_USER_SUPPORT
- if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) {
- HDF_LOGE("%s: add vfs fail!", __func__);
- }
- #endif
- HDF_LOGI("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device));
- return HDF_SUCCESS;
- }
- ```
-
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
-
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。
-
- ```c
- static void Pl061GpioUninitGroups(struct Pl061GpioData *pl061)
- {
- uint16_t i;
- struct Pl061GpioGroup *group = NULL;
-
- for (i = 0; i < pl061->groupNum; i++) {
- group = &pl061->groups[i];
- GpioDumperDestroy(&pl061->groups[i]);
- GpioCntlrRemove(&group->cntlr); // 从HDF核心层删除
- }
-
- OsalMemFree(pl061->groups);
- pl061->groups = NULL;
- }
-
- static void Pl061GpioRelease(struct HdfDeviceObject *device)
- {
- struct Pl061GpioData *pl061 = NULL;
-
- HDF_LOGI("%s: enter", __func__);
- if (device == NULL) {
- HDF_LOGE("%s: device is null!", __func__);
- return;
- }
-
- #ifdef PL061_GPIO_USER_SUPPORT
- GpioRemoveVfs();
- #endif
-
- pl061 = (struct Pl061GpioData *)device->priv;
- if (pl061 == NULL) {
- HDF_LOGE("%s: device priv is null", __func__);
- return;
- }
-
- Pl061GpioUninitGroups(pl061);
- OsalMemFree(pl061->gpioInfo);
- pl061->gpioInfo = NULL;
- OsalIoUnmap((void *)pl061->regBase);
- pl061->regBase = NULL;
- }
- ```
-
-4. 驱动调试。
-
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如GPIO控制状态,中断响应情况等。
\ No newline at end of file
+1. 实例化驱动入口
+
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+
+ GPIO驱动入口开发参考:
+
+ ```c
+ struct HdfDriverEntry g_gpioDriverEntry = {
+ .moduleVersion = 1,
+ .Bind = Pl061GpioBind, // GPIO不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作
+ .Init = Pl061GpioInit, // 挂接Gpio模块Init实例化
+ .Release = Pl061GpioRelease, // 挂接Gpio模块Release实例化
+ .moduleName = "hisi_pl061_driver", // 【必要且需要与HCS文件中里面的moduleName匹配】
+ };
+ HDF_INIT(g_gpioDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
+
+2. 配置属性文件
+
+ 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以一个GPIO控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在gpio_config.hcs文件中增加对应的器件属性。器件属性值与核心层GpioCntlr成员的默认值或限制范围有密切关系,需要在gpio_config.hcs中配置器件属性。
+
+ 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为GPIO管理器,其各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,GPIO管理器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。GPIO管理器具体配置为10 |
+ | permission | 驱动创建设备节点权限,GPIO管理器具体配置为0664 |
+ | moduleName | 驱动名称,GPIO管理器固定为HDF_PLATFORM_GPIO_MANAGER |
+ | serviceName | 驱动对外发布服务的名称,GPIO管理器服务名设置为HDF_PLATFORM_GPIO_MANAGER |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,GPIO管理器没有使用,可忽略 |
+
+ - device_info.hcs 配置参考:
+
+ 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
+
+ ```c
+ root {
+ device_info {
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_gpio :: device {
+ device0 :: deviceNode {
+ policy = 2;
+ priority = 10;
+ permission = 0644;
+ moduleName = "HDF_PLATFORM_GPIO_MANAGER";
+ serviceName = "HDF_PLATFORM_GPIO_MANAGER";
+ }
+ device1 :: deviceNode {
+ policy = 0; // 等于0,不需要发布服务
+ priority = 10; // 驱动启动优先级
+ permission = 0644; // 驱动创建设备节点权限
+ moduleName = "hisi_pl061_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致
+ deviceMatchAttr = "hisilicon_hi35xx_pl061"; // 【必要】用于配置控制器私有数据,要与gpio_config.hcs中对应控制器保持一致,其他控制器信息也在文件中
+ }
+ }
+ }
+ }
+ }
+ ```
+
+ - gpio_config.hcs配置参考:
+
+ 此处以Hi3516DV300为例,给出HCS配置参考。其中部分字段为Hi3516DV300特有功能,驱动适配者可根据需要进行删除或添加字段。
+
+ ```c
+ root {
+ platform {
+ gpio_config {
+ controller_0x120d0000 {
+ match_attr = "hisilicon_hi35xx_pl061"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致
+ groupNum = 12; // 【必要】GPIO组索引,需要根据设备情况填写
+ bitNum = 8; // 【必要】每组GPIO管脚数
+ regBase = 0x120d0000; // 【必要】物理基地址
+ regStep = 0x1000; // 【必要】寄存器偏移步进
+ irqStart = 48; // 【必要】开启中断
+ irqShare = 0; // 【必要】共享中断
+ }
+ template gpio_info { // gpio_info模板
+ gpioCustomName = ""; // gpio管脚默认名称
+ }
+ GPIO0 :: gpio_info {
+ gpioCustomName = "GPIO0_0";
+ }
+ ......
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增gpio_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // 配置文件相对路径
+ ```
+
+ 本例基于Hi3516DV300开发板的小型系统LiteOS内核运行,对应的hdf.hcs文件路径为vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs以及//device/hisilicon/hispark_taurus/sdk_liteos/hdf_config/hdf.hcs。驱动适配者需根据实际情况选择对应路径下的文件进行修改。
+
+3. 实例化GPIO控制器对象
+
+ 完成驱动入口注册之后,下一步就是以核心层GpioCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化GpioCntlr成员GpioMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
+
+ - 驱动适配者自定义结构体参考。
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,而且gpio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层GpioCntlr对象,例如索引、管脚数等。
+
+ ```c
+ //GPIO分组信息定义
+ struct Pl061GpioGroup {
+ struct GpioCntlr cntlr; // 【必要】是核心层控制对象,其成员定义见下面。
+ volatile unsigned char *regBase; // 【必要】寄存器基地址。
+ unsigned int index;
+ unsigned int irq;
+ OsalIRQHandle irqFunc;
+ OsalSpinlock lock;
+ uint32_t irqSave;
+ bool irqShare;
+ struct PlatformDumper *dumper;
+ char *dumperName;
+ };
+
+ struct Pl061GpioData {
+ volatile unsigned char *regBase; // 【必要】寄存器基地址。
+ uint32_t phyBase; // 【必要】物理基址。
+ uint32_t regStep; // 【必要】寄存器偏移步进。
+ uint32_t irqStart; // 【必要】中断开启。
+ uint16_t groupNum; // 【必要】用于描述厂商的GPIO端口号的参数。
+ uint16_t bitNum; // 【必要】用于描述厂商的GPIO端口号的参数。
+ uint8_t irqShare; // 【必要】共享中断。
+ struct Pl061GpioGroup *groups; // 【可选】根据厂商需要设置。
+ struct GpioInfo *gpioInfo;
+ void *priv;
+ };
+
+ struct GpioInfo {
+ struct GpioCntlr *cntlr;
+ char name[GPIO_NAME_LEN];
+ OsalSpinlock spin;
+ uint32_t irqSave;
+ struct GpioIrqRecord *irqRecord;
+ };
+ // GpioCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct GpioCntlr {
+ struct PlatformDevice device;
+ struct GpioMethod *ops;
+ uint16_t start;
+ uint16_t count;
+ struct GpioInfo *ginfos;
+ bool isAutoAlloced;
+ void *priv;
+ };
+ ```
+
+ - GpioCntlr成员钩子函数结构体GpioMethod的实例化。
+
+ ```c
+ //GpioMethod结构体成员都是钩子函数,驱动适配者需要根据表1完成相应的函数功能。
+ static struct GpioMethod g_method = {
+ .request = NULL,
+ .release = NULL,
+ .write = Pl061GpioWrite, // 写管脚
+ .read = Pl061GpioRead, // 读管脚
+ .setDir = Pl061GpioSetDir, // 设置管脚方向
+ .getDir = Pl061GpioGetDir, // 获取管脚方向
+ .toIrq = NULL,
+ .setIrq = Pl061GpioSetIrq, // 设置管脚中断,如不具备此能力可忽略
+ .unsetIrq = Pl061GpioUnsetIrq, // 取消管脚中断设置,如不具备此能力可忽略
+ .enableIrq = Pl061GpioEnableIrq, // 使能管脚中断,如不具备此能力可忽略
+ .disableIrq = Pl061GpioDisableIrq, // 禁止管脚中断,如不具备此能力可忽略
+ };
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化GpioCntlr成员,调用核心层GpioCntlrAdd函数,接入VFS(可选)。
+
+ ```c
+ static struct Pl061GpioData g_pl061 = {
+ .groups = NULL,
+ .groupNum = PL061_GROUP_MAX,
+ .bitNum = PL061_BIT_MAX,
+ };
+
+ static int32_t Pl061GpioInitGroups(struct Pl061GpioData *pl061)
+ {
+ int32_t ret;
+ uint16_t i;
+ struct Pl061GpioGroup *groups = NULL;
+
+ if (pl061 == NULL) {
+ return HDF_ERR_INVALID_PARAM;
+ }
+
+ groups = (struct Pl061GpioGroup *)OsalMemCalloc(sizeof(*groups) * pl061->groupNum);
+ if (groups == NULL) {
+ return HDF_ERR_MALLOC_FAIL;
+ }
+ pl061->groups = groups;
+
+ for (i = 0; i < pl061->groupNum; i++) {
+ // 相关信息初始化
+ groups[i].index = i;
+ groups[i].regBase = pl061->regBase + i * pl061->regStep;
+ groups[i].irq = pl061->irqStart + i;
+ groups[i].irqShare = pl061->irqShare;
+ groups[i].cntlr.start = i * pl061->bitNum;
+ groups[i].cntlr.count = pl061->bitNum;
+ groups[i].cntlr.ops = &g_method;
+ groups[i].cntlr.ginfos = &pl061->gpioInfo[i * pl061->bitNum];
+
+ if ((ret = OsalSpinInit(&groups[i].lock)) != HDF_SUCCESS) {
+ goto ERR_EXIT;
+ }
+
+ ret = GpioCntlrAdd(&groups[i].cntlr); // 向HDF core中添加相关信息
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: err add controller(%hu:%hu):%d", __func__,
+ groups[i].cntlr.start, groups[i].cntlr.count, ret);
+ (void)OsalSpinDestroy(&groups[i].lock);
+ goto ERR_EXIT;
+ }
+ ret = GpioDumperCreate(&pl061->groups[i]);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: create dumper failed:%d", __func__, ret);
+ return ret;
+ }
+ }
+ return HDF_SUCCESS;
+
+ ERR_EXIT:
+ while (i-- > 0) {
+ GpioCntlrRemove(&groups[i].cntlr);
+ (void)OsalSpinDestroy(&groups[i].lock);
+ }
+ pl061->groups = NULL;
+ OsalMemFree(groups);
+ return ret;
+ }
+
+ static int32_t Pl061GpioInit(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ struct Pl061GpioData *pl061 = &g_pl061;
+
+ if (device == NULL || device->property == NULL) {
+ HDF_LOGE("%s: device or property null!", __func__);
+ return HDF_ERR_INVALID_OBJECT;
+ }
+
+ pl061->gpioInfo = OsalMemCalloc(sizeof(struct GpioInfo) * GPIO_MAX_INFO_NUM);
+ if (pl061->gpioInfo == NULL) {
+ HDF_LOGE("%s: failed to calloc gpioInfo!", __func__);
+ return HDF_ERR_MALLOC_FAIL;
+ }
+
+ ret = Pl061GpioReadDrs(pl061, device->property); // 利用从gpio_config.HCS文件读取的属性值来初始化自定义结构体对象成员
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: failed to read drs:%d", __func__, ret);
+ return ret;
+ }
+
+ if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 ||
+ pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) {
+ HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl0 61->bitNum);
+ return HDF_ERR_INVALID_PARAM;
+ }
+
+ pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep); // 地址映射
+ if (pl061->regBase == NULL) {
+ HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase);
+ return HDF_ERR_IO;
+ }
+
+ ret = Pl061GpioInitGroups(pl061); // group信息初始化,并添加到HDF核心层
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: err init groups:%d", __func__, ret);
+ OsalIoUnmap((void *)pl061->regBase);
+ pl061->regBase = NULL;
+ return ret;
+ }
+ pl061->priv = (void *)device->property;
+ device->priv = (void *)pl061;
+ Pl061GpioDebug(pl061);
+
+ #ifdef PL061_GPIO_USER_SUPPORT
+ if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) {
+ HDF_LOGE("%s: add vfs fail!", __func__);
+ }
+ #endif
+ HDF_LOGI("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device));
+ return HDF_SUCCESS;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。
+
+ ```c
+ static void Pl061GpioUninitGroups(struct Pl061GpioData *pl061)
+ {
+ uint16_t i;
+ struct Pl061GpioGroup *group = NULL;
+
+ for (i = 0; i < pl061->groupNum; i++) {
+ group = &pl061->groups[i];
+ GpioDumperDestroy(&pl061->groups[i]);
+ GpioCntlrRemove(&group->cntlr); // 从HDF核心层删除
+ }
+
+ OsalMemFree(pl061->groups);
+ pl061->groups = NULL;
+ }
+
+ static void Pl061GpioRelease(struct HdfDeviceObject *device)
+ {
+ struct Pl061GpioData *pl061 = NULL;
+
+ HDF_LOGI("%s: enter", __func__);
+ if (device == NULL) {
+ HDF_LOGE("%s: device is null!", __func__);
+ return;
+ }
+
+ #ifdef PL061_GPIO_USER_SUPPORT
+ GpioRemoveVfs();
+ #endif
+
+ pl061 = (struct Pl061GpioData *)device->priv;
+ if (pl061 == NULL) {
+ HDF_LOGE("%s: device priv is null", __func__);
+ return;
+ }
+
+ Pl061GpioUninitGroups(pl061);
+ OsalMemFree(pl061->gpioInfo);
+ pl061->gpioInfo = NULL;
+ OsalIoUnmap((void *)pl061->regBase);
+ pl061->regBase = NULL;
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如GPIO控制状态,中断响应情况等。
diff --git a/zh-cn/device-dev/driver/driver-platform-hdmi-des.md b/zh-cn/device-dev/driver/driver-platform-hdmi-des.md
index e67370bda2be9dc82836a1f8743acbc5076aaf51..aafcaea77cd202545e38fccad77e384ca1a7d0fc 100755
--- a/zh-cn/device-dev/driver/driver-platform-hdmi-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-hdmi-des.md
@@ -1,18 +1,23 @@
# HDMI
-
## 概述
### 功能简介
HDMI(High Definition Multimedia Interface),即高清多媒体接口,主要用于DVD、机顶盒等音视频Source到TV、显示器等Sink设备的传输。
+
HDMI以主从方式工作,通常有一个Source端和一个Sink端。
+
HDMI接口定义了完成HDMI传输的通用方法集合,包括:
- HDMI控制器管理:打开或关闭HDMI控制器
+
- HDMI启动/停止传输:启动或停止HDMI传输
+
- HDMI控制器设置:设置音频、视频及HDR属性,设置色彩深度、声音图像消隐等
+
- HDMI读取EDID:读取Sink端原始的EDID数据
+
- HDMI热插拔:注册/注销热插拔回调函数
### 基本概念
@@ -20,25 +25,50 @@ HDMI接口定义了完成HDMI传输的通用方法集合,包括:
HDMI是Hitachi、Panasonic、Philips、Silicon Image、Sony、Thomson、Toshiba共同发布的一款音视频传输协议。传输过程遵循TMDS(Transition Minimized Differential Signaling)协议。
- TMDS(Transition Minimized Differential signal):过渡调制差分信号,也被称为最小化传输差分信号,用于发送音频、视频及各种辅助数据。
+
- DDC(Display Data Channel):显示数据通道,发送端与接收端可利用DDC通道得知彼此的发送与接收能力,但HDMI仅需单向获知接收端(显示器)的能力。
+
- CEC(Consumer Electronics Control):消费电子控制,该功能应该能够在连接HDMI的发送设备与接收设备之间实现交互操作。
+
- FRL(Fixed Rate Link):TMDS 的架构进行讯号传输时,最高带宽可达 18Gbps,而FRL模式的带宽则提升到48 Gbps。
+
- HDCP(High-bandwidth Digital Content Protection):即高带宽数字内容保护技术,当用户对高清晰信号进行非法复制时,该技术会进行干扰,降低复制出来的影像的质量,从而对内容进行保护。
+
- EDID(Extended Display Identification Data):扩展显示标识数据,通常存储在显示器的固件中,标识供应商信息、EDID版本信息、最大图像大小、颜色设置、厂商预设置、频率范围的限制以及显示器名和序列号的字符串。
### 运作机制
+在HDF框架中,HDMI模块接口适配模式拟采用独立服务模式,如图1所示。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDF设备管理器的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
+
+独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
+
+- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
+- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
+
+**图 1** HDMI独立服务模式结构图
+
+![HDMI独立服务模式结构图](figures/独立服务模式结构图.png)
+
+HDMI模块各分层作用:
+
+- 接口层提供打开HDMI设备、启动HDMI传输、停止HDMI传输、声音图像消隐设置、设置色彩深度、获取色彩深度、设置视频属性、获取视频属性、设置HDR属性、读取Sink端原始EDID数据、注册HDMI热插拔检测回调函数、注销HDMI热插拔检测回调函数、关闭HDMI设备的接口。
+
+- 核心层主要提供HDMI控制器的打开、关闭及管理的能力,通过钩子函数与适配层交互。
+
+- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
+
HDMI的Source端提供+5V和GND,用于DDC和CEC通信。通过DDC通道,Source端可以读取Sink端的各项参数,如接受能力等;CEC为可选通道,用于同步Source端与Sink端的控制信号,改善用户体验。TMDS通道有四组差分信号,TMDS Clock Channel为TMDS提供时钟信号,其余三组传输音视频数据及各种辅助数据;HDP为热插拔检测端口,当有Sink端接入时,Source端会通过中断服务程序进行响应。
-HDMI物理连接如图1所示:
+HDMI物理连接如图2所示:
-**图 1** HDMI物理连线示意图
+**图 2** HDMI物理连线示意图
-![](figures/HDMI物理连线示意图.png "HDMI物理连线示意图")
+![HDMI物理连线示意图](figures/HDMI物理连线示意图.png)
### 约束与限制
-HDMI模块当前仅支持轻量和小型系统内核(LiteOS)。
+HDMI模块当前仅支持轻量和小型系统内核(LiteOS),暂无实际适配驱动 。
## 使用指导
@@ -48,32 +78,33 @@ HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同
### 接口说明
-**表 1** HDMI驱动API接口功能介绍
+HDMI模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/framework/include/platform/hdmi_if.h。
+**表 1** HDMI驱动API接口功能介绍
-| 接口名 | 描述 |
+| 接口名 | 描述 |
| ----------------------------- | -------------------------- |
-| HdmiOpen | 打开HDMI控制器 |
-| HdmiClose | 关闭HDMI控制器 |
-| HdmiStart | 启动HDMI传输 |
-| HdmiStop | 停止HDMI传输 |
-| HdmiAvmuteSet | 声音图像消隐设置 |
-| HdmiDeepColorSet | 设置色彩深度 |
-| HdmiDeepColorGet | 获取色彩深度 |
-| HdmiSetVideoAttribute | 设置视频属性 |
-| HdmiSetAudioAttribute | 设置音频属性 |
-| HdmiSetHdrAttribute | 设置HDR属性 |
-| HdmiReadSinkEdid | 读取Sink端原始EDID数据 |
-| HdmiRegisterHpdCallbackFunc | 注册HDMI热插拔检测回调函数 |
+| HdmiOpen | 打开HDMI控制器 |
+| HdmiClose | 关闭HDMI控制器 |
+| HdmiStart | 启动HDMI传输 |
+| HdmiStop | 停止HDMI传输 |
+| HdmiAvmuteSet | 声音图像消隐设置 |
+| HdmiDeepColorSet | 设置色彩深度 |
+| HdmiDeepColorGet | 获取色彩深度 |
+| HdmiSetVideoAttribute | 设置视频属性 |
+| HdmiSetAudioAttribute | 设置音频属性 |
+| HdmiSetHdrAttribute | 设置HDR属性 |
+| HdmiReadSinkEdid | 读取Sink端原始EDID数据 |
+| HdmiRegisterHpdCallbackFunc | 注册HDMI热插拔检测回调函数 |
| HdmiUnregisterHpdCallbackFunc | 注销HDMI热插拔检测回调函数 |
### 开发步骤
-使用HDMI设备的一般流程如图2所示。
+使用HDMI设备的一般流程如图3所示。
-**图 2** HDMI设备使用流程图
+**图 3** HDMI设备使用流程图
-![](figures/HDMI使用流程图.png "HDMI使用流程图")
+![HDMI设备使用流程图](figures/HDMI使用流程图.png)
#### 打开HDMI控制器
@@ -83,25 +114,25 @@ HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同
DevHandle HdmiOpen(int16_t number);
```
-**表 2** HdmiOpen参数和返回值描述
+**表 2** HdmiOpen参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------------- |
-| number | HDMI控制器号 |
-| **返回值** | **返回值描述** |
-| NULL | 打开HDMI控制器失败 |
+| number | int16_t类型,HDMI控制器号 |
+| **返回值** | **返回值描述** |
+| NULL | 打开HDMI控制器失败 |
| 控制器句柄 | 打开的HDMI控制器句柄 |
假设系统中存在2个HDMI控制器,编号从0到1,以下代码示例为获取0号控制器:
```c
-DevHandle hdmiHandle = NULL; /* HDMI控制器句柄 /
+DevHandle hdmiHandle = NULL; // HDMI控制器句柄
-/* 打开HDMI控制器 */
+// 打开HDMI控制器
hdmiHandle = HdmiOpen(0);
if (hdmiHandle == NULL) {
- HDF_LOGE("HdmiOpen: failed\n");
- return;
+ HDF_LOGE("HdmiOpen: hdmi open fail!\n");
+ return NULL;
}
```
@@ -111,20 +142,20 @@ if (hdmiHandle == NULL) {
int32_t HdmiRegisterHpdCallbackFunc(DevHandle handle, struct HdmiHpdCallbackInfo *callback);
```
-**表 3** HdmiRegisterHpdCallbackFunc参数和返回值描述
+**表 3** HdmiRegisterHpdCallbackFunc参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | ------------------ |
-| handle | HDMI控制器句柄 |
-| callback | 热插拔回调函数信息 |
-| **返回值** | **返回值描述** |
-| 0 | 注册成功 |
-| 负数 | 注册失败 |
+| handle | DevHandle类型,HDMI控制器句柄 |
+| callback | 结构体指针,热插拔回调函数信息 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 注册成功 |
+| 负数 | 注册失败 |
注册热插拔检测回调函数示例:
```c
-/* 热插拔检测回调函数定义 */
+// 热插拔检测回调函数定义
static void HdmiHpdHandle(void *data, bool hpd)
{
if (data == NULL) {
@@ -133,23 +164,22 @@ static void HdmiHpdHandle(void *data, bool hpd)
}
if (hpd == true) {
HDF_LOGD("HdmiHpdHandle: hot plug");
- /* 调用者添加相关处理 */
+ // 调用者添加相关处理
} else {
HDF_LOGD("HdmiHpdHandle: hot unplug");
- /* 调用者添加相关处理 */
+ // 调用者添加相关处理
}
}
-/* 热插拔检测回调函数注册示例 */
-···
+// 热插拔检测回调函数注册示例
struct HdmiHpdCallbackInfo info = {0};
info.data = handle;
info.callbackFunc = HdmiHpdHandle;
ret = HdmiRegisterHpdCallbackFunc(hdmiHandle, info);
-if (ret != 0) {
- HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register hpd callback func fail, ret:%d", ret);
+ return ret;
}
-···
```
#### 读取EDID
@@ -158,16 +188,16 @@ if (ret != 0) {
int32_t HdmiReadSinkEdid(DevHandle handle, uint8_t *buffer, uint32_t len);
```
-**表 4** HdmiReadSinkEdid参数和返回值描述
+**表 4** HdmiReadSinkEdid参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | ---------------------- |
-| handle | HDMI控制器句柄 |
-| buffer | 数据缓冲区 |
-| len | 数据长度 |
-| **返回值** | **返回值描述** |
-| 正整数 | 成功读取的原始EDID数据 |
-| 负数或0 | 读取失败 |
+| handle | DevHandle类型,HDMI控制器句柄 |
+| buffer | uint8_t类型指针,数据缓冲区 |
+| len | uint32_t类型,数据长度 |
+| **返回值** | **返回值描述** |
+| 正整数 | 成功读取的原始EDID数据 |
+| 负数或0 | 读取失败 |
读取Sink端的原始EDID数据示例:
@@ -177,7 +207,8 @@ uint8_t edid[HDMI_EDID_MAX_LEN] = {0};
len = HdmiReadSinkEdid(hdmiHandle, edid, HDMI_EDID_MAX_LEN);
if (len <= 0) {
- HDF_LOGE("%s: HdmiReadSinkEdid failed len = %d.", __func__, len);
+ HDF_LOGE("HdmiReadSinkEdid: hdmi read sink edid fail, len = %d.", len);
+ return HDF_FAILURE;
}
```
@@ -187,16 +218,15 @@ if (len <= 0) {
int32_t HdmiSetAudioAttribute(DevHandle handle, struct HdmiAudioAttr *attr);
```
-**表 5** HdmiSetAudioAttribute参数和返回值描述
+**表 5** HdmiSetAudioAttribute参数和返回值描述
-
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ------ | -------------- |
-| handle | HDMI控制器句柄 |
-| attr | 音频属性 |
-| 返回值 | 返回值描述 |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| handle | DevHandle类型,HDMI控制器句柄 |
+| attr | 结构体指针,音频属性 |
+| 返回值 | 返回值描述 |
+| HDF_SUCCESS | 设置成功 |
+| 负数 | 设置失败 |
设置音频属性示例:
@@ -210,8 +240,9 @@ audioAttr.bitDepth = HDMI_ADIO_BIT_DEPTH_16;
audioAttr.sampleRate = HDMI_SAMPLE_RATE_8K;
audioAttr.channels = HDMI_AUDIO_FORMAT_CHANNEL_3;
ret = HdmiSetAudioAttribute(handle, &audioAttr);
-if (ret != 0) {
- HDF_LOGE("HdmiSetAudioAttribute failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiSetAudioAttribute: hdmi set audio attribute fail!, ret:%d", ret);
+ return ret;
}
```
@@ -221,16 +252,15 @@ if (ret != 0) {
int32_t HdmiSetVideoAttribute(DevHandle handle, struct HdmiVideoAttr *attr);
```
-**表 6** HdmiSetVideoAttribute参数和返回值描述
-
+**表 6** HdmiSetVideoAttribute参数和返回值描述
-| 参数 | 参数描述 |
+| 参数| 参数描述|
| ---------- | -------------- |
-| handle | HDMI控制器句柄 |
-| attr | 视频属性 |
+| handle | DevHandle类型,HDMI控制器句柄 |
+| attr | 结构体指针,视频属性 |
| **返回值** | **返回值描述** |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| HDF_SUCCESS | 设置成功 |
+| 负数 | 设置失败 |
设置视频属性示例:
@@ -243,8 +273,9 @@ videoAttr.colorimetry = HDMI_COLORIMETRY_EXTENDED;
videoAttr.extColorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM;
videoAttr.quantization = HDMI_QUANTIZATION_RANGE_FULL;
ret = HdmiSetVideoAttribute(handle, &videoAttr);
-if (ret != 0) {
- HDF_LOGE("HdmiSetVideoAttribute failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiSetVideoAttribute: hdmi set video attribute fail, ret:%d.", ret);
+ return ret;
}
```
@@ -254,16 +285,15 @@ if (ret != 0) {
int32_t HdmiSetHdrAttribute(DevHandle handle, struct HdmiHdrAttr *attr);
```
-**表 7** HdmiSetHdrAttribute参数和返回值描述
+**表 7** HdmiSetHdrAttribute参数和返回值描述
-
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | HDMI控制器句柄 |
-| attr | HDR属性 |
+| handle | DevHandle类型,HDMI控制器句柄 |
+| attr | 结构体指针,HDR属性 |
| **返回值** | **返回值描述** |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| HDF_SUCCESS | 设置成功 |
+| 负数 | 设置失败 |
设置HDR属性示例:
@@ -277,8 +307,9 @@ hdrAttr.eotfType = HDMI_EOTF_SMPTE_ST_2048;
hdrAttr.metadataType = HDMI_DRM_STATIC_METADATA_TYPE_1;
hdrAttr.colorimetry = HDMI_HDR_EXTENDED_COLORIMETRY_XV_YCC_709;
ret = HdmiSetHdrAttribute(handle, &hdrAttr);
-if (ret != 0) {
- HDF_LOGE("HdmiSetHdrAttribute failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiSetHdrAttribute: hdmi set hdr attribute fail, ret:%d", ret);
+ return ret;
}
```
@@ -288,16 +319,15 @@ if (ret != 0) {
int32_t HdmiAvmuteSet(DevHandle handle, bool enable);
```
-**表 8** HdmiAvmuteSet参数和返回值描述
-
+**表 8** HdmiAvmuteSet参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | ----------------- |
-| handle | HDMI控制器句柄 |
-| enable | 使能/去使能avmute |
-| **返回值** | **返回值描述** |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| handle | DevHandle类型,HDMI控制器句柄 |
+| enable | 布尔值,使能/去使能avmute |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 设置成功 |
+| 负数 | 设置失败 |
设置声音图像消隐示例:
@@ -305,8 +335,9 @@ int32_t HdmiAvmuteSet(DevHandle handle, bool enable);
int32_t ret;
ret = HdmiAvmuteSet(hdmiHandle, true);
-if (ret != 0) {
- HDF_LOGE("HdmiAvmuteSet failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiAvmuteSet: hdmi avmute set fail, ret:%d", ret);
+ return ret;
}
```
@@ -316,16 +347,15 @@ if (ret != 0) {
int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color);
```
-**表 9** HdmiDeepColorSet参数和返回值描述
+**表 9** HdmiDeepColorSet参数和返回值描述
-
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | HDMI控制器句柄 |
-| color | 色彩深度 |
+| handle | DevHandle类型,HDMI控制器句柄 |
+| color | 枚举类型,色彩深度 |
| **返回值** | **返回值描述** |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| HDF_SUCCESS | 设置成功 |
+| 负数 | 设置失败 |
设置色彩深度示例:
@@ -333,8 +363,9 @@ int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color);
int32_t ret;
ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS);
-if (ret != 0) {
- HDF_LOGE("HdmiDeepColorSet failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiDeepColorSet: hdmi deep color set fail, ret:%d.", ret);
+ return ret;
}
```
@@ -344,16 +375,15 @@ if (ret != 0) {
int32_t HdmiDeepColorGet(DevHandle handle, enum HdmiDeepColor *color);
```
-**表 10** HdmiDeepColorGet参数和返回值描述
-
+**表 10** HdmiDeepColorGet参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | HDMI控制器句柄 |
-| color | 色彩深度 |
+| handle | DevHandle类型,HDMI控制器句柄 |
+| color | 枚举类型指针,色彩深度 |
| **返回值** | **返回值描述** |
-| 0 | 获取成功 |
-| 负数 | 获取失败 |
+| HDF_SUCCESS | 获取成功 |
+| 负数 | 获取失败 |
获取色彩深度示例:
@@ -362,8 +392,9 @@ enum HdmiDeepColor color;
int32_t ret;
ret = HdmiDeepColorGet(handle, &color);
-if (ret != 0) {
- HDF_LOGE("HdmiDeepColorGet failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiDeepColorGet: hdmi deep color get fail, ret:%d", ret);
+ return ret;
}
```
@@ -373,15 +404,14 @@ if (ret != 0) {
int32_t HdmiStart(DevHandle handle);
```
-**表 11** HdmiStart参数和返回值描述
+**表 11** HdmiStart参数和返回值描述
-
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | HDMI控制器句柄 |
+| handle | DevHandle类型,HDMI控制器句柄 |
| **返回值** | **返回值描述** |
-| 0 | 启动成功 |
-| 负数 | 启动失败 |
+| HDF_SUCCESS | 启动成功 |
+| 负数 | 启动失败 |
启动HDMI传输示例:
@@ -389,8 +419,9 @@ int32_t HdmiStart(DevHandle handle);
int32_t ret;
ret = HdmiStart(hdmiHandle);
-if (ret != 0) {
- HDF_LOGE("start transmission failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiStart: start transmission fail, ret:%d", ret);
+ return ret;
}
```
@@ -400,15 +431,14 @@ if (ret != 0) {
int32_t HdmiStop(DevHandle handle);
```
-**表 12** HdmiStop参数和返回值描述
-
+**表 12** HdmiStop参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | HDMI控制器句柄 |
+| handle | DevHandle类型,HDMI控制器句柄 |
| **返回值** | **返回值描述** |
-| 0 | 停止成功 |
-| 负数 | 停止失败 |
+| HDF_SUCCESS | 停止成功 |
+| 负数 | 停止失败 |
停止HDMI传输示例:
@@ -416,8 +446,9 @@ int32_t HdmiStop(DevHandle handle);
int32_t ret;
ret = HdmiStop(hdmiHandle);
-if (ret != 0) {
- HDF_LOGE("stop transmission failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiStop: stop transmission fail, ret:%d.", ret);
+ return ret;
}
```
@@ -427,15 +458,14 @@ if (ret != 0) {
int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle);
```
-**表 13** HdmiUnregisterHpdCallbackFunc参数和返回值描述
-
+**表 13** HdmiUnregisterHpdCallbackFunc参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | HDMI控制器句柄 |
+| handle | DevHandle类型,HDMI控制器句柄 |
| **返回值** | **返回值描述** |
-| 0 | 注销成功 |
-| 负数 | 注销失败 |
+| HDF_SUCCESS | 注销成功 |
+| 负数 | 注销失败 |
注销热插拔检测回调函数示例:
@@ -443,8 +473,9 @@ int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle);
int32_t ret;
ret = HdmiUnregisterHpdCallbackFunc(hdmiHandle);
-if (ret != 0) {
- HDF_LOGE("unregister failed.");
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("HdmiUnregisterHpdCallbackFunc:unregister fail, ret:%d.", ret);
+ return ret;
}
```
@@ -456,10 +487,9 @@ void HdmiClose(DevHandle handle);
**表 14** HdmiClose参数和返回值描述
-
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | HDMI控制器句柄 |
+| handle | DevHandle类型,HDMI控制器句柄 |
关闭HDMI控制器示例:
@@ -473,9 +503,9 @@ HdmiClose(hdmiHandle);
本例拟在Hi3516DV300开发板上对虚拟驱动进行简单的传输操作:
-- SOC:hi3516dv300。
+- SOC:hi3516dv300。
-- HDMI控制器:使用0号HDMI控制器。
+- HDMI控制器:使用0号HDMI控制器。
示例如下:
@@ -615,4 +645,4 @@ static int32_t TestCaseHdmi(void)
return 0;
}
-```
\ No newline at end of file
+```
diff --git a/zh-cn/device-dev/driver/driver-platform-hdmi-develop.md b/zh-cn/device-dev/driver/driver-platform-hdmi-develop.md
index 2568e2e3cd54ff1aff5e8874141b75b258057d12..7486bf20f30e642cb061bd71b65a4d07c7a22039 100755
--- a/zh-cn/device-dev/driver/driver-platform-hdmi-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-hdmi-develop.md
@@ -1,5 +1,4 @@
-# HDMI
-
+# HDMI
## 概述
@@ -19,18 +18,25 @@ HDMI(High Definition Multimedia Interface),即高清多媒体接口,是H
- HDCP(High-bandwidth Digital Content Protection):即高带宽数字内容保护技术,当用户对高清晰信号进行非法复制时,该技术会进行干扰,降低复制出来的影像的质量,从而对内容进行保护。
-
### 运作机制
-在HDF框架中,HDMI的接口适配模式采用独立服务模式(如图1)。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用率。
+在HDF框架中,HDMI的接口适配模式拟采用独立服务模式(如图1)。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用率。
+
+HDMI模块各分层作用:
+
+- 接口层提供打开HDMI设备、启动HDMI传输、停止HDMI传输、声音图像消隐设置、设置色彩深度、获取色彩深度、设置视频属性、获取视频属性、设置HDR属性、读取Sink端原始EDID数据、注册HDMI热插拔检测回调函数、注销HDMI热插拔检测回调函数、关闭HDMI设备的接口。
+
+- 核心层主要提供HDMI控制器的打开、关闭及管理的能力,通过钩子函数与适配层交互。
- **图 1** HDMI独立服务模式
+- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
-![image1](figures/独立服务模式结构图.png)
+**图 1** HDMI独立服务模式
+
+![HDMI独立服务模式](figures/独立服务模式结构图.png)
### 约束与限制
-HDMI模块当前仅支持轻量和小型系统内核(LiteOS) 。
+HDMI模块当前仅支持轻量和小型系统内核(LiteOS),暂无实际适配驱动。
## 开发指导
@@ -41,6 +47,7 @@ HDMI具有体积小、传输速率高、传输带宽宽、兼容性好、能同
### 接口说明
HdmiCntlrOps定义:
+
```c
struct HdmiCntlrOps {
void (*hardWareInit)(struct HdmiCntlr *cntlr);
@@ -80,17 +87,17 @@ struct HdmiCntlrOps {
};
```
-**表1** HdmiCntlrOps结构体成员的回调函数功能说明
+**表 1** HdmiCntlrOps结构体成员的回调函数功能说明
-| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
+| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| ------------------------ | ------------------------------------------------------------ | -------------------------------------- | ------------------ | -------------------------------------------------- |
-| hardWareInit | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | 无 | 初始化HDMI硬件 |
-| hardWareStatusGet | **cntlr**:结构体指针,核心层HDMI控制器
| **status**:HDMI硬件状态 ; | 无 | 获取HDMI当前硬件状态 |
-| controllerReset | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | 无 | 复位HDMI控制器 |
-| hotPlugStateGet | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | bool:HDMI热插拔状态 | 获取HDMI热插拔状态 |
+| hardWareInit | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | 无 | 初始化HDMI硬件 |
+| hardWareStatusGet | **cntlr**:结构体指针,核心层HDMI控制器
| **status**:HDMI硬件状态 ; | 无 | 获取HDMI当前硬件状态 |
+| controllerReset | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | 无 | 复位HDMI控制器 |
+| hotPlugStateGet | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | bool:HDMI热插拔状态 | 获取HDMI热插拔状态 |
| hotPlugInterruptStateGet | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | bool:HDMI热插拔中断状态 | 获取HDMI热插拔中断状态 |
-| lowPowerSet | **cntlr**:结构体指针,核心层HDMI控制器
**enable**:bool,使能/去使能 | 无 | 无 | 使能/去使能低功耗 |
-| tmdsModeSet | **cntlr**:结构体指针,核心层HDMI控制器
**mode**:TMDS模式 | 无 | 无 | 设置TMDS模式 |
+| lowPowerSet | **cntlr**:结构体指针,核心层HDMI控制器
**enable**:bool,使能/去使能 | 无 | 无 | 使能/去使能低功耗 |
+| tmdsModeSet | **cntlr**:结构体指针,核心层HDMI控制器
**mode**:TMDS模式 | 无 | 无 | 设置TMDS模式 |
| tmdsConfigSet | **cntlr**:结构体指针,核心层HDMI控制器
**mode**:TMDS参数 | 无 | HDF_STATUS相关状态 | 配置TMDS参数 |
| infoFrameEnable | **cntlr**:结构体指针,核心层HDMI控制器
**infoFrameType**:packet类型
**enable**:bool,使能/去使能 | 无 | 无 | 使能/去使能infoFrame |
| infoFrameSend | **cntlr**:结构体指针,核心层HDMI控制器
**infoFrameType**:packet类型
**data**:infoFrame数据
**len**:数据长度 | 无 | HDF_STATUS相关状态 | 发送infoFrame |
@@ -120,20 +127,30 @@ struct HdmiCntlrOps {
### 开发步骤
-HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以及实例化HDMI控制器对象。
+HDMI模块适配包含以下四个步骤:
+
+- 实例化驱动入口
-- 实例化驱动入口:
- 实例化HdfDriverEntry结构体成员。
+
- 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
-- 配置属性文件:
+- 配置属性文件
+
- 在device_info.hcs文件中添加deviceNode描述。
+
- 【可选】添加hdmi_config.hcs器件属性文件。
-- 实例化HDMI控制器对象:
+- 实例化HDMI控制器对象
+
- 初始化HdmiCntlr成员。
+
- 实例化HdmiCntlr成员HdmiCntlrOps方法集合。
+- 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,HDMI传输等。
+
1. 实例化驱动入口
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
@@ -148,16 +165,14 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
.Bind = HdmiAdapterBind,
.Init = HdmiAdapterInit,
.Release = HdmiAdapterRelease,
- .moduleName = "adapter_hdmi_driver",//【必要】与HCS里面的名字匹配
+ .moduleName = "adapter_hdmi_driver", // 【必要且与HCS文件中里面的moduleName匹配】
};
- HDF_INIT(g_hdmiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ HDF_INIT(g_hdmiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
```
2. 配置属性文件
- 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在hdmi_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层HdmiCntlr相关成员的默认值或限制范围有密切关系。
-
- 从第一个节点开始配置具体HDMI控制器信息,此节点并不表示某一路HDMI控制器,而是代表一个资源性质设备,用于描述一类HDMI控制器的信息。本例只有一个HDMI控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在hdmi_config文件中增加对应的器件属性。
+ 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在hdmi_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层HdmiCntlr相关成员的默认值或限制范围有密切关系。从第一个节点开始配置具体HDMI控制器信息,此节点并不表示某一路HDMI控制器,而是代表一个资源性质设备,用于描述一类HDMI控制器的信息。本例只有一个HDMI控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在hdmi_config文件中增加对应的器件属性。
- device_info.hcs配置参考
@@ -181,8 +196,8 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
- hdmi_config.hcs 配置参考
```c
- root {
- platform {
+ root {
+ platform {
hdmi_config {
template hdmi_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。
match_attr = ""; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致。
@@ -227,7 +242,7 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
}
```
-3. 实例化控制器对象
+3. 实例化HDMI控制器对象
最后一步,完成驱动入口注册之后,要以核心层HdmiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化HdmiCntlr成员HdmiCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
@@ -245,7 +260,6 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
uint32_t irqNum; //【必要】中断号
};
- /* HdmiCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。 */
struct HdmiCntlr {
struct IDeviceIoService service;
struct HdfDeviceObject *hdfDevObj;
@@ -320,16 +334,17 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
**返回值:**
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS 定义)
+ HDF_STATUS相关状态 (表2为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。
+
+ **表 2** HDF_STATUS相关状态说明
- |状态(值)|状态描述|
- |:-|:-|
- |HDF_ERR_INVALID_OBJECT|控制器对象非法|
- |HDF_ERR_INVALID_PARAM |参数非法|
- |HDF_ERR_MALLOC_FAIL |内存分配失败|
- |HDF_ERR_IO |I/O错误|
- |HDF_SUCCESS |传输成功|
- |HDF_FAILURE |传输失败|
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
**函数说明:**
@@ -358,13 +373,13 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
cntlr->hdfDevObj = obj; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提
obj->service = &cntlr->service; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提
ret = HdmiAdapterCntlrParse(cntlr, obj); //【必要】初始化cntlr,失败则goto __ERR。
- ...
+ ......
ret = HdmiAdapterHostParse(host, obj); //【必要】初始化host对象的相关属性,失败则goto __ERR。
- ...
+ ......
ret = HdmiAdapterHostInit(host, cntlr); // 厂商自定义的初始化,失败则goto __ERR。
- ...
+ ......
ret = HdmiCntlrAdd(cntlr); // 调用核心层函数,失败则goto __ERR。
- ...
+ ......
HDF_LOGD("HdmiAdapterBind: success.");
return HDF_SUCCESS;
__ERR:
@@ -416,11 +431,13 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
static void HdmiAdapterRelease(struct HdfDeviceObject *obj)
{
struct HdmiCntlr *cntlr = NULL;
- ...
+ ......
cntlr = (struct HdmiCntlr *)obj->service; // 这里有HdfDeviceObject到HdmiCntlr的强制转化,通过service成员,赋值见Bind函数。
- ...
+ ......
HimciDeleteHost((struct HimciAdapterHost *)cntlr->priv);// 厂商自定义的内存释放函数,这里有HdmiCntlr到HimciAdapterHost的强制转化。
}
```
-
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,HDMI传输等。
diff --git a/zh-cn/device-dev/driver/driver-platform-i2c-des.md b/zh-cn/device-dev/driver/driver-platform-i2c-des.md
index c4324343a044891d354a1f2cf1938034be16ebc1..3a2e93a8fd5ecffb91195668028d08bc041f179f 100644
--- a/zh-cn/device-dev/driver/driver-platform-i2c-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-i2c-des.md
@@ -1,6 +1,5 @@
# I2C
-
## 概述
### 功能简介
@@ -16,12 +15,14 @@ I2C数据的传输必须以一个起始信号作为开始条件,以一个结
I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址,当主设备需要和某一个从设备通信时,通过广播的方式,将从设备地址写到总线上,如果某个从设备符合此地址,将会发出应答信号,建立传输。
I2C接口定义了完成I2C传输的通用方法集合,包括:
+
- I2C控制器管理:打开或关闭I2C控制器
+
- I2C消息传输:通过消息传输结构体数组进行自定义传输
- **图1** I2C物理连线示意图
+**图 1** I2C物理连线示意图
- ![image](figures/I2C物理连线示意图.png "I2C物理连线示意图")
+![I2C物理连线示意图](figures/I2C物理连线示意图.png)
## 使用指导
@@ -33,9 +34,9 @@ I2C通常用于与各类支持I2C协议的传感器、执行器或输入输出
I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i2c_if.h。
-**表1** I2C驱动API接口功能介绍
+**表 1** I2C驱动API接口功能介绍
-| 接口名 | 接口描述 |
+| 接口名 | 接口描述 |
| -------- | -------- |
| DevHandle I2cOpen(int16_t number) | 打开I2C控制器 |
| void I2cClose(DevHandle handle) | 关闭I2C控制器 |
@@ -45,9 +46,9 @@ I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
使用I2C设备的一般流程如下图所示。
-**图2** I2C设备使用流程图
+**图 2** I2C设备使用流程图
-![image](figures/I2C设备使用流程图.png "I2C设备使用流程图")
+![I2C设备使用流程图](figures/I2C设备使用流程图.png)
#### 打开I2C控制器
@@ -58,11 +59,11 @@ I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
DevHandle I2cOpen(int16_t number);
```
- **表2** I2cOpen参数和返回值描述
+**表 2** I2cOpen参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| number | I2C控制器号 |
+| number | int16_t类型,I2C控制器号 |
| **返回值** | **返回值描述** |
| NULL | 打开I2C控制器失败 |
| 设备句柄 | 打开的I2C控制器设备句柄 |
@@ -70,57 +71,55 @@ DevHandle I2cOpen(int16_t number);
假设系统中存在8个I2C控制器,编号从0到7,以下代码示例为获取3号控制器:
```c
-DevHandle i2cHandle = NULL; /* I2C控制器句柄 /
+DevHandle i2cHandle = NULL; // I2C控制器句柄
-/* 打开I2C控制器 */
+// 打开I2C控制器
i2cHandle = I2cOpen(3);
if (i2cHandle == NULL) {
- HDF_LOGE("I2cOpen: failed\n");
- return;
+ HDF_LOGE("I2cOpen: i2c open fail.\n");
+ return NULL;
}
```
-
#### 进行I2C通信
消息传输
```c
-int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count);
+int32_t I2cTransfer(DevHandle handle, struct I2cMsg *msgs, int16_t count);
```
- **表3** I2cTransfer参数和返回值描述
+**表 3** I2cTransfer参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | I2C控制器设备句柄 |
-| msgs | 待传输数据的消息结构体数组 |
-| count | 消息数组长度 |
+| handle | DevHandle类型,I2C控制器设备句柄 |
+| msgs | 结构体指针,待传输数据的消息结构体数组 |
+| count | int16_t类型,消息数组长度 |
| **返回值** | **返回值描述** |
| 正整数 | 成功传输的消息结构体数目 |
| 负数 | 执行失败 |
I2C传输消息类型为I2cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。组合读写示例:
-
```c
int32_t ret;
uint8_t wbuff[2] = { 0x12, 0x13 };
uint8_t rbuff[2] = { 0 };
-struct I2cMsg msgs[2]; /* 自定义传输的消息结构体数组 */
-msgs[0].buf = wbuff; /* 写入的数据 */
-msgs[0].len = 2; /* 写入数据长度为2 */
-msgs[0].addr = 0x5A; /* 写入设备地址为0x5A */
-msgs[0].flags = 0; /* 传输标记为0,默认为写 */
-msgs[1].buf = rbuff; /* 要读取的数据 */
-msgs[1].len = 2; /* 读取数据长度为2 */
-msgs[1].addr = 0x5A; /* 读取设备地址为0x5A */
-msgs[1].flags = I2C_FLAG_READ /* I2C_FLAG_READ置位 */
-/* 进行一次自定义传输,传输的消息个数为2 */
+struct I2cMsg msgs[2]; // 自定义传输的消息结构体数组
+msgs[0].buf = wbuff; // 写入的数据
+msgs[0].len = 2; // 写入数据长度为2
+msgs[0].addr = 0x5A; // 写入设备地址为0x5A
+msgs[0].flags = 0; // 传输标记为0,默认为写
+msgs[1].buf = rbuff; // 要读取的数据
+msgs[1].len = 2; // 读取数据长度为2
+msgs[1].addr = 0x5A; // 读取设备地址为0x5A
+msgs[1].flags = I2C_FLAG_READ // I2C_FLAG_READ置位
+// 进行一次自定义传输,传输的消息个数为2
ret = I2cTransfer(i2cHandle, msgs, 2);
if (ret != 2) {
- HDF_LOGE("I2cTransfer: failed, ret %d\n", ret);
- return;
+ HDF_LOGE("I2cTransfer: i2c transfer fail, ret:%d\n", ret);
+ return HDF_FAILURE;
}
```
@@ -142,19 +141,18 @@ I2C通信完成之后,需要关闭I2C控制器,关闭函数如下所述:
void I2cClose(DevHandle handle);
```
- **表4** I2cClose参数和返回值描述
+**表 4** I2cClose参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | I2C控制器设备句柄 |
+| handle | DevHandle类型,I2C控制器设备句柄 |
关闭I2C控制器示例:
```c
-I2cClose(i2cHandle); /* 关闭I2C控制器 */
+I2cClose(i2cHandle); // 关闭I2C控制器
```
-
### 使用示例
本例程以操作开发板上的I2C设备为例,详细展示I2C接口的完整使用流程。
diff --git a/zh-cn/device-dev/driver/driver-platform-i2c-develop.md b/zh-cn/device-dev/driver/driver-platform-i2c-develop.md
index cf8fdfa72c847a500739343cf7fb49bc4aefbc76..ef65858fef86b272e756ff7e45905ab87be5f3b9 100755
--- a/zh-cn/device-dev/driver/driver-platform-i2c-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-i2c-develop.md
@@ -15,11 +15,14 @@ I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单
I2C模块各分层的作用为:
- 接口层:提供打开设备,数据传输以及关闭设备的能力。
+
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。
+
- 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。
-**图1** I2C统一服务模式结构图
-![image](figures/统一服务模式结构图.png "I2C统一服务模式结构图")
+**图 1** I2C统一服务模式结构图
+
+![I2C统一服务模式结构图](figures/统一服务模式结构图.png)
## 使用指导
@@ -71,13 +74,13 @@ static const struct I2cLockMethod g_i2cLockOpsDefault = {
若实际情况不允许使用mutex(例如使用者可能在中断上下文调用I2C接口,mutex可能导致休眠,而中断上下文不允许休眠)时,驱动适配者可以考虑使用其他类型的锁来实现一个自定义的I2cLockMethod。一旦实现了自定义的I2cLockMethod,默认的I2cLockMethod将被覆盖。
- **表1** I2cMethod结构体成员函数功能说明
+**表 1** I2cMethod结构体成员函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
-| transfer | cntlr:结构体指针,核心层I2C控制器。
msgs:结构体指针,用户消息。
count:uint16_t,消息数量。 | 无 | HDF_STATUS相关状态 | 传递用户消息 |
+| transfer | cntlr:结构体指针,核心层I2C控制器。
msgs:结构体指针,用户消息。
count:uint16_t类型,消息数量。 | 无 | HDF_STATUS相关状态 | 传递用户消息 |
- **表2** I2cLockMethod结构体成员函数功能说明
+**表 2** I2cLockMethod结构体成员函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
@@ -86,296 +89,306 @@ static const struct I2cLockMethod g_i2cLockOpsDefault = {
### 开发步骤
-I2C模块适配的三个必选环节是实例化驱动入口,配置属性文件,以及实例化核心层接口函数。
+I2C模块适配包含以下四个步骤:
1. 实例化驱动入口
- - 实例化HdfDriverEntry结构体成员。
- - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
+ - 实例化HdfDriverEntry结构体成员。
+
+ - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2. 配置属性文件
- - 在device_info.hcs文件中添加deviceNode描述。
- - 【可选】添加i2c_config.hcs器件属性文件。
+ - 在device_info.hcs文件中添加deviceNode描述。
+
+ - 【可选】添加i2c_config.hcs器件属性文件。
3. 实例化I2C控制器对象
- - 初始化I2cCntlr成员。
- - 实例化I2cCntlr成员I2cMethod和I2cLockMethod。
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化I2cCntlr成员I2cMethod和I2cLockMethod,详见[接口说明](#接口说明)。
+ - 初始化I2cCntlr成员。
+
+ - 实例化I2cCntlr成员I2cMethod和I2cLockMethod。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化I2cCntlr成员I2cMethod和I2cLockMethod,详见[接口说明](#接口说明)。
4. 驱动调试
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,消息传输的成功与否等。
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,消息传输的成功与否等。
### 开发实例
下方将以Hi3516DV300的驱动//device/soc/hisilicon/common/platform/i2c/i2c_hi35xx.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 驱动开发首先需要实例化驱动入口。
+1. 实例化驱动入口
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
- I2C驱动入口开发参考:
+ I2C驱动入口开发参考:
- I2C控制器会出现很多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个设备时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备。
+ I2C控制器会出现很多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个设备时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备。
- I2C管理器服务的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的I2cCntlrAdd函数,它会实现相应功能。
+ I2C管理器服务的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的I2cCntlrAdd函数,它会实现相应功能。
```c
struct HdfDriverEntry g_i2cDriverEntry = {
- .moduleVersion = 1,
- .Init = Hi35xxI2cInit,
- .Release = Hi35xxI2cRelease,
- .moduleName = "hi35xx_i2c_driver", // 【必要且与config.hcs文件里面匹配】
+ .moduleVersion = 1,
+ .Init = Hi35xxI2cInit,
+ .Release = Hi35xxI2cRelease,
+ .moduleName = "hi35xx_i2c_driver", // 【必要且与config.hcs文件里面匹配】
};
- HDF_INIT(g_i2cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
-
- /* 核心层i2c_core.c管理器服务的驱动入口 */
+ HDF_INIT(g_i2cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+
+ // 核心层i2c_core.c管理器服务的驱动入口
struct HdfDriverEntry g_i2cManagerEntry = {
.moduleVersion = 1,
.Bind = I2cManagerBind,
.Init = I2cManagerInit,
.Release = I2cManagerRelease,
- .moduleName = "HDF_PLATFORM_I2C_MANAGER", // 这与device_info.hcs文件中device0对应
+ .moduleName = "HDF_PLATFORM_I2C_MANAGER", // 这与device_info.hcs文件中device0对应
};
HDF_INIT(g_i2cManagerEntry);
```
-2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i2c_config.hcs中配置器件属性。
+2. 配置属性文件
+
+ 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i2c_config.hcs中配置器件属性。
- deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I2cCntlr相关成员的默认值或限制范围有密切关系。
+ deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I2cCntlr相关成员的默认值或限制范围有密切关系。
- 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I2C管理器,其各项参数必须如表2设置:
+ 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I2C管理器,其各项参数必须如表3设置:
- **表3** 统一服务模式的特点
+ **表 3** device_info.hcs节点参数说明
- | 成员名 | 值 |
+ | 成员名 | 值 |
| -------- | -------- |
- | moduleName | 固定为HDF_PLATFORM_I2C_MANAGER |
- | serviceName | 固定为HDF_PLATFORM_I2C_MANAGER |
- | policy | 具体配置为1或2取决于是否对用户态可见 |
- | deviceMatchAttr | 没有使用,可忽略 |
+ | policy | 驱动服务发布的策略,I2C管理器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。I2C管理器具体配置为50 |
+ | permission | 驱动创建设备节点权限,I2C管理器具体配置为0664 |
+ | moduleName | 驱动名称,I2C管理器固定为HDF_PLATFORM_I2C_MANAGER |
+ | serviceName | 驱动对外发布服务的名称,I2C管理器服务名设置为HDF_PLATFORM_I2C_MANAGER |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,I2C管理器设置为hdf_platform_i2c_manager |
从第二个节点开始配置具体I2C控制器信息,此节点并不表示某一路I2C控制器,而是代表一个资源性质设备,用于描述一类I2C控制器的信息。多个控制器之间相互区分的参数是busId和reg_pbase,这在i2c_config.hcs文件中有所体现。
- - device_info.hcs配置参考
-
- ```c
- root {
- device_info {
- match_attr = "hdf_manager";
- device_i2c :: device {
- device0 :: deviceNode {
- policy = 2;
- priority = 50;
- permission = 0644;
- moduleName = "HDF_PLATFORM_I2C_MANAGER";
- serviceName = "HDF_PLATFORM_I2C_MANAGER";
- deviceMatchAttr = "hdf_platform_i2c_manager";
- }
- device1 :: deviceNode {
- policy = 0; // 等于0,不需要发布服务。
- priority = 55; // 驱动启动优先级。
- permission = 0644; // 驱动创建设备节点权限。
- moduleName = "hi35xx_i2c_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
- serviceName = "HI35XX_I2C_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。
- deviceMatchAttr = "hisilicon_hi35xx_i2c"; //【必要】用于配置控制器私有数据,要与i2c_config.hcs中对应控制器保持一致,
- // 具体的控制器信息在 i2c_config.hcs中。
- }
- }
- }
- }
- ```
-
- - i2c_config.hcs配置参考
-
- ```c
- root {
- platform {
- i2c_config {
- match_attr = "hisilicon_hi35xx_i2c"; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致
- template i2c_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。
- bus = 0; //【必要】i2c识别号
- reg_pbase = 0x120b0000; //【必要】物理基地址
- reg_size = 0xd1; //【必要】寄存器位宽
- irq = 0; //【可选】中断号,由控制器的中断特性决定是否需要
- freq = 400000; //【可选】频率,初始化硬件控制器的可选参数
- clk = 50000000; //【可选】控制器时钟,由控制器时钟的初始化流程决定是否需要
- }
- controller_0x120b0000 :: i2c_controller {
- bus = 0;
- }
- controller_0x120b1000 :: i2c_controller {
- bus = 1;
- reg_pbase = 0x120b1000;
- }
- ...
- }
- }
- }
- ```
-
- 需要注意的是,新增i2c_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
-
- 例如:本例中i2c_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs" // 配置文件相对路径
- ```
-
-3. 完成驱动入口注册之后,下一步就是以核心层I2cCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化I2cCntlr成员I2cMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
-
- - 自定义结构体参考
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,而且i2c_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I2cCntlr对象,例如设备号、总线号等。
-
- ```c
- /* 驱动适配者自定义结构体 */
- struct Hi35xxI2cCntlr {
- struct I2cCntlr cntlr; // 【必要】是核心层控制对象,具体描述见下面。
- OsalSpinlock spin; // 【必要】驱动适配者需要基于此锁变量对各个i2c操作函数实现对应的加锁解锁。
- volatile unsigned char *regBase; // 【必要】寄存器基地址
- uint16_t regSize; // 【必要】寄存器位宽
- int16_t bus; // 【必要】i2c_config.hcs文件中可读取具体值
- uint32_t clk; // 【可选】驱动适配者自定义
- uint32_t freq; // 【可选】驱动适配者自定义
- uint32_t irq; // 【可选】驱动适配者自定义
- uint32_t regBasePhy; // 【必要】寄存器物理基地址
- };
-
- /* I2cCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。*/
- struct I2cCntlr {
- struct OsalMutex lock;
- void *owner;
- int16_t busId;
- void *priv;
- const struct I2cMethod *ops;
- const struct I2cLockMethod *lockOps;
- };
- ```
-
- - I2cCntlr成员钩子函数结构体I2cMethod的实例化,和锁机制钩子函数结构体I2cLockMethod实例化,其他成员在Init函数中初始化。
-
- ```c
- /* i2c_hi35xx.c中的示例 */
- static const struct I2cMethod g_method = {
- .transfer = Hi35xxI2cTransfer,
- };
-
- static const struct I2cLockMethod g_lockOps = {
- .lock = Hi35xxI2cLock, // 加锁函数
- .unlock = Hi35xxI2cUnlock, // 解锁函数
- };
- ```
-
- - Init函数开发参考
+ - device_info.hcs配置参考
+
+ ```c
+ root {
+ device_info {
+ match_attr = "hdf_manager";
+ device_i2c :: device {
+ device0 :: deviceNode {
+ policy = 2;
+ priority = 50;
+ permission = 0644;
+ moduleName = "HDF_PLATFORM_I2C_MANAGER";
+ serviceName = "HDF_PLATFORM_I2C_MANAGER";
+ deviceMatchAttr = "hdf_platform_i2c_manager";
+ }
+ device1 :: deviceNode {
+ policy = 0; // 等于0,不需要发布服务。
+ priority = 55; // 驱动启动优先级。
+ permission = 0644; // 驱动创建设备节点权限。
+ moduleName = "hi35xx_i2c_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
+ serviceName = "HI35XX_I2C_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。
+ deviceMatchAttr = "hisilicon_hi35xx_i2c"; //【必要】用于配置控制器私有数据,要与i2c_config.hcs中对应控制器保持一致,
+ // 具体的控制器信息在 i2c_config.hcs中。
+ }
+ }
+ }
+ }
+ ```
+
+ - i2c_config.hcs配置参考
+
+ ```c
+ root {
+ platform {
+ i2c_config {
+ match_attr = "hisilicon_hi35xx_i2c"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
+ template i2c_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。
+ bus = 0; // 【必要】i2c识别号
+ reg_pbase = 0x120b0000; // 【必要】物理基地址
+ reg_size = 0xd1; // 【必要】寄存器位宽
+ irq = 0; // 【可选】中断号,由控制器的中断特性决定是否需要
+ freq = 400000; // 【可选】频率,初始化硬件控制器的可选参数
+ clk = 50000000; // 【可选】控制器时钟,由控制器时钟的初始化流程决定是否需要
+ }
+ controller_0x120b0000 :: i2c_controller {
+ bus = 0;
+ }
+ controller_0x120b1000 :: i2c_controller {
+ bus = 1;
+ reg_pbase = 0x120b1000;
+ }
+ ...
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增i2c_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
+
+ 例如:本例中i2c_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs" // 配置文件相对路径
+ ```
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+3. 实例化I2C控制器对象
- **表4** Init函数入参及返回值参考
+ 完成驱动入口注册之后,下一步就是以核心层I2cCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化I2cCntlr成员I2cMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
+
+ - 自定义结构体参考
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,而且i2c_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I2cCntlr对象,例如设备号、总线号等。
+
+ ```c
+ // 驱动适配者自定义结构体
+ struct Hi35xxI2cCntlr {
+ struct I2cCntlr cntlr; // 【必要】是核心层控制对象,具体描述见下面。
+ OsalSpinlock spin; // 【必要】驱动适配者需要基于此锁变量对各个i2c操作函数实现对应的加锁解锁。
+ volatile unsigned char *regBase; // 【必要】寄存器基地址
+ uint16_t regSize; // 【必要】寄存器位宽
+ int16_t bus; // 【必要】i2c_config.hcs文件中可读取具体值
+ uint32_t clk; // 【可选】驱动适配者自定义
+ uint32_t freq; // 【可选】驱动适配者自定义
+ uint32_t irq; // 【可选】驱动适配者自定义
+ uint32_t regBasePhy; // 【必要】寄存器物理基地址
+ };
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 传输成功 |
- | HDF_FAILURE | 传输失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化I2cCntlr成员,调用核心层I2cCntlrAdd函数,接入VFS(可选)。
-
- ```c
- static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device)
- {
- ...
- /* 遍历、解析i2c_config.hcs中的所有配置节点,并分别进行初始化,需要调用Hi35xxI2cParseAndInit函数。*/
- DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
- ret = Hi35xxI2cParseAndInit(device, childNode);//函数定义见下
- ...
- }
- ...
- }
-
- static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
- {
- struct Hi35xxI2cCntlr *hi35xx = NULL;
- ... // 入参判空
- hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx)); // 内存分配
- ... // 返回值校验
- hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // 地址映射
- ... // 返回值校验
- Hi35xxI2cCntlrInit(hi35xx); // 【必要】i2c设备的初始化
-
- hi35xx->cntlr.priv = (void *)node; // 【必要】存储设备属性
- hi35xx->cntlr.busId = hi35xx->bus; // 【必要】初始化I2cCntlr成员busId
- hi35xx->cntlr.ops = &g_method; // 【必要】I2cMethod的实例化对象的挂载
- hi35xx->cntlr.lockOps = &g_lockOps; // 【必要】I2cLockMethod的实例化对象的挂载
- (void)OsalSpinInit(&hi35xx->spin); // 【必要】锁的初始化
- ret = I2cCntlrAdd(&hi35xx->cntlr); // 【必要】调用此函数将控制器对象添加至平台核心层,返回成功信号后驱动才完全接入平台核心层。
- ...
- #ifdef USER_VFS_SUPPORT
- (void)I2cAddVfsById(hi35xx->cntlr.busId); // 【可选】若支持用户级的虚拟文件系统,则接入。
- #endif
- return HDF_SUCCESS;
- __ERR__: // 若不成功,需要回滚函数内已执行的操作(如取消IO映射、释放内存等),并返回错误码
- if (hi35xx != NULL) {
- if (hi35xx->regBase != NULL) {
- OsalIoUnmap((void *)hi35xx->regBase);
- hi35xx->regBase = NULL;
- }
- OsalMemFree(hi35xx);
- hi35xx = NULL;
- }
- return ret;
- }
- ```
-
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+ // I2cCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct I2cCntlr {
+ struct OsalMutex lock;
+ void *owner;
+ int16_t busId;
+ void *priv;
+ const struct I2cMethod *ops;
+ const struct I2cLockMethod *lockOps;
+ };
+ ```
+
+ - I2cCntlr成员钩子函数结构体I2cMethod的实例化,和锁机制钩子函数结构体I2cLockMethod实例化,其他成员在Init函数中初始化。
+
+ ```c
+ // i2c_hi35xx.c中的示例
+ static const struct I2cMethod g_method = {
+ .transfer = Hi35xxI2cTransfer,
+ };
+
+ static const struct I2cLockMethod g_lockOps = {
+ .lock = Hi35xxI2cLock, // 加锁函数
+ .unlock = Hi35xxI2cUnlock, // 解锁函数
+ };
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表4为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 4** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_IO | I/O错误 |
+ | HDF_SUCCESS | 传输成功 |
+ | HDF_FAILURE | 传输失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化I2cCntlr成员,调用核心层I2cCntlrAdd函数,接入VFS(可选)。
+
+ ```c
+ static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device)
+ {
+ ......
+ // 遍历、解析i2c_config.hcs中的所有配置节点,并分别进行初始化,需要调用Hi35xxI2cParseAndInit函数。
+ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
+ ret = Hi35xxI2cParseAndInit(device, childNode);//函数定义见下
+ ......
+ }
+ ......
+ }
+
+ static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
+ {
+ struct Hi35xxI2cCntlr *hi35xx = NULL;
+ ...... // 入参判空
+ hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx)); // 内存分配
+ ...... // 返回值校验
+ hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // 地址映射
+ ...... // 返回值校验
+ Hi35xxI2cCntlrInit(hi35xx); // 【必要】i2c设备的初始化
+
+ hi35xx->cntlr.priv = (void *)node; // 【必要】存储设备属性
+ hi35xx->cntlr.busId = hi35xx->bus; // 【必要】初始化I2cCntlr成员busId
+ hi35xx->cntlr.ops = &g_method; // 【必要】I2cMethod的实例化对象的挂载
+ hi35xx->cntlr.lockOps = &g_lockOps; // 【必要】I2cLockMethod的实例化对象的挂载
+ (void)OsalSpinInit(&hi35xx->spin); // 【必要】锁的初始化
+ ret = I2cCntlrAdd(&hi35xx->cntlr); // 【必要】调用此函数将控制器对象添加至平台核心层,返回成功信号后驱动才完全接入平台核心层。
+ ......
+ #ifdef USER_VFS_SUPPORT
+ (void)I2cAddVfsById(hi35xx->cntlr.busId); // 【可选】若支持用户级的虚拟文件系统,则接入。
+ #endif
+ return HDF_SUCCESS;
+ __ERR__: // 若不成功,需要回滚函数内已执行的操作(如取消IO映射、释放内存等),并返回错误码
+ if (hi35xx != NULL) {
+ if (hi35xx->regBase != NULL) {
+ OsalIoUnmap((void *)hi35xx->regBase);
+ hi35xx->regBase = NULL;
+ }
+ OsalMemFree(hi35xx);
+ hi35xx = NULL;
+ }
+ return ret;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+
+ ```c
+ static void Hi35xxI2cRelease(struct HdfDeviceObject *device)
+ {
+ ......
+ // 与Hi35xxI2cInit一样,需要将每个节点分别进行释放。
+ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
+ Hi35xxI2cRemoveByNode(childNode); // 函数定义如下
+ }
+ }
- ```c
- static void Hi35xxI2cRelease(struct HdfDeviceObject *device)
- {
- ...
- /* 与Hi35xxI2cInit一样,需要将每个节点分别进行释放。*/
- DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
- Hi35xxI2cRemoveByNode(childNode); // 函数定义如下
- }
- }
-
static void Hi35xxI2cRemoveByNode(const struct DeviceResourceNode *node)
{
- ...
- /* 【必要】可以调用I2cCntlrGet函数通过设备的bus号获取I2cCntlr对象的指针,以及调用I2cCntlrRemove函数将I2cCntlr对象从平台核心层移除。*/
+ ......
+ // 【必要】可以调用I2cCntlrGet函数通过设备的bus号获取I2cCntlr对象的指针,以及调用I2cCntlrRemove函数将I2cCntlr对象从平台核心层移除。
cntlr = I2cCntlrGet(bus);
if (cntlr != NULL && cntlr->priv == node) {
- ...
+ ......
I2cCntlrRemove(cntlr);
- /* 【必要】解除地址映射,释放锁和内存。*/
+ // 【必要】解除地址映射,释放锁和内存。
hi35xx = (struct Hi35xxI2cCntlr *)cntlr;
OsalIoUnmap((void *)hi35xx->regBase);
(void)OsalSpinDestroy(&hi35xx->spin);
@@ -384,3 +397,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
return;
}
```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,消息传输的成功与否等。
diff --git a/zh-cn/device-dev/driver/driver-platform-i3c-des.md b/zh-cn/device-dev/driver/driver-platform-i3c-des.md
index 0926a9a2be4976b86ab3c107cf356480d5b3a88c..4310c03a28e742414a11a1eb6f93715135983420 100755
--- a/zh-cn/device-dev/driver/driver-platform-i3c-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-i3c-des.md
@@ -9,38 +9,49 @@ I3C(Improved Inter Integrated Circuit)总线是由MIPI Alliance开发的一
I3C是两线双向串行总线,针对多个传感器从设备进行了优化,并且一次只能由一个I3C主设备控制。相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。I3C增加了带内中断(In-Bind Interrupt)功能,支持I3C设备进行热接入操作,弥补了I2C总线需要额外增加中断线来完成中断的不足。I3C总线上允许同时存在I2C设备、I3C从设备和I3C次级主设备。
I3C接口定义了完成I3C传输的通用方法集合,包括:
+
- I3C控制器管理:打开或关闭I3C控制器。
+
- I3C控制器配置:获取或配置I3C控制器参数。
+
- I3C消息传输:通过消息传输结构体数组进行自定义传输。
+
- I3C带内中断:请求或释放带内中断。
### 基本概念
- IBI(In-Band Interrupt)
- 带内中断。在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从机同时发出中断请求,I3C主机则通过从机地址进行仲裁,低地址优先相应。
+
+ 带内中断。在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从机同时发出中断请求,I3C主机则通过从机地址进行仲裁,低地址优先相应。
- DAA(Dynamic Address Assignment)
- 动态地址分配。I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C设备都应以两种方式之一来唯一标识:
+
+ 动态地址分配。I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C设备都应以两种方式之一来唯一标识:
+
1)设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址;
+
2)在任何情况下,设备均应具有48位的临时ID。 除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。
- CCC(Common Command Code)
- 通用命令代码,所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。
+
+ 通用命令代码,所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。
- BCR(Bus Characteristic Register)
- 总线特性寄存器,每个连接到 I3C 总线的 I3C 设备都应具有相关的只读总线特性寄存器 (BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。
+
+ 总线特性寄存器,每个连接到 I3C 总线的 I3C 设备都应具有相关的只读总线特性寄存器 (BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。
- DCR(Device Characteristic Register)
- 设备特性寄存器,连接到 I3C 总线的每个 I3C 设备都应具有相关的只读设备特性寄存器 (DCR)。 该寄存器描述了用于动态地址分配和通用命令代码的 I3C 兼容设备类型(例如,加速度计、陀螺仪等)。
+
+ 设备特性寄存器,连接到 I3C 总线的每个 I3C 设备都应具有相关的只读设备特性寄存器 (DCR)。 该寄存器描述了用于动态地址分配和通用命令代码的 I3C 兼容设备类型(例如,加速度计、陀螺仪等)。
### 运作机制
在HDF框架中,I3C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I3C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I3C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。
- 相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。一路I3C总线上,可以连接多个设备,这些设备可以是I2C从设备、I3C从设备和I3C次级主设备,但只能同时存在一个主设备,一般为控制器本身。
+相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。一路I3C总线上,可以连接多个设备,这些设备可以是I2C从设备、I3C从设备和I3C次级主设备,但只能同时存在一个主设备,一般为控制器本身。
-**图 1** I3C物理连线示意图
-![](figures/I3C物理连线示意图.png "I3C物理连线示意图")
+**图 1** I3C物理连线示意图
+![I3C物理连线示意图](figures/I3C物理连线示意图.png)
### 约束与限制
@@ -53,25 +64,26 @@ I3C模块当前仅支持轻量和小型系统内核(LiteOS-A),不支持在
I3C可连接单个或多个I3C、I2C从器件,它主要用于:
- 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等;
+
- 通过软件或硬件协议转换,与其他接口(如 UART 串口等)的设备进行通信。
### 接口说明
I3C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i3c_if.h。
-**表 1** I3C驱动API接口功能介绍
+**表 1** I3C驱动API接口功能介绍
-| 接口名 | 接口描述 |
+| 接口名 | 接口描述 |
| ------------- | ----------------- |
-| DevHandle I3cOpen(int16_t number) | 打开I3C控制器 |
-| void I3cClose(DevHandle handle) | 关闭I3C控制器 |
-| int32_t I3cTransfer(DevHandle handle, struct I3cMsg \*msg, int16_t count, enum TransMode mode) | 自定义传输 |
-| int32_t I3cSetConfig(DevHandle handle, struct I3cConfig \*config) | 配置I3C控制器 |
-| int32_t I3cGetConfig(DevHandle handle, struct I3cConfig \*config) | 获取I3C控制器配置 |
-| int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload) | 请求带内中断 |
-| int32_t I3cFreeIbi(DevHandle handle, uint16_t addr) | 释放带内中断 |
+| DevHandle I3cOpen(int16_t number) | 打开I3C控制器 |
+| void I3cClose(DevHandle handle) | 关闭I3C控制器 |
+| int32_t I3cTransfer(DevHandle handle, struct I3cMsg \*msg, int16_t count, enum TransMode mode) | 自定义传输 |
+| int32_t I3cSetConfig(DevHandle handle, struct I3cConfig \*config) | 配置I3C控制器 |
+| int32_t I3cGetConfig(DevHandle handle, struct I3cConfig \*config) | 获取I3C控制器配置 |
+| int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload) | 请求带内中断 |
+| int32_t I3cFreeIbi(DevHandle handle, uint16_t addr) | 释放带内中断 |
>![](../public_sys-resources/icon-note.gif) **说明:**
>本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。
@@ -80,8 +92,8 @@ I3C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
I3C的使用流程如图2所示。
-**图 2** I3C使用流程图
-![](figures/I3C使用流程图.png "I3C使用流程图")
+**图 2** I3C使用流程图
+![I3C使用流程图](figures/I3C使用流程图.png)
#### 打开I3C控制器
@@ -90,27 +102,27 @@ I3C的使用流程如图2所示。
DevHandle I3cOpen(int16_t number);
```
-**表 2** I3cOpen参数和返回值描述
+**表 2** I3cOpen参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | ------------------- |
-| number | I3C控制器号 |
-| **返回值** | **返回值描述** |
-| NULL | 打开I3C控制器失败 |
+| number | int16_t类型,I3C控制器号 |
+| **返回值** | **返回值描述** |
+| NULL | 打开I3C控制器失败 |
| 控制器句柄 | 打开的I3C控制器句柄 |
假设系统中存在8个I3C控制器,编号从0到7,以下示例代码为打开1号控制器:
```c
-DevHandle i3cHandle = NULL; /* I3C控制器句柄 /
+DevHandle i3cHandle = NULL; // I3C控制器句柄
-/* 打开I3C控制器 */
+// 打开I3C控制器
i3cHandle = I3cOpen(1);
if (i3cHandle == NULL) {
- HDF_LOGE("I3cOpen: failed\n");
- return;
+ HDF_LOGE("I3cOpen: i3c open fail.\n");
+ return NULL;
}
```
@@ -120,27 +132,27 @@ if (i3cHandle == NULL) {
int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config);
```
-**表 3** I3cGetConfig参数和返回值描述
+**表 3** I3cGetConfig参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | I3C控制器句柄 |
-| config | I3C控制器配置 |
+| handle | DevHandle类型,I3C控制器句柄 |
+| config | 结构体指针,I3C控制器配置 |
| **返回值** | **返回值描述** |
-| 0 | 获取成功 |
-| 负数 | 获取失败 |
+| HDF_SUCCESS | 获取成功 |
+| 负数 | 获取失败 |
获取I3C控制器配置示例:
```c
struct I3cConfig config;
-ret = I3cGetConfig(i3cHandle, &config);
+int32_t ret = I3cGetConfig(i3cHandle, &config);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: Get config fail!", __func__);
- return HDF_FAILURE;
+ HDF_LOGE("I3cGetConfig: get config fail, ret:%d", ret);
+ return ret;
}
```
@@ -150,17 +162,17 @@ if (ret != HDF_SUCCESS) {
int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config);
```
-**表 4** I3cSetConfig参数和返回值描述
+**表 4** I3cSetConfig参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | I3C控制器句柄 |
-| config | I3C控制器配置 |
+| handle | DevHandle类型,I3C控制器句柄 |
+| config | 结构体指针,I3C控制器配置 |
| **返回值** | **返回值描述** |
-| 0 | 配置成功 |
-| 负数 | 配置失败 |
+| HDF_SUCCESS | 配置成功 |
+| 负数 | 配置失败 |
配置I3C控制器示例:
@@ -169,10 +181,10 @@ struct I3cConfig config;
config->busMode = I3C_BUS_HDR_MODE;
config->curMaster = NULL;
-ret = I3cSetConfig(i3cHandle, &config);
+int32_t ret = I3cSetConfig(i3cHandle, &config);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: Set config fail!", __func__);
- return HDF_FAILURE;
+ HDF_LOGE("I3cSetConfig: set config fail, ret:%d", ret);
+ return ret;
}
```
@@ -183,19 +195,19 @@ if (ret != HDF_SUCCESS) {
int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum TransMode mode);
```
-**表 5** I3cTransfer参数和返回值描述
+**表 5** I3cTransfer参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------------------------------------- |
-| handle | I3C控制器句柄 |
-| msgs | 待传输数据的消息结构体数组 |
-| count | 消息数组长度 |
-| mode | 传输模式,0:I2C模式;1:I3C模式;2:发送CCC |
-| **返回值** | **返回值描述** |
-| 正整数 | 成功传输的消息结构体数目 |
-| 负数 | 执行失败 |
+| handle | DevHandle类型,I3C控制器句柄 |
+| msgs | 结构体指针,待传输数据的消息结构体数组 |
+| count | int16_t类型,消息数组长度 |
+| mode | 枚举类型,传输模式,0:I2C模式;1:I3C模式;2:发送CCC |
+| **返回值** | **返回值描述** |
+| 正整数 | 成功传输的消息结构体数目 |
+| 负数 | 执行失败 |
I3C传输消息类型为I3cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。
@@ -203,20 +215,20 @@ I3C传输消息类型为I3cMsg,每个传输消息结构体表示一次读或
int32_t ret;
uint8_t wbuff[2] = { 0x12, 0x13 };
uint8_t rbuff[2] = { 0 };
-struct I3cMsg msgs[2]; /* 自定义传输的消息结构体数组 */
-msgs[0].buf = wbuff; /* 写入的数据 */
-msgs[0].len = 2; /* 写入数据长度为2 */
-msgs[0].addr = 0x3F; /* 写入设备地址为0x3F */
-msgs[0].flags = 0; /* 传输标记为0,默认为写 */
-msgs[1].buf = rbuff; /* 要读取的数据 */
-msgs[1].len = 2; /* 读取数据长度为2 */
-msgs[1].addr = 0x3F; /* 读取设备地址为0x3F */
-msgs[1].flags = I3C_FLAG_READ /* I3C_FLAG_READ置位 */
-/* 进行一次I2C模式自定义传输,传输的消息个数为2 */
+struct I3cMsg msgs[2]; // 自定义传输的消息结构体数组
+msgs[0].buf = wbuff; // 写入的数据
+msgs[0].len = 2; // 写入数据长度为2
+msgs[0].addr = 0x3F; // 写入设备地址为0x3F
+msgs[0].flags = 0; // 传输标记为0,默认为写
+msgs[1].buf = rbuff; // 要读取的数据
+msgs[1].len = 2; // 读取数据长度为2
+msgs[1].addr = 0x3F; // 读取设备地址为0x3F
+msgs[1].flags = I3C_FLAG_READ // I3C_FLAG_READ置位
+// 进行一次I2C模式自定义传输,传输的消息个数为2
ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE);
if (ret != 2) {
- HDF_LOGE("I3cTransfer: failed, ret %d\n", ret);
- return;
+ HDF_LOGE("I3cTransfer: transfer fail, ret:%d\n", ret);
+ return HDF_FAILURE;
}
```
@@ -232,19 +244,19 @@ if (ret != 2) {
int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload);
```
-**表 6** I3cRequestIbi参数和返回值描述
+**表 6** I3cRequestIbi参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | I3C控制器句柄 |
-| addr | I3C设备地址 |
-| func | IBI回调函数 |
-| payload | IBI有效载荷 |
+| handle | DevHandle类型,I3C控制器句柄 |
+| addr | uint16_t类型,I3C设备地址 |
+| func | 函数指针,IBI回调函数 |
+| payload | IBI有效载荷 |
| **返回值** | **返回值描述** |
-| 0 | 请求成功 |
-| 负数 | 请求失败 |
+| HDF_SUCCESS | 请求成功 |
+| 负数 | 请求失败 |
请求带内中断示例:
@@ -253,9 +265,9 @@ static int32_t TestI3cIbiFunc(DevHandle handle, uint16_t addr, struct I3cIbiData
{
(void)handle;
(void)addr;
- HDF_LOGD("%s: %.16s", __func__, (char *)data.buf);
+ HDF_LOGD("TestI3cIbiFunc: %.16s", (char *)data.buf);
- return 0;
+ return HDF_SUCCESS;
}
int32_t I3cTestRequestIbi(void)
@@ -263,22 +275,22 @@ int32_t I3cTestRequestIbi(void)
DevHandle i3cHandle = NULL;
int32_t ret;
- /* 打开I3C控制器 */
+ // 打开I3C控制器
i3cHandle = I3cOpen(1);
if (i3cHandle == NULL) {
- HDF_LOGE("I3cOpen: failed\n");
- return;
-}
+ HDF_LOGE("I3cOpen: i3c open fail.\n");
+ return;
+ }
ret = I3cRequestIbi(i3cHandle, 0x3F, TestI3cIbiFunc, 16);
- if (ret != 0) {
+ if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: Request IBI failed!", __func__);
- return -1;
+ return ret;
}
I3cClose(i3cHandle);
- HDF_LOGD("%s: Done", __func__);
+ HDF_LOGD("I3cTestRequestIbi: done");
- return 0;
+ return HDF_SUCCESS;
}
```
@@ -288,22 +300,22 @@ int32_t I3cTestRequestIbi(void)
int32_t I3cFreeIbi(DevHandle handle, uint16_t addr);
```
-**表 7** I3cFreeIbi参数和返回值描述
+**表 7** I3cFreeIbi参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | I3C控制器句柄 |
-| addr | I3C设备地址 |
+| handle | DevHandle类型,I3C控制器句柄 |
+| addr | uint16_t类型,I3C设备地址 |
| **返回值** | **返回值描述** |
-| 0 | 释放成功 |
-| 负数 | 释放失败 |
+| HDF_SUCCESS | 释放成功 |
+| 负数 | 释放失败 |
释放带内中断示例:
```c
-I3cFreeIbi(i3cHandle, 0x3F); /* 释放带内中断 */
+I3cFreeIbi(i3cHandle, 0x3F); // 释放带内中断
```
#### 关闭I3C控制器
@@ -313,57 +325,57 @@ I3C通信完成之后,需要关闭I3C控制器,关闭函数如下所示:
void I3cClose(DevHandle handle);
```
-**表 8** I3cClose参数和返回值描述
+**表 8** I3cClose参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | -------------- |
-| handle | I3C控制器句柄 |
+| handle | DevHandle类型,I3C控制器句柄 |
关闭I3C控制器实例:
```c
-I3cClose(i3cHandle); /* 关闭I3C控制器 */
+I3cClose(i3cHandle); // 关闭I3C控制器
```
## 使用实例
本例程以操作Hi3516DV300开发板上的I3C虚拟设备为例,详细展示I3C接口的完整使用流程,基本硬件信息如下。
-- SOC:hi3516dv300。
+- SOC:hi3516dv300。
-- 虚拟I3C设备:I3C地址为0x3f, 寄存器位宽为1字节。
+- 虚拟I3C设备:I3C地址为0x3f, 寄存器位宽为1字节。
-- 硬件连接:虚拟I3C设备挂接在18号和19号I3C控制器下。
+- 硬件连接:虚拟I3C设备挂接在18号和19号I3C控制器下。
本例程进行简单的I3C传输,测试I3C通路是否正常。
示例如下:
```c
-#include "i3c_if.h" /* I3C标准接口头文件 */
-#include "hdf_log.h" /* 标准日志打印头文件 */
-#include "osal_io.h" /* 标准IO读写接口头文件 */
-#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */
+#include "i3c_if.h" // I3C标准接口头文件
+#include "hdf_log.h" // 标准日志打印头文件
+#include "osal_io.h" // 标准IO读写接口头文件
+#include "osal_time.h" // 标准延迟&睡眠接口头文件
-/* 定义一个表示设备的结构体,存储信息 */
+// 定义一个表示设备的结构体,存储信息
struct TestI3cDevice {
- uint16_t busNum; /* I3C总线号 */
- uint16_t addr; /* I3C设备地址 */
- uint16_t regLen; /* 寄存器字节宽度 */
- DevHandle i3cHandle; /* I3C控制器句柄 */
+ uint16_t busNum; // I3C总线号
+ uint16_t addr; // I3C设备地址
+ uint16_t regLen; // 寄存器字节宽度
+ DevHandle i3cHandle; // I3C控制器句柄
};
-/* 基于I3cTransfer方法封装一个寄存器读写的辅助函数, 通过flag表示读或写 */
-static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAddr,
+// 基于I3cTransfer方法封装一个寄存器读写的辅助函数,通过flag表示读或写
+static int32_t TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAddr,
unsigned char *regData, unsigned int dataLen, uint8_t flag)
{
int index = 0;
unsigned char regBuf[4] = {0};
struct I3cMsg msgs[2] = {0};
- /* 单双字节寄存器长度适配 */
+ // 单双字节寄存器长度适配
if (testDevice->regLen == 1) {
regBuf[index++] = regAddr & 0xFF;
} else {
@@ -371,81 +383,80 @@ static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAd
regBuf[index++] = regAddr & 0xFF;
}
- /* 填充I3cMsg消息结构 */
+ // 填充I3cMsg消息结构
msgs[0].addr = testDevice->addr;
- msgs[0].flags = 0; /* 标记为0,表示写入 */
+ msgs[0].flags = 0; // 标记为0,表示写入
msgs[0].len = testDevice->regLen;
msgs[0].buf = regBuf;
msgs[1].addr = testDevice->addr;
- msgs[1].flags = (flag == 1) ? I3C_FLAG_READ : 0; /* 添加读标记位,表示读取 */
+ msgs[1].flags = (flag == 1) ? I3C_FLAG_READ : 0; // 添加读标记位,表示读取
msgs[1].len = dataLen;
msgs[1].buf = regData;
if (I3cTransfer(testDevice->i3cHandle, msgs, 2, I2C_MODE) != 2) {
- HDF_LOGE("%s: i3c read err", __func__);
+ HDF_LOGE("TestI3cReadWrite: i3c transfer err.");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
-/* 寄存器读函数 */
-static inline int TestI3cReadReg(struct TestI3cDevice *testDevice, unsigned int regAddr,
+// 寄存器读函数
+static inline int32_t TestI3cReadReg(struct TestI3cDevice *testDevice, unsigned int regAddr,
unsigned char *regData, unsigned int dataLen)
{
return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 1);
}
-/* 寄存器写函数 */
-static inline int TestI3cWriteReg(struct TestI3cDevice *testDevice, unsigned int regAddr,
+// 寄存器写函数
+static inline int32_t TestI3cWriteReg(struct TestI3cDevice *testDevice, unsigned int regAddr,
unsigned char *regData, unsigned int dataLen)
{
return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 0);
}
-/* I3C例程总入口 */
+// I3C例程总入口
static int32_t TestCaseI3c(void)
{
- int32_t i;
int32_t ret;
unsigned char bufWrite[7] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xA, 0xB, 0xC };
unsigned char bufRead[7] = {0};
static struct TestI3cDevice testDevice;
- /* 设备信息初始化 */
+ // 设备信息初始化
testDevice.busNum = 18;
testDevice.addr = 0x3F;
- testDevice.regLen = 1;
+ testDevice.regLen = 2;
testDevice.i3cHandle = NULL;
- /* 打开I3C控制器 */
+ // 打开I3C控制器
testDevice.i3cHandle = I3cOpen(testDevice.busNum);
if (testDevice.i3cHandle == NULL) {
- HDF_LOGE("%s: Open I3c:%u fail!", __func__, testDevice.busNum);
- return -1;
+ HDF_LOGE("TestCaseI3c: open I3c:%u fail!", testDevice.busNum);
+ return HDF_FAILURE;
}
- /* 向地址为0x3F的设备连续写7字节数据 */
+ // 向地址为0x3F的设备连续写7字节数据
ret = TestI3cWriteReg(&testDevice, 0x3F, bufWrite, 7);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: test i3c write reg fail!:%d", __func__, ret);
+ HDF_LOGE("TestCaseI3c: test i3c write reg fail, ret:%d", ret);
I3cClose(testDevice.i3cHandle);
- return -1;
+ return ret;
}
OsalMSleep(10);
- /* 从地址为0x3F的设备连续读7字节数据 */
+ // 从地址为0x3F的设备连续读7字节数据
ret = TestI3cReadReg(&testDevice, 0x3F, bufRead, 7);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: test i3c read reg fail!:%d", __func__, ret);
+ HDF_LOGE("TestCaseI3c: test i3c read reg fail, ret:%d", ret);
I3cClose(testDevice.i3cHandle);
- return -1;
+ return ret;
}
- HDF_LOGI("%s: test i3c write&read reg success!", __func__);
-
- /* 访问完毕关闭I3C控制器 */
+ HDF_LOGD("TestCaseI3c: test i3c write&read reg success!");
+ HDF_LOGD("TestCaseI3c: function tests end.");
+ // 访问完毕关闭I3C控制器
I3cClose(testDevice.i3cHandle);
- return 0;
+ return HDF_SUCCESS;
}
```
diff --git a/zh-cn/device-dev/driver/driver-platform-i3c-develop.md b/zh-cn/device-dev/driver/driver-platform-i3c-develop.md
index 125f67535d37a6a4ae9bbbf8303a69155fde5b0a..e48fac00702b4bc09fc96f75e71e639cccce5ac0 100755
--- a/zh-cn/device-dev/driver/driver-platform-i3c-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-i3c-develop.md
@@ -12,27 +12,27 @@ I3C是两线双向串行总线,针对多个传感器从设备进行了优化
- IBI(In-Band Interrupt):带内中断。
- 在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从设备同时发出中断请求,I3C主设备则通过从设备地址进行仲裁,低地址优先相应。
+ 在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从设备同时发出中断请求,I3C主设备则通过从设备地址进行仲裁,低地址优先相应。
- DAA(Dynamic Address Assignment):动态地址分配。
- I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C/I2C设备都应以两种方式之一来唯一标识:
+ I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C/I2C设备都应以两种方式之一来唯一标识:
- - 设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址。
+ - 设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址。
- - 在任何情况下,I3C设备均应具有48位的临时ID。除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。
+ - 在任何情况下,I3C设备均应具有48位的临时ID。除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。
- CCC(Common Command Code):通用命令代码。
- 所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。
+ 所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。
- BCR(Bus Characteristic Register):总线特性寄存器。
- 每个连接到I3C总线的I3C设备都应具有相关的只读总线特性寄存器(BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。
+ 每个连接到I3C总线的I3C设备都应具有相关的只读总线特性寄存器(BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。
- DCR(Device Characteristic Register):设备特性寄存器。
- 连接到I3C总线的每个I3C设备都应具有相关的只读设备特性寄存器(DCR),该寄存器描述了用于动态地址分配和通用命令代码的I3C兼容设备类型(例如加速度计、陀螺仪等)。
+ 连接到I3C总线的每个I3C设备都应具有相关的只读设备特性寄存器(DCR),该寄存器描述了用于动态地址分配和通用命令代码的I3C兼容设备类型(例如加速度计、陀螺仪等)。
### 运作机制
@@ -42,14 +42,16 @@ I3C是两线双向串行总线,针对多个传感器从设备进行了优化
I3C模块各分层的作用为:
- 接口层:提供打开设备,写入数据,关闭设备的能力。
+
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。由于框架需要统一管理I3C总线上挂载的所有设备,因此还提供了添加、删除以及获取设备的能力,以及中断回调函数。
+
- 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
- **图 1** I3C统一服务模式结构图
+**图 1** I3C统一服务模式结构图
-![image1](figures/统一服务模式结构图.png)
+![I3C统一服务模式结构图](figures/统一服务模式结构图.png)
### 约束与限制
@@ -62,6 +64,7 @@ I3C模块当前仅支持轻量和小型系统内核(LiteOS-A) 。
I3C可连接单个或多个I3C、I2C从器件,它主要用于:
- 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等。
+
- 通过软件或硬件协议转换,与其他通信接口(如UART串口等)的设备进行通信。
当驱动开发者需要将I3C设备适配到OpenHarmony时,需要进行I3C驱动适配,下文将介绍如何进行I3C驱动适配。
@@ -71,6 +74,7 @@ I3C可连接单个或多个I3C、I2C从器件,它主要用于:
为了保证上层在调用I3C接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/i3c/i3c_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互。
I3cMethod定义:
+
```c
struct I3cMethod {
int32_t (*sendCccCmd)(struct I3cCntlr *cntlr, struct I3cCccCmd *ccc);
@@ -83,39 +87,52 @@ struct I3cMethod {
};
```
-**表1** I3cMethod结构体成员的钩子函数功能说明
-|函数成员|入参|出参|返回值|功能|
-|-|-|-|-|-|
-|sendCccCmd| **cntlr**:结构体指针,核心层I3C控制器
**ccc**:传入的通用命令代码结构体指针 | **ccc**:传出的通用命令代码结构体指针 | HDF_STATUS相关状态|发送CCC(Common command Code,即通用命令代码)|
-|Transfer | **cntlr**:结构体指针,核心层I3C控制器
**msgs**:结构体指针,用户消息
**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息| HDF_STATUS相关状态 | 使用I3C模式传递用户消息 |
+**表 1** I3cMethod结构体成员的钩子函数功能说明
+| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
+| - | - | - | - | - |
+| sendCccCmd | **cntlr**:结构体指针,核心层I3C控制器
**ccc**:传入的通用命令代码结构体指针 | **ccc**:传出的通用命令代码结构体指针 | HDF_STATUS相关状态|发送CCC(Common command Code,即通用命令代码)|
+| Transfer | **cntlr**:结构体指针,核心层I3C控制器
**msgs**:结构体指针,用户消息
**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息 | HDF_STATUS相关状态 | 使用I3C模式传递用户消息 |
|i2cTransfer | **cntlr**:结构体指针,核心层I3C控制器
**msgs**:结构体指针,用户消息
**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息 | HDF_STATUS相关状态 | 使用I2C模式传递用户消息 |
-|setConfig| **cntlr**:结构体指针,核心层I3C控制器
**config**:控制器配置参数| 无 | HDF_STATUS相关状态 | 设置I3C控制器配置参数 |
-|getConfig| **cntlr**:结构体指针,核心层I3C控制器| **config**:控制器配置参数 | HDF_STATUS相关状态 | 获取I3C控制器配置参数 |
-|requestIbi| **device**:结构体指针,核心层I3C设备| 无 | HDF_STATUS相关状态 | 为I3C设备请求IBI(In-Bind Interrupt,即带内中断) |
-|freeIbi| **device**:结构体指针,核心层I3C设备| 无 | HDF_STATUS相关状态 | 释放IBI |
+| setConfig | **cntlr**:结构体指针,核心层I3C控制器
**config**:控制器配置参数| 无 | HDF_STATUS相关状态 | 设置I3C控制器配置参数 |
+| getConfig | **cntlr**:结构体指针,核心层I3C控制器 | **config**:控制器配置参数 | HDF_STATUS相关状态 | 获取I3C控制器配置参数 |
+| requestIbi | **device**:结构体指针,核心层I3C设备 | 无 | HDF_STATUS相关状态 | 为I3C设备请求IBI(In-Bind Interrupt,即带内中断) |
+| freeIbi | **device**:结构体指针,核心层I3C设备 | 无 | HDF_STATUS相关状态 | 释放IBI |
### 开发步骤
-I3C模块适配的四个环节是实例化驱动入口、配置属性文件、实例化I3C控制器对象以及注册中断处理子程序。
+I3C模块适配包含以下五个步骤:
+
+- 实例化驱动入口
-- 实例化驱动入口:
- 实例化HdfDriverEntry结构体成员。
+
- 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
-- 配置属性文件:
+- 配置属性文件
- 在device_info.hcs文件中添加deviceNode描述。
+
- 【可选】添加i3c_config.hcs器件属性文件。
-- 实例化I3C控制器对象:
+- 实例化I3C控制器对象
+
- 初始化I3cCntlr成员。
+
- 实例化I3cCntlr成员I3cMethod方法集合,其定义和成员函数说明见下文。
-
-- 注册中断处理子程序:
+
+- 注册中断处理子程序
+
为控制器注册中断处理程序,实现设备热接入和IBI(带内中断)功能。
-1. 实例化驱动入口
+- 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功,数据能否传输等。
+### 开发实例
+
+下方将以Hi3516DV300的虚拟驱动//drivers/hdf_core/framework/test/unittest/platform/virtual/i3c_virtual.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
+
+1. 实例化驱动入口
驱动入口必须为HdfDriverEntry(在//drivers/hdf_core/framework/include/core/hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
@@ -132,34 +149,35 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
.moduleVersion = 1,
.Init = VirtualI3cInit,
.Release = VirtualI3cRelease,
- .moduleName = "virtual_i3c_driver", // 【必要且与hcs文件中的名字匹配】
+ .moduleName = "virtual_i3c_driver", // 【必要且与hcs文件中的名字匹配】
};
- HDF_INIT(g_virtualI3cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ HDF_INIT(g_virtualI3cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
- /* 核心层i3c_core.c管理器服务的驱动入口 */
+ // 核心层i3c_core.c管理器服务的驱动入口
struct HdfDriverEntry g_i3cManagerEntry = {
.moduleVersion = 1,
.Init = I3cManagerInit,
.Release = I3cManagerRelease,
- .moduleName = "HDF_PLATFORM_I3C_MANAGER", // 这与device_info.hcs文件中device0对应
+ .moduleName = "HDF_PLATFORM_I3C_MANAGER", // 这与device_info.hcs文件中device0对应
};
HDF_INIT(g_i3cManagerEntry);
```
2. 配置属性文件
+ 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i3c_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I3cCntlr相关成员的默认值或限制范围有密切关系。
- 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i3c_config.hcs中配置器件属性。
+ 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I3C管理器,其各项参数必须如表2设置:
- deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I3cCntlr相关成员的默认值或限制范围有密切关系。
-
- 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I3C管理器,其各项参数必须如下设置:
-
- |成员名|值|
- |-|-|
- |moduleName |HDF_PLATFORM_I3C_MANAGER|
- |serviceName|无(预留)|
- |policy|0|
- |cntlrMatchAttr| 无(预留)|
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,I3C管理器具体配置为0,表示驱动不需要发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。I3C管理器具体配置为52 |
+ | permission | 驱动创建设备节点权限,I3C管理器具体配置为0664 |
+ | moduleName | 驱动名称,I3C管理器固定为HDF_PLATFORM_I3C_MANAGER |
+ | serviceName | 驱动对外发布服务的名称,I3C管理器服务名设置为HDF_PLATFORM_I3C_MANAGER |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,I3C管理器没有使用,可忽略 |
从第二个节点开始配置具体I3C控制器信息,此节点并不表示某一路I3C控制器,而是代表一个资源性质设备,用于描述一类I3C控制器的信息。本例只有一个I3C控制器,如有多个控制器,则需要在device_info.hcs文件增加deviceNode信息,以及在i3c_config文件中增加对应的器件属性。
@@ -228,12 +246,12 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
此步骤需要通过实现HdfDriverEntry成员函数(Bind,Init,Release)来完成。
- I3cCntlr成员钩子函数结构体I3cMethod的实例化,I3cLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发,其他成员在Init函数中初始化。
+ I3cCntlr成员钩子函数结构体I3cMethod的实例化,I3cLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发。
- 自定义结构体参考
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 从驱动的角度看,自定义结构体是参数和数据的载体,而且i3c_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I3cCntlr对象,例如设备号、总线号等。
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 从驱动的角度看,自定义结构体是参数和数据的载体,而且i3c_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I3cCntlr对象,例如设备号、总线号等。
```c
struct VirtualI3cCntlr {
@@ -250,7 +268,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
uint32_t i2cFmPlusRate;
};
- /* I3cCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。 */
+ // I3cCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。
struct I3cCntlr {
OsalSpinlock lock;
void *owner;
@@ -268,121 +286,122 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
**入参:**
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
**返回值:**
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS 定义)。
-
-
- |状态(值)|问题描述|
- |:-|:-:|
- |HDF_ERR_INVALID_OBJECT|控制器对象非法|
- |HDF_ERR_INVALID_PARAM |参数非法|
- |HDF_ERR_MALLOC_FAIL |内存分配失败|
- |HDF_ERR_IO |I/O 错误|
- |HDF_SUCCESS |传输成功|
- |HDF_FAILURE |传输失败|
-
- **函数说明:**
-
- 初始化自定义结构体对象,初始化I3cCntlr成员,调用核心层I3cCntlrAdd函数。
-
- ```c
- static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
- {
- int32_t ret;
- struct VirtualI3cCntlr *virtual = NULL; // 【必要】自定义结构体对象
- (void)device;
-
- virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // 【必要】内存分配
- if (virtual == NULL) {
- HDF_LOGE("%s: Malloc virtual fail!", __func__);
- return HDF_ERR_MALLOC_FAIL;
- }
-
- ret = VirtualI3cReadDrs(virtual, node); // 【必要】将i3c_config文件的默认值填充到结构体中,函数定义见下方
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
- goto __ERR__;
- }
- ...
- virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize); // 【必要】地址映射
- ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); //【必要】注册中断程序
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: register irq failed!", __func__);
- return ret;
- }
- ...
- VirtualI3cCntlrInit(virtual); // 【必要】I3C设备的初始化
- virtual->cntlr.priv = (void *)node; // 【必要】存储设备属性
- virtual->cntlr.busId = virtual->busId; // 【必要】初始化I3cCntlr成员
- virtual->cntlr.ops = &g_method; // 【必要】I3cMethod的实例化对象的挂载
- (void)OsalSpinInit(&virtual->spin);
- ret = I3cCntlrAdd(&virtual->cntlr); // 【必要且重要】调用此函数将控制器添加至核心,返回成功信号后驱动才完全接入平台核心层。
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret);
- (void)OsalSpinDestroy(&virtual->spin);
- goto __ERR__;
- }
-
- return HDF_SUCCESS;
- __ERR__: // 若控制器添加失败,需要执行去初始化相关函数。
- if (virtual != NULL) {
- OsalMemFree(virtual);
- virtual = NULL;
- }
-
- return ret;
- }
-
- static int32_t VirtualI3cInit(struct HdfDeviceObject *device)
- {
- int32_t ret;
- const struct DeviceResourceNode *childNode = NULL;
-
- if (device == NULL || device->property == NULL) {
- HDF_LOGE("%s: device or property is NULL", __func__);
- return HDF_ERR_INVALID_OBJECT;
- }
-
- DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
- ret = VirtualI3cParseAndInit(device, childNode);
- if (ret != HDF_SUCCESS) {
- break;
- }
- }
-
- return ret;
- }
-
- static int32_t VirtualI3cReadDrs(struct VirtualI3cCntlr *virtual, const struct DeviceResourceNode *node)
- {
- struct DeviceResourceIface *drsOps = NULL;
-
- /* 获取drsOps方法 */
- drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
- HDF_LOGE("%s: Invalid drs ops fail!", __func__);
- return HDF_FAILURE;
- }
- /* 将配置参数依次读出,并填充至结构体中 */
- if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) {
- HDF_LOGE("%s: Read busId fail!", __func__);
- return HDF_ERR_IO;
- }
- if (drsOps->GetUint16(node, "busMode", &virtual->busMode, 0) != HDF_SUCCESS) {
- HDF_LOGE("%s: Read busMode fail!", __func__);
- return HDF_ERR_IO;
- }
- if (drsOps->GetUint16(node, "IrqNum", &virtual->IrqNum, 0) != HDF_SUCCESS) {
- HDF_LOGE("%s: Read IrqNum fail!", __func__);
- return HDF_ERR_IO;
- }
- ···
- return HDF_SUCCESS;
- }
- ```
+ HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_IO | I/O错误 |
+ | HDF_SUCCESS | 传输成功 |
+ | HDF_FAILURE | 传输失败 |
+
+ **函数说明:**
+
+ 初始化自定义结构体对象,初始化I3cCntlr成员,调用核心层I3cCntlrAdd函数。
+
+ ```c
+ static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
+ {
+ int32_t ret;
+ struct VirtualI3cCntlr *virtual = NULL; // 【必要】自定义结构体对象
+ (void)device;
+
+ virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // 【必要】内存分配
+ if (virtual == NULL) {
+ HDF_LOGE("%s: Malloc virtual fail!", __func__);
+ return HDF_ERR_MALLOC_FAIL;
+ }
+
+ ret = VirtualI3cReadDrs(virtual, node); // 【必要】将i3c_config文件的默认值填充到结构体中,函数定义见下方
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
+ goto __ERR__;
+ }
+ ......
+ virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize); // 【必要】地址映射
+ ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); //【必要】注册中断程序
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: register irq failed!", __func__);
+ return ret;
+ }
+ ......
+ VirtualI3cCntlrInit(virtual); // 【必要】I3C设备的初始化
+ virtual->cntlr.priv = (void *)node; // 【必要】存储设备属性
+ virtual->cntlr.busId = virtual->busId; // 【必要】初始化I3cCntlr成员
+ virtual->cntlr.ops = &g_method; // 【必要】I3cMethod的实例化对象的挂载
+ (void)OsalSpinInit(&virtual->spin);
+ ret = I3cCntlrAdd(&virtual->cntlr); // 【必要且重要】调用此函数将控制器添加至核心,返回成功信号后驱动才完全接入平台核心层。
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret);
+ (void)OsalSpinDestroy(&virtual->spin);
+ goto __ERR__;
+ }
+
+ return HDF_SUCCESS;
+ __ERR__: // 若控制器添加失败,需要执行去初始化相关函数。
+ if (virtual != NULL) {
+ OsalMemFree(virtual);
+ virtual = NULL;
+ }
+
+ return ret;
+ }
+
+ static int32_t VirtualI3cInit(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ const struct DeviceResourceNode *childNode = NULL;
+
+ if (device == NULL || device->property == NULL) {
+ HDF_LOGE("%s: device or property is NULL", __func__);
+ return HDF_ERR_INVALID_OBJECT;
+ }
+
+ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
+ ret = VirtualI3cParseAndInit(device, childNode);
+ if (ret != HDF_SUCCESS) {
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ static int32_t VirtualI3cReadDrs(struct VirtualI3cCntlr *virtual, const struct DeviceResourceNode *node)
+ {
+ struct DeviceResourceIface *drsOps = NULL;
+
+ // 获取drsOps方法
+ drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
+ if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
+ HDF_LOGE("%s: Invalid drs ops fail!", __func__);
+ return HDF_FAILURE;
+ }
+ // 将配置参数依次读出,并填充至结构体中
+ if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) {
+ HDF_LOGE("%s: Read busId fail!", __func__);
+ return HDF_ERR_IO;
+ }
+ if (drsOps->GetUint16(node, "busMode", &virtual->busMode, 0) != HDF_SUCCESS) {
+ HDF_LOGE("%s: Read busMode fail!", __func__);
+ return HDF_ERR_IO;
+ }
+ if (drsOps->GetUint16(node, "IrqNum", &virtual->IrqNum, 0) != HDF_SUCCESS) {
+ HDF_LOGE("%s: Read IrqNum fail!", __func__);
+ return HDF_ERR_IO;
+ }
+ ......
+ return HDF_SUCCESS;
+ }
+ ```
- Release函数开发参考
@@ -409,20 +428,20 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
struct I3cCntlr *cntlr = NULL;
struct VirtualI3cCntlr *virtual = NULL;
struct DeviceResourceIface *drsOps = NULL;
-
+
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
if (drsOps == NULL || drsOps->GetUint32 == NULL) {
HDF_LOGE("%s: invalid drs ops fail!", __func__);
return;
}
-
+
ret = drsOps->GetUint16(node, "busId", (uint16_t *)&busId, 0);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: read busId fail!", __func__);
return;
}
- ...
- /* 可以调用I3cCntlrGet函数通过设备的cntlrNum获取I3cCntlr对象,以及调用I3cCntlrRemove函数来释放I3cCntlr对象的内容。 */
+ ......
+ // 可以调用I3cCntlrGet函数通过设备的cntlrNum获取I3cCntlr对象,以及调用I3cCntlrRemove函数来释放I3cCntlr对象的内容。
cntlr = I3cCntlrGet(busId);
if (cntlr != NULL && cntlr->priv == node) {
I3cCntlrPut(cntlr);
@@ -433,7 +452,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
}
return;
}
-
+
static void VirtualI3cRelease(struct HdfDeviceObject *device)
{
const struct DeviceResourceNode *childNode = NULL;
@@ -444,8 +463,8 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
HDF_LOGE("%s: device or property is NULL", __func__);
return;
}
- ...
- /* 遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作 */
+ ......
+ // 遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
VirtualI3cRemoveByNode(childNode); //函数定义如上
}
@@ -471,31 +490,31 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
case I3C_RESERVED_ADDR_7H7A:
case I3C_RESERVED_ADDR_7H7C:
case I3C_RESERVED_ADDR_7H7F:
- /* 广播地址单比特错误的所有情形 */
+ // 广播地址单比特错误的所有情形
HDF_LOGW("%s: broadcast Address single bit error!", __func__);
break;
default:
HDF_LOGD("%s: Reserved address which is not supported!", __func__);
break;
}
-
+
return HDF_SUCCESS;
}
-
+
static int32_t I3cIbiHandle(uint32_t irq, void *data)
{
struct VirtualI3cCntlr *virtual = NULL;
struct I3cDevice *device = NULL;
uint16_t ibiAddr;
char *testStr = "Hello I3C!";
-
+
(void)irq;
if (data == NULL) {
HDF_LOGW("%s: data is NULL!", __func__);
return HDF_ERR_INVALID_PARAM;
}
virtual = (struct VirtualI3cCntlr *)data;
- /* 【必要】获取产生中断的地址,使用CHECK_RESERVED_ADDR宏判断该地址是否为I3C保留地址。 */
+ // 【必要】获取产生中断的地址,使用CHECK_RESERVED_ADDR宏判断该地址是否为I3C保留地址。
ibiAddr = VirtualI3cGetIbiAddr();
if (CHECK_RESERVED_ADDR(ibiAddr) == I3C_ADDR_RESERVED) {
HDF_LOGD("%s: Calling VirtualI3cResAddrWorker...", __func__);
@@ -508,13 +527,17 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
return HDF_ERR_MALLOC_FAIL;
}
if (device->ibi->payload > VIRTUAL_I3C_TEST_STR_LEN) {
- /* 将字符串"Hello I3C!"放入IBI缓冲区内 */
+ // 将字符串"Hello I3C!"放入IBI缓冲区内
*device->ibi->data = *testStr;
}
- /* 根据产生IBI的I3C设备调用IBI回调函数 */
+ // 根据产生IBI的I3C设备调用IBI回调函数
return I3cCntlrIbiCallback(device);
}
-
+
return HDF_SUCCESS;
}
- ```
\ No newline at end of file
+ ```
+
+5. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功,数据能否传输等。
diff --git a/zh-cn/device-dev/driver/driver-platform-mipicsi-des.md b/zh-cn/device-dev/driver/driver-platform-mipicsi-des.md
index 31aad8035c3f0b6eafdfafbc93a93b5dcb3a61a1..0bb5052f2f9ab7cb7b10a2c51279a4a0680e50d4 100755
--- a/zh-cn/device-dev/driver/driver-platform-mipicsi-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-mipicsi-des.md
@@ -10,36 +10,36 @@ CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接
图1显示了简化的CSI接口。D-PHY采用1对源同步的差分时钟和1~4对差分数据线来进行数据传输。数据传输采用DDR方式,即在时钟的上下边沿都有数据传输。
-**图1** CSI发送、接收接口
-![](figures/CSI发送-接收接口.png)
+**图 1** CSI发送、接收接口
+
+![CSI发送、接收接口](figures/CSI发送-接收接口.png)
MIPI CSI标准分为应用层、协议层与物理层,协议层又细分为像素字节转换层、低级协议层、Lane管理层。
- 物理层(PHY Layer)
- PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。
+ PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。
- 协议层(Protocol Layer)
- 协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。
-
- - 像素字节转换层(Pixel/Byte Packing/Unpacking Layer)
+ 协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。
- CSI-2规范支持多种不同像素格式的图像应用。在发送方中,本层在发送数据到Low Level Protocol层之前,将来自应用层的像素封包为字节数据。在接收方中,本层在发送数据到应用层之前,将来自Low Level Protocol层的字节数据解包为像素。8位的像素数据在本层中传输时保持不变。
+ - 像素字节转换层(Pixel/Byte Packing/Unpacking Layer)
- - 低级协议层(Low Level Protocol)
+ CSI-2规范支持多种不同像素格式的图像应用。在发送方中,本层在发送数据到Low Level Protocol层之前,将来自应用层的像素封包为字节数据。在接收方中,本层在发送数据到应用层之前,将来自Low Level Protocol层的字节数据解包为像素。8位的像素数据在本层中传输时保持不变。
- LLP主要包含了在SoT和EoT事件之间的bit和byte级别的同步方法,以及和下一层传递数据的方法。LLP最小数据粒度是1个字节。LLP也包含了一个字节内的bit值解析,即Endian(大小端里的Endian的意思)的处理。
+ - 低级协议层(Low Level Protocol)
+ LLP主要包含了在SoT和EoT事件之间的bit和byte级别的同步方法,以及和下一层传递数据的方法。LLP最小数据粒度是1个字节。LLP也包含了一个字节内的bit值解析,即Endian(大小端里的Endian的意思)的处理。
- - Lane管理层(Lane Management)
+ - Lane管理层(Lane Management)
- CSI-2的Lane是可扩展的。具体的数据Lane的数量规范并没有给出限制,具体根据应用的带宽需求而定。发送侧分发(distributor功能)来自出口方向数据流的字节到1条或多条Lane上。接收侧则从一条或多条Lane中收集字节并合并(merge功能)到一个数据流上,复原出原始流的字节顺序。对于C-PHY物理层来说,本层专门分发字节对(16 bits)到数据Lane或从数据Lane中收集字节对。基于每Lane的扰码功能是可选特性。
+ CSI-2的Lane是可扩展的。具体的数据Lane的数量规范并没有给出限制,具体根据应用的带宽需求而定。发送侧分发(distributor功能)来自出口方向数据流的字节到1条或多条Lane上。接收侧则从一条或多条Lane中收集字节并合并(merge功能)到一个数据流上,复原出原始流的字节顺序。对于C-PHY物理层来说,本层专门分发字节对(16 bits)到数据Lane或从数据Lane中收集字节对。基于每Lane的扰码功能是可选特性。
- 协议层的数据组织形式是包(packet)。接口的发送侧会增加包头(header)和错误校验(error-checking)信息到即将被LLP发送的数据上。接收侧在LLP将包头剥掉,包头会被接收器中对应的逻辑所解析。错误校验信息可以用来做入口数据的完整性检查。
+ 协议层的数据组织形式是包(packet)。接口的发送侧会增加包头(header)和错误校验(error-checking)信息到即将被LLP发送的数据上。接收侧在LLP将包头剥掉,包头会被接收器中对应的逻辑所解析。错误校验信息可以用来做入口数据的完整性检查。
- 应用层(Application Layer)
- 本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。
+ 本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。
### 运作机制
@@ -47,9 +47,9 @@ MIPI CSI模块各分层的作用为:接口层提供打开设备、写入数据
![](../public_sys-resources/icon-note.gif) **说明:**
核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
-**图2**CSI无服务模式结构图
+**图 2** CSI无服务模式结构图
-![image](figures/无服务模式结构图.png "CSI无服务模式结构图")
+![CSI无服务模式结构图](figures/无服务模式结构图.png)
### 约束与限制
@@ -65,31 +65,31 @@ MIPI CSI主要用于连接摄像头组件。
MIPI CSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/mipi_csi_if.h。
-**表1** ComboDevAttr结构体介绍
+**表 1** ComboDevAttr结构体介绍
-| 名称 | 描述 |
+| 名称 | 描述 |
| --------- | ----------------------------------------------------- |
-| devno | 设备号 |
-| inputMode | 输入模式:MIPI/LVDS/SUBSLVDS/HISPI/DC |
-| dataRate | Mipi Rx,SLVS输入速率 |
-| imgRect | MIPI Rx设备裁剪区域(与原始传感器输入图像大小相对应) |
-| MIPIAttr | Mipi设备属性 |
-| lvdsAttr | LVDS/SubLVDS/HiSPi设备属性 |
+| devno | 设备号 |
+| inputMode | 输入模式:MIPI/LVDS/SUBSLVDS/HISPI/DC |
+| dataRate | Mipi Rx,SLVS输入速率 |
+| imgRect | MIPI Rx设备裁剪区域(与原始传感器输入图像大小相对应) |
+| MIPIAttr | Mipi设备属性 |
+| lvdsAttr | LVDS/SubLVDS/HiSPi设备属性 |
-**表2** ExtDataType结构体介绍
+**表 2** ExtDataType结构体介绍
-| 名称 | 描述 |
+| 名称 | 描述 |
| --------------- | ------------------------------- |
-| devno | 设备号 |
-| num | Sensor号 |
-| extDataBitWidth | 图片的位深 |
-| extDataType | 定义YUV和原始数据格式以及位深度 |
+| devno | 设备号 |
+| num | Sensor号 |
+| extDataBitWidth | 图片的位深 |
+| extDataType | 定义YUV和原始数据格式以及位深度 |
-**表3** MIPI CSI API接口功能介绍
+**表 3** MIPI CSI API接口功能介绍
@@ -110,17 +110,15 @@ MIPI CSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_
| int32_t MipiCsiEnableSensorClock(DevHandle handle, uint8_t snsClkSource) | 使能MIPI上的Sensor时钟 |
| int32_t MipiCsiDisableSensorClock(DevHandle handle, uint8_t snsClkSource) | 关闭Sensor的时钟 |
-
## 开发步骤
#### 使用流程
使用MIPI CSI的一般流程如图3所示。
-**图3** MIPI CSI使用流程图
-
+**图 3** MIPI CSI使用流程图
-![](figures/MIPI-CSI使用流程图.png)
+![MIPI CSI使用流程图](figures/MIPI-CSI使用流程图.png)
#### 获取MIPI CSI控制器操作句柄
@@ -130,427 +128,427 @@ MIPI CSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_
DevHandle MipiCsiOpen(uint8_t id);
```
-**表4** MipiCsiOpen的参数和返回值描述
+**表 4** MipiCsiOpen的参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ---------- | ----------------------------------------------- |
-| id | MIPI CSI通道ID |
-| **返回值** | **返回值描述** |
-| NULL | 获取失败 |
-| 设备句柄 | 获取到指令通道的控制器操作句柄,类型为DevHandle |
+| id | uint8_t类型,MIPI CSI通道ID |
+| **返回值** | **返回值描述** |
+| NULL | 获取失败 |
+| 设备句柄 | 获取到指令通道的控制器操作句柄,类型为DevHandle |
假设系统中的MIPI CSI通道为0,获取该通道控制器操作句柄的示例如下:
```c
-DevHandle MipiCsiHandle = NULL; /* 设备句柄 */
-id = 0; /* MIPI CSI通道ID */
+DevHandle MipiCsiHandle = NULL; // 设备句柄
+id = 0; // MIPI CSI通道ID
-/* 获取控制器操作句柄 */
+// 获取控制器操作句柄
MipiCsiHandle = MipiCsiOpen(id);
if (MipiCsiHandle == NULL) {
- HDF_LOGE("MipiCsiOpen: failed\n");
- return;
+ HDF_LOGE("MipiCsiOpen: mipi csi open fail.\n");
+ return NULL;
}
```
#### 进行MIPI CSI相应配置
-- 写入MIPI CSI配置
+- 写入MIPI CSI配置
```c
int32_t MipiCsiSetComboDevAttr(DevHandle handle, ComboDevAttr *pAttr);
```
- **表5** MipiCsiSetComboDevAttr的参数和返回值描述
+ **表 5** MipiCsiSetComboDevAttr的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ---------- | -------------------------- |
- | handle | 控制器操作句柄 |
- | pAttr | MIPI CSI相应配置结构体指针 |
- | **返回值** | **返回值描述** |
- | 0 | 设置成功 |
- | 负数 | 设置失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | pAttr | 结构体指针,MIPI CSI相应配置 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 设置成功 |
+ | 负数 | 设置失败 |
```c
int32_t ret;
struct ComboDevAttr attr;
- /* 当前配置如下 */
+ // 当前配置如下
(void)memset_s(&attr, sizeof(ComboDevAttr), 0, sizeof(ComboDevAttr));
- attr.devno = 0; /* 设备0 */
- attr.inputMode = INPUT_MODE_MIPI; /* 输入模式为MIPI */
- attr.dataRate = MIPI_DATA_RATE_X1; /* 每时钟输出1像素 */
- attr.imgRect.x = 0; /* 0: 图像传感器左上位置 */
- attr.imgRect.y = 0; /* 0: 图像传感器右上位置 */
- attr.imgRect.width = 2592; /* 2592: 图像传感器宽度大小 */
- attr.imgRect.height = 1944; /* 1944: 图像传感器高度尺寸 */
- /* 写入配置数据 */
+ attr.devno = 0; // 设备0
+ attr.inputMode = INPUT_MODE_MIPI; // 输入模式为MIPI
+ attr.dataRate = MIPI_DATA_RATE_X1; // 每时钟输出1像素
+ attr.imgRect.x = 0; // 0: 图像传感器左上位置
+ attr.imgRect.y = 0; // 0: 图像传感器右上位置
+ attr.imgRect.width = 2592; // 2592: 图像传感器宽度大小
+ attr.imgRect.height = 1944; // 1944: 图像传感器高度尺寸
+ // 写入配置数据
ret = MipiCsiSetComboDevAttr(MipiCsiHandle, &attr);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiSetComboDevAttr fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiSetComboDevAttr: mipi csi set combo dev attr fail, ret=%d\n", ret);
+ return ret;
}
```
-- 设置YUV和RAW数据格式和位深
+- 设置YUV和RAW数据格式和位深
```c
int32_t MipiCsiSetExtDataType(DevHandle handle, ExtDataType* dataType);
```
- **表6** MipiCsiSetExtDataType的参数和返回值描述
+ **表 6** MipiCsiSetExtDataType的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ---------- | ------------------------------- |
- | handle | 控制器操作句柄 |
- | dataType | 定义YUV和原始数据格式以及位深度 |
- | **返回值** | **返回值描述** |
- | 0 | 设置成功 |
- | 负数 | 设置失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | dataType | 结构体指针,定义YUV和原始数据格式以及位深度 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 设置成功 |
+ | 负数 | 设置失败 |
```c
int32_t ret;
struct ExtDataType dataType;
- /* 配置YUV和RAW数据格式和位深参数 */
- dataType.devno = 0; /* 设备0 */
- dataType.num = 0; /* Sensor 0 */
- dataType.extDataBitWidth[0] = 12; /* 位深数组元素0 */
- dataType.extDataBitWidth[1] = 12; /* 位深数组元素1 */
- dataType.extDataBitWidth[2] = 12; /* 位深数组元素2 */
-
- dataType.extDataType[0] = 0x39; /* 定义YUV和原始数据格式以及位深度元素0 */
- dataType.extDataType[1] = 0x39; /* 定义YUV和原始数据格式以及位深度元素1 */
- dataType.extDataType[2] = 0x39; /* 定义YUV和原始数据格式以及位深度元素2 */
- /* 设置YUV和RAW数据格式和位深 */
+ // 配置YUV和RAW数据格式和位深参数
+ dataType.devno = 0; // 设备0
+ dataType.num = 0; // Sensor 0
+ dataType.extDataBitWidth[0] = 12; // 位深数组元素0
+ dataType.extDataBitWidth[1] = 12; // 位深数组元素1
+ dataType.extDataBitWidth[2] = 12; // 位深数组元素2
+
+ dataType.extDataType[0] = 0x39; // 定义YUV和原始数据格式以及位深度元素0
+ dataType.extDataType[1] = 0x39; // 定义YUV和原始数据格式以及位深度元素1
+ dataType.extDataType[2] = 0x39; // 定义YUV和原始数据格式以及位深度元素2
+ // 设置YUV和RAW数据格式和位深
ret = MipiCsiSetExtDataType(MipiCsiHandle, &dataType);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiSetExtDataType fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiSetExtDataType: mipi csi set ext data type fail, ret=%d\n", ret);
+ return ret;
}
```
-- 设置MIPI RX的Lane分布
+- 设置MIPI RX的Lane分布
```c
int32_t MipiCsiSetHsMode(DevHandle handle, LaneDivideMode laneDivideMode);
```
- **表7** MipiCsiSetHsMode的参数和返回值描述
+ **表 7** MipiCsiSetHsMode的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| -------------- | -------------- |
- | handle | 控制器操作句柄 |
- | laneDivideMode | Lane模式参数 |
- | **返回值** | **返回值描述** |
- | 0 | 设置成功 |
- | 负数 | 设置失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | laneDivideMode | 结构体类型,Lane模式参数 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 设置成功 |
+ | 负数 | 设置失败 |
```c
int32_t ret;
enum LaneDivideMode mode;
- /* Lane模式参数为0 */
+ // Lane模式参数为0
mode = LANE_DIVIDE_MODE_0;
- /* 设置MIPI RX的 Lane分布 */
+ // 设置MIPI RX的 Lane分布
ret = MipiCsiSetHsMode(MipiCsiHandle, mode);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiSetHsMode fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiSetHsMode: mipi csi set hs mode fail, ret=%d\n", ret);
+ return ret;
}
```
-- 设置共模电压模式
+- 设置共模电压模式
```c
int32_t MipiCsiSetPhyCmvmode(DevHandle handle, uint8_t devno, PhyCmvMode cmvMode);
```
- **表8** MipiCsiSetPhyCmvmode的参数和返回值描述
+ **表 8** MipiCsiSetPhyCmvmode的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ---------- | ---------------- |
- | handle | 控制器操作句柄 |
- | cmvMode | 共模电压模式参数 |
- | devno | 设备编号 |
- | **返回值** | **返回值描述** |
- | 0 | 设置成功 |
- | 负数 | 设置失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | cmvMode | 结构体类型,共模电压模式参数 |
+ | devno | uint8_t类型,设备编号 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 设置成功 |
+ | 负数 | 设置失败 |
```c
int32_t ret;
enum PhyCmvMode mode;
uint8_t devno;
- /* 共模电压模式参数为0 */
+ // 共模电压模式参数为0
mode = PHY_CMV_GE1200MV;
- /* 设备编号为0 */
+ // 设备编号为0
devno = 0;
- /* 设置共模电压模式 */
+ // 设置共模电压模式
ret = MipiCsiSetPhyCmvmode(MipiCsiHandle, devno, mode);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiSetPhyCmvmode fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiSetPhyCmvmode: mipi csi set phy cmv mode fail, ret=%d\n", ret);
+ return ret;
}
```
#### 复位/撤销复位Sensor
-- 复位Sensor
+- 复位Sensor
```c
int32_t MipiCsiResetSensor(DevHandle handle, uint8_t snsResetSource);
```
- **表9** MipiCsiResetSensor的参数和返回值描述
+ **表 9** MipiCsiResetSensor的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| -------------- | ------------------------------------------------ |
- | handle | 控制器操作句柄 |
- | snsResetSource | 传感器的复位信号线号,在软件中称为传感器的复位源 |
- | **返回值** | **返回值描述** |
- | 0 | 复位成功 |
- | 负数 | 复位失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | snsResetSource | uint8_t类型,传感器的复位信号线号,在软件中称为传感器的复位源 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 复位成功 |
+ | 负数 | 复位失败 |
```c
int32_t ret;
uint8_t snsResetSource;
- /* 传感器复位信号线号为0 */
+ // 传感器复位信号线号为0
snsResetSource = 0;
- /* 复位Sensor */
+ // 复位Sensor
ret = MipiCsiResetSensor(MipiCsiHandle, snsResetSource);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiResetSensor fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiResetSensor: mipi csi reset sensor fail, ret=%d\n", ret);
+ return ret;
}
```
-- 撤销复位Sensor
+- 撤销复位Sensor
```c
int32_t MipiCsiUnresetSensor(DevHandle handle, uint8_t snsResetSource);
```
- **表10** MipiCsiUnresetSensor的参数和返回值描述
+ **表 10** MipiCsiUnresetSensor的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| -------------- | ------------------------------------------------ |
- | handle | 控制器操作句柄 |
- | snsResetSource | 传感器的复位信号线号,在软件中称为传感器的复位源 |
- | **返回值** | **返回值描述** |
- | 0 | 撤销复位成功 |
- | 负数 | 撤销复位失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | snsResetSource | uint8_t类型,传感器的复位信号线号,在软件中称为传感器的复位源 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 撤销复位成功 |
+ | 负数 | 撤销复位失败 |
```c
int32_t ret;
uint8_t snsResetSource;
- /* 传感器撤销复位信号线号为0 */
+ // 传感器撤销复位信号线号为0
snsResetSource = 0;
- /* 撤销复位Sensor */
+ // 撤销复位Sensor
ret = MipiCsiUnresetSensor(MipiCsiHandle, snsResetSource);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiUnresetSensor fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiUnresetSensor: mipi csi unreset sensor fail, ret=%d\n", ret);
+ return ret;
}
```
#### 复位/撤销复位MIPI RX
-- 复位MIPI RX
+- 复位MIPI RX
```c
int32_t MipiCsiResetRx(DevHandle handle, uint8_t comboDev);
```
- **表11** MipiCsiResetRx的参数和返回值描述
+ **表 11** MipiCsiResetRx的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ---------- | --------------------- |
- | handle | 控制器操作句柄 |
- | comboDev | MIPI RX或LVDS通路序号 |
- | **返回值** | **返回值描述** |
- | 0 | 复位成功 |
- | 负数 | 复位失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | comboDev | uint8_t类型,MIPI RX或LVDS通路序号 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 复位成功 |
+ | 负数 | 复位失败 |
```c
int32_t ret;
uint8_t comboDev;
- /* 通路序号为0 */
+ // 通路序号为0
comboDev = 0;
- /* 复位MIPI RX */
+ // 复位MIPI RX
ret = MipiCsiResetRx(MipiCsiHandle, comboDev);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiResetRx fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiResetRx: mipi csi reset rx fail, ret=%d\n", ret);
+ return ret;
}
```
-- 撤销复位MIPI RX
+- 撤销复位MIPI RX
```c
int32_t MipiCsiUnresetRx(DevHandle handle, uint8_t comboDev);
```
- **表12** MipiCsiUnresetRx的参数和返回值描述
+ **表 12** MipiCsiUnresetRx的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ---------- | --------------------- |
- | handle | 控制器操作句柄 |
- | comboDev | MIPI RX或LVDS通路序号 |
- | **返回值** | **返回值描述** |
- | 0 | 撤销复位成功 |
- | 负数 | 撤销复位失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | comboDev | uint8_t类型,MIPI RX或LVDS通路序号 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 撤销复位成功 |
+ | 负数 | 撤销复位失败 |
```c
int32_t ret;
uint8_t comboDev;
- /* 通路序号为0 */
+ // 通路序号为0
comboDev = 0;
- /* 撤销复位MIPI RX */
+ // 撤销复位MIPI RX
ret = MipiCsiUnresetRx(MipiCsiHandle, comboDev);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiUnresetRx fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiUnresetRx: mipi csi unreset rx fail, ret=%d\n", ret);
+ return ret;
}
```
#### 使能/关闭MIPI的时钟
-- 使能MIPI的时钟
+- 使能MIPI的时钟
```c
int32_t MipiCsiEnableClock(DevHandle handle, uint8_t comboDev);
```
- **表13** MipiCsiEnableClock的参数和返回值描述
+ **表 13** MipiCsiEnableClock的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ---------- | -------------- |
- | handle | 控制器操作句柄 |
- | comboDev | 通路序号 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | comboDev | uint8_t类型,通路序号 |
| **返回值** | **返回值描述** |
- | 0 | 使能成功 |
- | 负数 | 使能失败 |
+ | HDF_SUCCESS | 使能成功 |
+ | 负数 | 使能失败 |
```c
int32_t ret;
uint8_t comboDev;
- /* 通路序号为0 */
+ // 通路序号为0
comboDev = 0;
- /* 使能MIPI的时钟 */
+ // 使能MIPI的时钟
ret = MipiCsiEnableClock(MipiCsiHandle, comboDev);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiEnableClock fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiEnableClock: mipi csi enable clock fail, ret=%d\n", ret);
+ return ret;
}
```
-- 关闭MIPI的时钟
+- 关闭MIPI的时钟
```c
int32_t MipiCsiDisableClock(DevHandle handle, uint8_t comboDev);
```
- **表14** MipiCsiDisableClock的参数和返回值描述
+ **表 14** MipiCsiDisableClock的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ---------- | -------------- |
- | handle | 控制器操作句柄 |
- | comboDev | 通路序号 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | comboDev | uint8_t类型,通路序号 |
| **返回值** | **返回值描述** |
- | 0 | 关闭成功 |
- | 负数 | 关闭失败 |
+ | HDF_SUCCESS | 关闭成功 |
+ | 负数 | 关闭失败 |
```c
int32_t ret;
uint8_t comboDev;
- /* 通路序号为0 */
+ // 通路序号为0
comboDev = 0;
- /* 关闭MIPI的时钟 */
+ // 关闭MIPI的时钟
ret = MipiCsiDisableClock(MipiCsiHandle, comboDev);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiDisableClock fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiDisableClock: mipi csi disable clock fail, ret=%d\n", ret);
+ return ret;
}
```
#### 使能/关闭MIPI上的Sensor时钟
-- 使能MIPI上的Sensor时钟
+- 使能MIPI上的Sensor时钟
```c
int32_t MipiCsiEnableSensorClock(DevHandle handle, uint8_t snsClkSource);
```
- **表15** MipiCsiEnableSensorClock的参数和返回值描述
+ **表 15** MipiCsiEnableSensorClock的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ------------ | ------------------------------------------------ |
- | handle | 控制器操作句柄 |
- | snsClkSource | 传感器的时钟信号线号,在软件中称为传感器的时钟源 |
- | **返回值** | **返回值描述** |
- | 0 | 使能成功 |
- | 负数 | 使能失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | snsClkSource | uint8_t类型,传感器的时钟信号线号,在软件中称为传感器的时钟源 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 使能成功 |
+ | 负数 | 使能失败 |
```c
int32_t ret;
uint8_t snsClkSource;
- /* 传感器的时钟信号线号为0 */
+ // 传感器的时钟信号线号为0
snsClkSource = 0;
- /* 使能MIPI上的Sensor时钟 */
+ // 使能MIPI上的Sensor时钟
ret = MipiCsiEnableSensorClock(MipiCsiHandle, snsClkSource);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiEnableSensorClock fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiEnableSensorClock: mipi csi enable sensor clock fail, ret=%d\n", ret);
+ return ret;
}
```
-- 关闭MIPI上的Sensor时钟
+- 关闭MIPI上的Sensor时钟
```c
int32_t MipiCsiDisableSensorClock(DevHandle handle, uint8_t snsClkSource);
```
- **表16** MipiCsiDisableSensorClock的参数和返回值描述
+ **表 16** MipiCsiDisableSensorClock的参数和返回值描述
- | 参数 | 参数描述 |
+ | 参数 | 参数描述 |
| ------------ | ------------------------------------------------ |
- | handle | 控制器操作句柄 |
- | snsClkSource | 传感器的时钟信号线号,在软件中称为传感器的时钟源 |
- | **返回值** | **返回值描述** |
- | 0 | 关闭成功 |
- | 负数 | 关闭失败 |
+ | handle | DevHandle类型,控制器操作句柄 |
+ | snsClkSource | uint8_t类型,传感器的时钟信号线号,在软件中称为传感器的时钟源 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 关闭成功 |
+ | 负数 | 关闭失败 |
```c
int32_t ret;
uint8_t snsClkSource;
- /* 传感器的时钟信号线号为0 */
+ // 传感器的时钟信号线号为0
snsClkSource = 0;
- /* 关闭MIPI上的Sensor时钟 */
+ // 关闭MIPI上的Sensor时钟
ret = MipiCsiDisableSensorClock(MipiCsiHandle, snsClkSource);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiDisableSensorClock fail! ret=%d\n", __func__, ret);
- return -1;
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiCsiDisableSensorClock: mipi csi disable sensor clock fail, ret=%d\n", ret);
+ return ret;
}
```
@@ -564,16 +562,16 @@ void MipiCsiClose(DevHandle handle);
该函数会释放掉由MipiCsiOpen申请的资源。
-**表17** MipiCsiClose的参数和返回值描述
+**表 17** MipiCsiClose的参数和返回值描述
-| 参数 | 参数描述 |
+| 参数 | 参数描述 |
| ------------ | ------------------------------------------------ |
-| handle | MIPI CSI控制器操作句柄 |
+| handle | DevHandle类型,MIPI CSI控制器操作句柄 |
```c
-MipiCsiClose(MIPIHandle); /* 释放掉MIPI CSI控制器操作句柄 */
+MipiCsiClose(MIPIHandle); // 释放掉MIPI CSI控制器操作句柄
```
## 使用实例
@@ -583,133 +581,257 @@ MipiCsiClose(MIPIHandle); /* 释放掉MIPI CSI控制器操作句柄 */
MIPI CSI完整的使用示例如下所示:
```c
-#include "hdf.h"
-#include "MIPI_csi_if.h"
+#include "hdf_log.h"
+#include "mipi_csi_if.h"
+#include "securec.h"
+
+enum InterfaceType {
+ INTERFACE_MIPI = 0,
+ INTERFACE_LVDS,
+ INTERFACE_CMOS,
+ INTERFACE_BUTT
+};
+
+static void InitMipiDevAttr(MipiDevAttr *mipiAttr)
+{
+ MipiDevAttr attr;
+ if (mipiAttr == NULL) {
+ return;
+ }
+
+ HDF_LOGI("InitMipiDevAttr: enter.");
+ (void)memset_s(&attr, sizeof(MipiDevAttr), 0, sizeof(MipiDevAttr));
+ attr.inputDataType = DATA_TYPE_RAW_12BIT;
+ attr.wdrMode = HI_MIPI_WDR_MODE_NONE;
+ // laneId: -1 - disable
+ attr.laneId[0] = 0; // 0 -- laneId 0
+ attr.laneId[1] = 1; // 1 -- laneId 1
+ attr.laneId[2] = 2; // 2 -- laneId 2
+ attr.laneId[3] = 3; // 3 -- laneId 3
+
+ // Used by the HI_MIPI_WDR_MODE_DT, This is not fully tested!
+ if (attr.wdrMode == HI_MIPI_WDR_MODE_DT) {
+ attr.dataType[0] = 0x39; // 0x39 -- data type reserved
+ attr.dataType[1] = 0x39; // 0x39 -- data type reserved
+ attr.dataType[2] = 0x39; // 0x39 -- data type reserved
+ attr.dataType[3] = 0x39; // 0x39 -- data type reserved
+ }
+
+ *mipiAttr = attr;
+}
+
+static int MipiGetIntputModeType(InputMode inputMode)
+{
+ switch (inputMode) {
+ case INPUT_MODE_SUBLVDS:
+ case INPUT_MODE_LVDS:
+ case INPUT_MODE_HISPI:
+ return INTERFACE_LVDS;
+ case INPUT_MODE_MIPI:
+ return INTERFACE_MIPI;
+ case INPUT_MODE_CMOS:
+ case INPUT_MODE_BT1120:
+ case INPUT_MODE_BT656:
+ case INPUT_MODE_BYPASS:
+ return INTERFACE_CMOS;
+ default:
+ break;
+ }
-void PalMipiCsiTestSample(void)
+ return INTERFACE_BUTT;
+}
+
+static void InitLvdsDevAttr(LvdsDevAttr *lvdsAttr)
+{
+ int i;
+ int j;
+ int k;
+ LvdsDevAttr attr;
+
+ if (lvdsAttr == NULL) {
+ return;
+ }
+
+ (void)memset_s(&attr, sizeof(LvdsDevAttr), 0, sizeof(LvdsDevAttr));
+ attr.inputDataType = DATA_TYPE_RAW_12BIT;
+ attr.wdrMode = HI_WDR_MODE_NONE;
+ // LVDS synchronization mode. LVDS_SYNC_MODE_SOF, LVDS_SYNC_MODE_SAV
+ attr.syncMode = LVDS_SYNC_MODE_SOF;
+ // LVDS Vsync type. LVDS_VSYNC_NORMAL, LVDS_VSYNC_SHARE, LVDS_VSYNC_HCONNECT
+ attr.vsyncAttr.syncType = LVDS_VSYNC_NORMAL;
+ // hconnect vsync blanking len, valid when the syncType is LVDS_VSYNC_HCONNECT
+ // This is not fully tested!
+ if (attr.vsyncAttr.syncType == LVDS_VSYNC_HCONNECT) {
+ attr.vsyncAttr.hblank1 = 0;
+ attr.vsyncAttr.hblank2 = 0;
+ }
+ // frame identification code: LVDS_FID_NONE, LVDS_FID_IN_SAV, LVDS_FID_IN_DATA
+ attr.fidAttr.fidType = LVDS_FID_NONE;
+ // Sony DOL has the Frame Information Line, in DOL H-Connection mode, should
+ // configure this flag as false to disable output the Frame Information Line.
+ // This is not fully tested!
+ attr.fidAttr.outputFil = 'm';
+ // LVDS bit size end mode: LVDS_ENDIAN_LITTLE, LVDS_ENDIAN_BIG
+ attr.dataEndian = LVDS_ENDIAN_LITTLE;
+ // sync code endian: little/big, LVDS_ENDIAN_LITTLE, LVDS_ENDIAN_BIG
+ attr.syncCodeEndian = LVDS_ENDIAN_LITTLE;
+ // laneId: -1 - disable
+ attr.laneId[0] = 0; // 0 -- laneId 0
+ attr.laneId[1] = 1; // 1 -- laneId 1
+ attr.laneId[2] = 2; // 2 -- laneId 2
+ attr.laneId[3] = 3; // 3 -- laneId 3
+
+ /* each vc has 4 params, syncCode[i]:
+ syncMode is SYNC_MODE_SOF: SOF, EOF, SOL, EOL
+ syncMode is SYNC_MODE_SAV: invalid sav, invalid eav, valid sav, valid eav
+ This is not fully tested! */
+ for (i = 0; i < LVDS_LANE_NUM; i++) {
+ for (j = 0; j < WDR_VC_NUM; j++) {
+ for (k = 0; k < SYNC_CODE_NUM; k++) {
+ attr.syncCode[i][j][k] = 0; // 0 -- frame0 sof
+ }
+ }
+ }
+
+ *lvdsAttr = attr;
+}
+
+static int32_t PalMipiCsiTestSample(void)
{
uint8_t id;
int32_t ret;
uint8_t comboDev;
uint8_t snsClkSource;
+ uint8_t snsResetSource;
uint8_t devno;
- enum LaneDivideMode mode;
- enum PhyCmvMode mode;
- struct ComboDevAttr attr;
- struct ExtDataType dataType;
+ LaneDivideMode laneMode;
+ PhyCmvMode CmvMode;
+ ComboDevAttr attr;
DevHandle MipiCsiHandle = NULL;
+ enum InterfaceType interType;
- /* 控制器ID号 */
+ // 控制器ID号
id = 0;
- /* 获取控制器操作句柄 */
+ // 获取控制器操作句柄
MipiCsiHandle = MipiCsiOpen(id);
if (MipiCsiHandle == NULL) {
- HDF_LOGE("MipiCsiOpen: failed!\n");
- return;
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi open fail!\n");
+ return HDF_FAILURE;
}
-
- /* Lane模式参数为0 */
- mode = LANE_DIVIDE_MODE_0;
- /* 设置MIPI RX的Lane分布 */
- ret = MipiCsiSetHsMode(MipiCsiHandle, mode);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiSetHsMode fail! ret=%d\n", __func__, ret);
- return;
+
+ // Lane模式参数为0
+ laneMode = LANE_DIVIDE_MODE_0;
+ // 设置MIPI RX的Lane分布
+ ret = MipiCsiSetHsMode(MipiCsiHandle, laneMode);
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi set hs mode fail, ret=%d\n", ret);
+ return ret;
}
- /* 通路序号为0 */
+ // 通路序号为0
comboDev = 0;
- /* 使能MIPI的时钟 */
+ // 使能MIPI的时钟
ret = MipiCsiEnableClock(MipiCsiHandle, comboDev);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiEnableClock fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi enable clock fail, ret=%d\n", ret);
+ return ret;
}
- /* 复位MIPI RX */
+ // 复位MIPI RX
ret = MipiCsiResetRx(MipiCsiHandle, comboDev);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiResetRx fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi reset rx fail, ret=%d\n", ret);
+ return ret;
}
- /* 传感器的时钟信号线号为0 */
+ // 传感器的时钟信号线号为0
snsClkSource = 0;
- /* 使能MIPI上的Sensor时钟 */
+ // 使能MIPI上的Sensor时钟
ret = MipiCsiEnableSensorClock(MipiCsiHandle, snsClkSource);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiEnableSensorClock fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi enable sensor clock fail, ret=%d\n", ret);
+ return ret;
}
-
- /* 复位Sensor */
+ snsResetSource = 0;
+ // 复位Sensor
ret = MipiCsiResetSensor(MipiCsiHandle, snsResetSource);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiResetSensor fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi reset sensor fail, ret=%d\n", ret);
+ return ret;
}
- /* MIPI参数配置如下 */
+ // MIPI参数配置如下
(void)memset_s(&attr, sizeof(ComboDevAttr), 0, sizeof(ComboDevAttr));
- attr.devno = 0; /* 设备0 */
- attr.inputMode = INPUT_MODE_MIPI; /* 输入模式为MIPI */
- attr.dataRate = MIPI_DATA_RATE_X1; /* 每时钟输出1像素 */
- attr.imgRect.x = 0; /* 0: 图像传感器左上位置 */
- attr.imgRect.y = 0; /* 0: 图像传感器右上位置 */
- attr.imgRect.width = 2592; /* 2592: 图像传感器宽度大小 */
- attr.imgRect.height = 1944; /* 1944: 图像传感器高度尺寸 */
- /* 写入配置数据 */
+ attr.devno = 0; // 设备0
+ attr.inputMode = INPUT_MODE_MIPI; // 输入模式为MIPI
+ attr.dataRate = MIPI_DATA_RATE_X1; // 每时钟输出1像素
+ attr.imgRect.x = 0; // 0: 图像传感器左上位置
+ attr.imgRect.y = 0; // 0: 图像传感器右上位置
+ attr.imgRect.width = 2592; // 2592: 图像传感器宽度大小
+ attr.imgRect.height = 1944; // 1944: 图像传感器高度尺寸
+ interType = MipiGetIntputModeType(attr.inputMode);
+ if (interType == INTERFACE_MIPI) {
+ HDF_LOGI("PalMipiCsiTestSample: call[InitMipiDevAttr].");
+ InitMipiDevAttr(&attr.mipiAttr);
+ } else if (interType == INTERFACE_LVDS) {
+ HDF_LOGI("PalMipiCsiTestSample: call[InitLvdsDevAttr].");
+ InitLvdsDevAttr(&attr.lvdsAttr);
+ } else {
+ HDF_LOGE("PalMipiCsiTestSample: interType = %d is error!", attr.inputMode);
+ }
+ // 写入配置数据
ret = MipiCsiSetComboDevAttr(MipiCsiHandle, &attr);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiSetComboDevAttr fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi set combo devAttr fail, ret=%d\n", ret);
+ return ret;
}
- /* 共模电压模式参数为0 */
- mode = PHY_CMV_GE1200MV;
- /* 设备编号为0 */
+ // 共模电压模式参数为0
+ CmvMode = PHY_CMV_GE1200MV;
+ // 设备编号为0
devno = 0;
- /* 设置共模电压模式 */
- ret = MipiCsiSetPhyCmvmode(MipiCsiHandle, devno, mode);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiSetPhyCmvmode fail! ret=%d\n", __func__, ret);
- return;
+ // 设置共模电压模式
+ ret = MipiCsiSetPhyCmvmode(MipiCsiHandle, devno, CmvMode);
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi set phy cmv mode fail, ret=%d\n", ret);
+ return ret;
}
- /* 通路序号为0 */
+ // 通路序号为0
comboDev = 0;
- /* 撤销复位MIPI RX */
+ // 撤销复位MIPI RX
ret = MipiCsiUnresetRx(MipiCsiHandle, comboDev);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiUnresetRx fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi unreset rx fail, ret=%d\n", ret);
+ return ret;
}
- /* 关闭MIPI的时钟 */
+ // 关闭MIPI的时钟
ret = MipiCsiDisableClock(MipiCsiHandle, comboDev);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiDisableClock fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi disable clock fail, ret=%d\n", ret);
+ return ret;
}
- /* 传感器撤销复位信号线号为0 */
+ // 传感器撤销复位信号线号为0
snsResetSource = 0;
- /* 撤销复位Sensor */
+ // 撤销复位Sensor
ret = MipiCsiUnresetSensor(MipiCsiHandle, snsResetSource);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiUnresetSensor fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi unreset sensor fail, ret=%d\n", ret);
+ return ret;
}
- /* 关闭MIPI上的Sensor时钟 */
+ // 关闭MIPI上的Sensor时钟
ret = MipiCsiDisableSensorClock(MipiCsiHandle, snsClkSource);
- if (ret != 0) {
- HDF_LOGE("%s: MipiCsiDisableSensorClock fail! ret=%d\n", __func__, ret);
- return;
+ if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) {
+ HDF_LOGE("PalMipiCsiTestSample: mipi csi disable sensor clock fail, ret=%d\n", ret);
+ return ret;
}
-
- /* 释放MIPI DSI设备句柄 */
- MipiCsiClose(MipiCsiHandle);
+ HDF_LOGI("PalMipiCsiTestSample: function tests end.");
+ // 释放MIPI DSI设备句柄
+ MipiCsiClose(MipiCsiHandle);
+ return ret;
}
```
diff --git a/zh-cn/device-dev/driver/driver-platform-mipicsi-develop.md b/zh-cn/device-dev/driver/driver-platform-mipicsi-develop.md
index fd4c6675b305cb91851a01c5e38fc9618fd9f369..d5f90ae7940f3fd079f2d88f9798ed2b72f196aa 100755
--- a/zh-cn/device-dev/driver/driver-platform-mipicsi-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-mipicsi-develop.md
@@ -10,48 +10,51 @@ CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接
图1显示了简化的CSI接口。D-PHY采用1对源同步的差分时钟和1~4对差分数据线来进行数据传输。数据传输采用DDR方式,即在时钟的上下边沿都有数据传输。
- **图 1** CSI发送、接收接口
-![](figures/CSI发送-接收接口.png)
+**图 1** CSI发送、接收接口
+
+![CSI发送、接收接口](figures/CSI发送-接收接口.png)
MIPI CSI标准分为应用层、协议层与物理层,协议层又细分为像素字节转换层、低级协议层、Lane管理层。
- 物理层(PHY Layer)
- PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。
+ PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。
- 协议层(Protocol Layer)
- 协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。
+ 协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。
- - 像素字节转换层(Pixel/Byte Packing/Unpacking Layer)
+ - 像素字节转换层(Pixel/Byte Packing/Unpacking Layer)
CSI-2规范支持多种不同像素格式的图像应用。在发送方中,本层在发送数据到Low Level Protocol层之前,将来自应用层的像素封包为字节数据。在接收方中,本层在发送数据到应用层之前,将来自Low Level Protocol层的字节数据解包为像素。8位的像素数据在本层中传输时保持不变。
- - 低级协议层(Low Level Protocol)
-
+ - 低级协议层(Low Level Protocol)
LLP主要包含了在SoT和EoT事件之间的bit和byte级别的同步方法,以及和下一层传递数据的方法。LLP最小数据粒度是1个字节。LLP也包含了一个字节内的bit值解析,即Endian(大小端里的Endian的意思)的处理。
- - Lane管理层(Lane Management)
+ - Lane管理层(Lane Management)
CSI-2的Lane是可扩展的。具体的数据Lane的数量规范并没有给出限制,具体根据应用的带宽需求而定。发送侧分发(distributor功能)来自出口方向数据流的字节到1条或多条Lane上。接收侧则从一条或多条Lane中收集字节并合并(merge功能)到一个数据流上,复原出原始流的字节顺序。对于C-PHY物理层来说,本层专门分发字节对(16 bits)到数据Lane或从数据Lane中收集字节对。基于每Lane的扰码功能是可选特性。
-
协议层的数据组织形式是包(packet)。接口的发送侧会增加包头(header)和错误校验(error-checking)信息到即将被LLP发送的数据上。接收侧在LLP将包头剥掉,包头会被接收器中对应的逻辑所解析。错误校验信息可以用来做入口数据的完整性检查。
- 应用层(Application Layer)
- 本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。
+ 本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。
### 运作机制
MIPI CSI模块各分层的作用为:
- 接口层提供打开设备、写入数据和关闭设备的接口。
+
- 核心层主要提供绑定设备、初始化设备以及释放设备的能力。
+
- 适配层实现其它具体的功能。
![](../public_sys-resources/icon-note.gif) **说明:**
核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
-![image1](figures/无服务模式结构图.png)
+**图 2** CSI无服务模式结构图
+
+![CSI无服务模式结构图](figures/无服务模式结构图.png)
## 开发指导
@@ -81,306 +84,327 @@ struct MipiCsiCntlrMethod {
int32_t (*unresetSensor)(struct MipiCsiCntlr *cntlr, uint8_t snsResetSource);
};
```
-**表1** MipiCsiCntlrMethod成员的钩子函数功能说明
-| 成员函数 | 入参 | 出参 | 返回状态 | 功能 |
+
+**表 1** MipiCsiCntlrMethod成员的钩子函数功能说明
+| 成员函数 | 入参 | 出参 | 返回状态 | 功能 |
| ------------------ | ------------------------------------------------------------ | ---- | ------------------ | -------------------------- |
-| setComboDevAttr | **cntlr**:结构体指针,MipiCsi控制器 ;
**pAttr**:结构体指针,MIPI CSI相应配置结构体指针 | 无 | HDF_STATUS相关状态 | 写入MIPI CSI配置 |
-| setPhyCmvmode | **cntlr**:结构体指针,MipiCsi控制器 ;
**devno**:uint8_t,设备编号;
**cmvMode**:枚举类型,共模电压模式参数 | 无 | HDF_STATUS相关状态 | 设置共模电压模式 |
-| setExtDataType | **cntlr**:结构体指针,MipiCsi控制器 ;
**dataType**:结构体指针,定义YUV和原始数据格式以及位深度 | 无 | HDF_STATUS相关状态 | 设置YUV和RAW数据格式和位深 |
-| setHsMode | **cntlr**:结构体指针,MipiCsi控制器 ;
**laneDivideMode**:枚举类型,Lane模式参数 | 无 | HDF_STATUS相关状态 | 设置MIPI RX的Lane分布 |
-| enableClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 使能MIPI的时钟 |
-| disableClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 关闭MIPI的时钟 |
-| resetRx | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 复位MIPI RX |
-| unresetRx | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 撤销复位MIPI RX |
-| enableSensorClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 使能MIPI上的Sensor时钟 |
-| disableSensorClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 关闭MIPI上的Sensor时钟 |
-| resetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 复位Sensor |
-| unresetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 撤销复位Sensor |
+| setComboDevAttr | **cntlr**:结构体指针,MipiCsi控制器 ;
**pAttr**:结构体指针,MIPI CSI相应配置结构体指针 | 无 | HDF_STATUS相关状态 | 写入MIPI CSI配置 |
+| setPhyCmvmode | **cntlr**:结构体指针,MipiCsi控制器 ;
**devno**:uint8_t类型,设备编号;
**cmvMode**:枚举类型,共模电压模式参数 | 无 | HDF_STATUS相关状态 | 设置共模电压模式 |
+| setExtDataType | **cntlr**:结构体指针,MipiCsi控制器 ;
**dataType**:结构体指针,定义YUV和原始数据格式以及位深度 | 无 | HDF_STATUS相关状态 | 设置YUV和RAW数据格式和位深 |
+| setHsMode | **cntlr**:结构体指针,MipiCsi控制器 ;
**laneDivideMode**:枚举类型,Lane模式参数 | 无 | HDF_STATUS相关状态 | 设置MIPI RX的Lane分布 |
+| enableClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t类型,通路序号 | 无 | HDF_STATUS相关状态 | 使能MIPI的时钟 |
+| disableClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t类型,通路序号 | 无 | HDF_STATUS相关状态 | 关闭MIPI的时钟 |
+| resetRx | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t类型,通路序号 | 无 | HDF_STATUS相关状态 | 复位MIPI RX |
+| unresetRx | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t类型,通路序号 | 无 | HDF_STATUS相关状态 | 撤销复位MIPI RX |
+| enableSensorClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t类型,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 使能MIPI上的Sensor时钟 |
+| disableSensorClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t类型,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 关闭MIPI上的Sensor时钟 |
+| resetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t类型,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 复位Sensor |
+| unresetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t类型,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 撤销复位Sensor |
### 开发步骤
-MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动入口、以及实例化核心层接口函数。
+MIPI CSI模块适配包含以下四个步骤:
-1. 配置属性文件:
+1. 实例化驱动入口
- - 在device_info.hcs文件中添加deviceNode描述。
- - 【可选】添加mipicsi_config.hcs器件属性文件。
+ - 实例化HdfDriverEntry结构体成员。
-2. 实例化驱动入口:
-
- - 实例化HdfDriverEntry结构体成员。
- - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
-
-3. 实例化MIPI CSI控制器对象:
+ - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
+
+2. 配置属性文件
+
+ - 在device_info.hcs文件中添加deviceNode描述。
+
+ - 【可选】添加mipi_csi_config.hcs器件属性文件。
+
+3. 实例化MIPI CSI控制器对象
- - 初始化MipiCsiCntlr成员。
- - 实例化MipiCsiCntlr成员MipiCsiCntlrMethod。
- >![](../public_sys-resources/icon-note.gif) **说明:**
- >实例化MipiCsiCntlr成员MipiCsiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。
+ - 初始化MipiCsiCntlr成员。
+
+ - 实例化MipiCsiCntlr成员MipiCsiCntlrMethod。
+
+ >![](../public_sys-resources/icon-note.gif) **说明:**
+ >实例化MipiCsiCntlr成员MipiCsiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。
-4. 驱动调试:
+4. 驱动调试
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
### 开发实例
-下方将以mipi_rx_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。
+下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/mipi_csi/mipi_csi_hi35xx.c驱动为示例,展示需要厂商提供哪些内容来完整实现设备功能。
+
+1. 实例化驱动入口
+
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。
+
+ 一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+
+ MIPI CSI驱动入口参考
+
+ ```c
+ struct HdfDriverEntry g_mipiCsiDriverEntry = {
+ .moduleVersion = 1,
+ .Init = Hi35xxMipiCsiInit, // 挂接MIPI CSI模块Init实例化
+ .Release = Hi35xxMipiCsiRelease, // 挂接MIPI CSI模块Release实例化
+ .moduleName = "HDF_MIPI_RX", // 【必要且与HCS文件中里面的moduleName匹配】
+ };
+ HDF_INIT(g_mipiCsiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
+
+2. 配置属性文件
+ 一般来说,驱动开发首先需要新增mipi_csi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据。器件属性值与核心层MipiCsiCntlr成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。
+
+ >![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**
+ >本例中MIPI控制器配置属性在源文件中,没有新增配置文件,驱动适配者如有需要,可在device_info.hcs文件的deviceNode增加deviceMatchAttr字段,同时新增mipi_csi_config.hcs文件,并使其match_attr字段与之相同。
+
+ 无服务模式device_info.hcs文件中设备节点也代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,MIPI CSI控制器具体配置为0,表示驱动不需要发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。MIPI CSI控制器具体配置为160 |
+ | permission | 驱动创建设备节点权限,MIPI CSI控制器具体配置为0664 |
+ | moduleName | 驱动名称,MIPI CSI控制器固定为HDF_MIPI_RX |
+ | serviceName | 驱动对外发布服务的名称,MIPI CSI控制器服务名设置为HDF_MIPI_RX |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,MIPI CSI控制器没有使用,可忽略 |
+
+ device_info.hcs配置参考
+
+ ```c
+ root {
+ device_info {
+ match_attr = "hdf_manager";
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_mipi_csi:: device {
+ device0 :: deviceNode {
+ policy = 0;
+ priority = 160;
+ permission = 0644;
+ moduleName = "HDF_MIPI_RX"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
+ serviceName = "HDF_MIPI_RX"; // 【必要且唯一】驱动对外发布服务的名称
+ }
+ }
+ }
+ }
+ }
+ ```
+
+3. 实例化MIPI CSI控制器对象
+
+ 完成驱动入口注册之后,最后一步就是以核心层MipiCsiCntlr对象的初始化为核心,实现HdfDriverEntry成员函数(Bind,Init,Release)。
+
+ MipiCsiCntlr对象的初始化包括驱动适配者自定义结构体(用于传递参数和数据)和实例化MipiCsiCntlr成员MipiCsiCntlrMethod(让用户可以通过接口来调用驱动底层函数)。
+
+ - 自定义结构体参考
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,本例的mipicsi器件属性在源文件中,故基本成员结构与MipiCsiCntlr无太大差异。
+
+ ```c
+ typedef struct {
+ // 数据类型:8/10/12/14/16位
+ DataType inputDataType;
+ // MIPI波分复用模式
+ MipiWdrMode wdrMode;
+ // laneId: -1 - 禁用
+ short laneId[MIPI_LANE_NUM];
+
+ union {
+ // 用于 HI_MIPI_WDR_MODE_DT
+ short dataType[WDR_VC_NUM];
+ };
+ } MipiDevAttr;
+
+ typedef struct {
+ // 设备号
+ uint8_t devno;
+ // 输入模式: MIPI/LVDS/SUBLVDS/HISPI/DC
+ InputMode inputMode;
+ MipiDataRate dataRate;
+ // MIPI Rx设备裁剪区域(与原始传感器输入图像大小相对应)
+ ImgRect imgRect;
+
+ union {
+ MipiDevAttr mipiAttr;
+ LvdsDevAttr lvdsAttr;
+ };
+ } ComboDevAttr;
+
+ // MipiCsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct MipiCsiCntlr {
+ // 当驱动程序绑定到HDF框架时,将发送此控制器提供的服务。
+ struct IDeviceIoService service;
+ // 当驱动程序绑定到HDF框架时,将传入设备端指针。
+ struct HdfDeviceObject *device;
+ // 设备号
+ unsigned int devNo;
+ // 控制器提供的所有接口
+ struct MipiCsiCntlrMethod *ops;
+ // 对于控制器调试的所有接口,如果未实现驱动程序,则需要null。
+ struct MipiCsiCntlrDebugMethod *debugs;
+ // 控制器上下文参数变量
+ MipiDevCtx ctx;
+ // 访问控制器上下文参数变量时锁定
+ OsalSpinlock ctxLock;
+ // 操作控制器时锁定方法
+ struct OsalMutex lock;
+ // 匿名数据指针,用于存储csi设备结构。
+ void *priv;
+ };
+ ```
+
+ - MipiCsiCntlr成员钩子函数结构体MipiCsiCntlrMethod的实例化
+
+ >![](../public_sys-resources/icon-note.gif) **说明:**
+ >其他成员在Init函数中初始化。
+
+ ```c
+ static struct MipiCsiCntlrMethod g_method = {
+ .setComboDevAttr = Hi35xxSetComboDevAttr,
+ .setPhyCmvmode = Hi35xxSetPhyCmvmode,
+ .setExtDataType = Hi35xxSetExtDataType,
+ .setHsMode = Hi35xxSetHsMode,
+ .enableClock = Hi35xxEnableClock,
+ .disableClock = Hi35xxDisableClock,
+ .resetRx = Hi35xxResetRx,
+ .unresetRx = Hi35xxUnresetRx,
+ .enableSensorClock = Hi35xxEnableSensorClock,
+ .disableSensorClock = Hi35xxDisableSensorClock,
+ .resetSensor = Hi35xxResetSensor,
+ .unresetSensor = Hi35xxUnresetSensor
+ };
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态 (表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ MipiCsiCntlrMethod的实例化对象的挂载,调用MipiCsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。
+
+ ```c
+ static int32_t Hi35xxMipiCsiInit(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+
+ HDF_LOGI("%s: enter!", __func__);
+ g_mipiCsi.priv = NULL; // g_mipiTx是定义的全局变量
+ // static struct MipiCsiCntlr g_mipiCsi = {
+ // .devNo = 0
+ // };
+ g_mipiCsi.ops = &g_method; // MipiCsiCntlrMethod的实例化对象的挂载
+ #ifdef CONFIG_HI_PROC_SHOW_SUPPORT
+ g_mipiCsi.debugs = &g_debugMethod;
+ #endif
+ ret = MipiCsiRegisterCntlr(&g_mipiCsi, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: [MipiCsiRegisterCntlr] failed!", __func__);
+ return ret;
+ }
+
+ ret = MipiRxDrvInit(); // 【必要】驱动适配者对设备的初始化,形式不限。
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: [MipiRxDrvInit] failed.", __func__);
+ return ret;
+ }
+ #ifdef MIPICSI_VFS_SUPPORT
+ ret = MipiCsiDevModuleInit(g_mipiCsi.devNo);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: [MipiCsiDevModuleInit] failed!", __func__);
+ return ret;
+ }
+ #endif
+
+ OsalSpinInit(&g_mipiCsi.ctxLock);
+ HDF_LOGI("%s: load mipi csi driver success!", __func__);
+ return ret;
+ }
-1. 一般来说,驱动开发首先需要新增mipicsi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据。
+ // mipi_csi_core.c核心层
+ int32_t MipiCsiRegisterCntlr(struct MipiCsiCntlr *cntlr, struct HdfDeviceObject *device)
+ {
+ ......
+ // 定义的全局变量:static struct MipiCsiHandle g_mipiCsihandle[MAX_CNTLR_CNT];
+ if (g_mipiCsihandle[cntlr->devNo].cntlr == NULL) {
+ (void)OsalMutexInit(&g_mipiCsihandle[cntlr->devNo].lock);
+ (void)OsalMutexInit(&(cntlr->lock));
+
+ g_mipiCsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiCsiHandle成员
+ g_mipiCsihandle[cntlr->devNo].priv = NULL;
+ cntlr->device = device; // 使HdfDeviceObject与MipiCsiHandle可以相互转化的前提
+ device->service = &(cntlr->service); // 使HdfDeviceObject与MipiCsiHandle可以相互转化的前提
+ cntlr->priv = NULL;
+ HDF_LOGI("%s: success.", __func__);
+
+ return HDF_SUCCESS;
+ }
- 器件属性值与核心层MipiCsiCntlr 成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。
+ HDF_LOGE("%s: cntlr already exists.", __func__);
+ return HDF_FAILURE;
+ }
+ ```
- >![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**
- >本例中MIPI控制器配置属性在源文件中,没有新增配置文件,驱动适配者如有需要,可在device_info.hcs文件的deviceNode增加deviceMatchAttr字段,同时新增mipicsi_config.hcs文件,并使其match_attr字段与之相同。
+ - Release函数开发参考
- device_info.hcs配置参考
+ 入参:
- ```c
- root {
- device_info {
- match_attr = "hdf_manager";
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_mipi_csi:: device {
- device0 :: deviceNode {
- policy = 0;
- priority = 160;
- permission = 0644;
- moduleName = "HDF_MIPI_RX"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
- serviceName = "HDF_MIPI_RX"; // 【必要且唯一】驱动对外发布服务的名称
- }
- }
- }
+ HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ 无
+
+ 函数说明:
+
+ 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。
+
+ >![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**
+ >所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
+
+ ```c
+ static void Hi35xxMipiCsiRelease(struct HdfDeviceObject *device)
+ {
+ struct MipiCsiCntlr *cntlr = NULL;
+ ......
+ cntlr = MipiCsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiCsiCntlr的强制转化
+ // return (device == NULL) ? NULL : (struct MipiCsiCntlr *)device->service;
+ ......
+
+ OsalSpinDestroy(&cntlr->ctxLock);
+ #ifdef MIPICSI_VFS_SUPPORT
+ MipiCsiDevModuleExit(cntlr->devNo);
+ #endif
+ MipiRxDrvExit(); // 【必要】对设备所占资源的释放
+ MipiCsiUnregisterCntlr(&g_mipiCsi); // 空函数
+ g_mipiCsi.priv = NULL;
+
+ HDF_LOGI("%s: unload mipi csi driver success!", __func__);
}
- }
- ```
-
-2. 完成器件属性文件的配置之后,下一步请实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。
-
- 一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
- MIPI CSI驱动入口参考
-
- ```c
- struct HdfDriverEntry g_mipiCsiDriverEntry = {
- .moduleVersion = 1,
- .Init = Hi35xxMipiCsiInit, // 见Init开发参考
- .Release = Hi35xxMipiCsiRelease, // 见Release开发参考
- .moduleName = "HDF_MIPI_RX", // 【必要】需要与device_info.hcs 中保持一致
- };
- HDF_INIT(g_mipiCsiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
- ```
-
-3. 完成驱动入口注册之后,最后一步就是以核心层MipiCsiCntlr对象的初始化为核心,实现HdfDriverEntry成员函数(Bind,Init,Release)。
-
- MipiCsiCntlr对象的初始化包括驱动适配者自定义结构体(用于传递参数和数据)和实例化MipiCsiCntlr成员MipiCsiCntlrMethod(让用户可以通过接口来调用驱动底层函数)。
-
- - 自定义结构体参考
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,本例的mipicsi器件属性在源文件中,故基本成员结构与MipiCsiCntlr无太大差异。
-
- ```c
- typedef struct {
- /** 数据类型:8/10/12/14/16位 */
- DataType inputDataType;
- /** MIPI波分复用模式 */
- MipiWdrMode wdrMode;
- /** laneId: -1 - 禁用 */
- short laneId[MIPI_LANE_NUM];
-
- union {
- /** 用于 HI_MIPI_WDR_MODE_DT */
- short dataType[WDR_VC_NUM];
- };
- } MipiDevAttr;
-
- typedef struct {
- /** 设备号 */
- uint8_t devno;
- /** 输入模式: MIPI/LVDS/SUBLVDS/HISPI/DC */
- InputMode inputMode;
- MipiDataRate dataRate;
- /** MIPI Rx设备裁剪区域(与原始传感器输入图像大小相对应) */
- ImgRect imgRect;
-
- union {
- MipiDevAttr mipiAttr;
- LvdsDevAttr lvdsAttr;
- };
- } ComboDevAttr;
-
- /* MipiCsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
- struct MipiCsiCntlr {
- /** 当驱动程序绑定到HDF框架时,将发送此控制器提供的服务。 */
- struct IDeviceIoService service;
- /** 当驱动程序绑定到HDF框架时,将传入设备端指针。 */
- struct HdfDeviceObject *device;
- /** 设备号 */
- unsigned int devNo;
- /** 控制器提供的所有接口 */
- struct MipiCsiCntlrMethod *ops;
- /** 对于控制器调试的所有接口,如果未实现驱动程序,则需要null。 */
- struct MipiCsiCntlrDebugMethod *debugs;
- /** 控制器上下文参数变量 */
- MipiDevCtx ctx;
- /** 访问控制器上下文参数变量时锁定 */
- OsalSpinlock ctxLock;
- /** 操作控制器时锁定方法 */
- struct OsalMutex lock;
- /** 匿名数据指针,用于存储csi设备结构。 */
- void *priv;
- };
- ```
-
- - MipiCsiCntlr成员钩子函数结构体MipiCsiCntlrMethod的实例化
-
- >![](../public_sys-resources/icon-note.gif) **说明:**
- >其他成员在Init函数中初始化。
-
- ```c
- static struct MipiCsiCntlrMethod g_method = {
- .setComboDevAttr = Hi35xxSetComboDevAttr,
- .setPhyCmvmode = Hi35xxSetPhyCmvmode,
- .setExtDataType = Hi35xxSetExtDataType,
- .setHsMode = Hi35xxSetHsMode,
- .enableClock = Hi35xxEnableClock,
- .disableClock = Hi35xxDisableClock,
- .resetRx = Hi35xxResetRx,
- .unresetRx = Hi35xxUnresetRx,
- .enableSensorClock = Hi35xxEnableSensorClock,
- .disableSensorClock = Hi35xxDisableSensorClock,
- .resetSensor = Hi35xxResetSensor,
- .unresetSensor = Hi35xxUnresetSensor
- };
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- **表2** HDF_STATUS返回值描述
-
- | 状态(值) | 问题描述 |
- | :--------------------- | :----------: |
- | HDF_ERR_INVALID_OBJECT | 无效对象 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 无效参数 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 执行成功 |
- | HDF_FAILURE | 执行失败 |
-
- 函数说明:
-
- MipiCsiCntlrMethod的实例化对象的挂载,调用MipiCsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。
-
- ```c
- static int32_t Hi35xxMipiCsiInit(struct HdfDeviceObject *device)
- {
- int32_t ret;
-
- HDF_LOGI("%s: enter!", __func__);
- g_mipiCsi.priv = NULL; // g_mipiTx是定义的全局变量
- // static struct MipiCsiCntlr g_mipiCsi = {
- // .devNo = 0
- // };
- g_mipiCsi.ops = &g_method; // MipiCsiCntlrMethod的实例化对象的挂载
- #ifdef CONFIG_HI_PROC_SHOW_SUPPORT
- g_mipiCsi.debugs = &g_debugMethod;
- #endif
- ret = MipiCsiRegisterCntlr(&g_mipiCsi, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: [MipiCsiRegisterCntlr] failed!", __func__);
- return ret;
- }
-
- ret = MipiRxDrvInit(); // 【必要】驱动适配者对设备的初始化,形式不限。
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: [MipiRxDrvInit] failed.", __func__);
- return ret;
- }
- #ifdef MIPICSI_VFS_SUPPORT
- ret = MipiCsiDevModuleInit(g_mipiCsi.devNo);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: [MipiCsiDevModuleInit] failed!", __func__);
- return ret;
- }
- #endif
-
- OsalSpinInit(&g_mipiCsi.ctxLock);
- HDF_LOGI("%s: load mipi csi driver success!", __func__);
-
- return ret;
- }
-
- /* mipi_csi_core.c核心层 */
- int32_t MipiCsiRegisterCntlr(struct MipiCsiCntlr *cntlr, struct HdfDeviceObject *device)
- {
- ...
- /* 定义的全局变量:static struct MipiCsiHandle g_mipiCsihandle[MAX_CNTLR_CNT]; */
- if (g_mipiCsihandle[cntlr->devNo].cntlr == NULL) {
- (void)OsalMutexInit(&g_mipiCsihandle[cntlr->devNo].lock);
- (void)OsalMutexInit(&(cntlr->lock));
-
- g_mipiCsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiCsiHandle成员
- g_mipiCsihandle[cntlr->devNo].priv = NULL;
- cntlr->device = device; // 使HdfDeviceObject与MipiCsiHandle可以相互转化的前提
- device->service = &(cntlr->service); // 使HdfDeviceObject与MipiCsiHandle可以相互转化的前提
- cntlr->priv = NULL;
- HDF_LOGI("%s: success.", __func__);
-
- return HDF_SUCCESS;
- }
-
- HDF_LOGE("%s: cntlr already exists.", __func__);
- return HDF_FAILURE;
- }
- ```
-
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- 无
-
- 函数说明:
-
- 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。
-
- >![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**
- >所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
-
- ```c
- static void Hi35xxMipiCsiRelease(struct HdfDeviceObject *device)
- {
- struct MipiCsiCntlr *cntlr = NULL;
- ...
- cntlr = MipiCsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiCsiCntlr的强制转化
- // return (device == NULL) ? NULL : (struct MipiCsiCntlr *)device->service;
- ...
-
- OsalSpinDestroy(&cntlr->ctxLock);
- #ifdef MIPICSI_VFS_SUPPORT
- MipiCsiDevModuleExit(cntlr->devNo);
- #endif
- MipiRxDrvExit(); // 【必要】对设备所占资源的释放
- MipiCsiUnregisterCntlr(&g_mipiCsi); // 空函数
- g_mipiCsi.priv = NULL;
-
- HDF_LOGI("%s: unload mipi csi driver success!", __func__);
- }
- ```
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈。
diff --git a/zh-cn/device-dev/driver/driver-platform-mipidsi-des.md b/zh-cn/device-dev/driver/driver-platform-mipidsi-des.md
index 73b158b58f3380dc511e95cff958e58959b3af01..17d6b5e220b92aec55d60b2b3e91f3b1c4877f94 100644
--- a/zh-cn/device-dev/driver/driver-platform-mipidsi-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-mipidsi-des.md
@@ -10,41 +10,43 @@ MIPI DSI具备高速模式和低速模式两种工作模式,全部数据通道
图1显示了简化的DSI接口。从概念上看,符合DSI的接口与基于DBI-2和DPI-2标准的接口具有相同的功能。它向外围设备传输像素或命令数据,并且可以从外围设备读取状态或像素信息。主要区别在于,DSI对所有像素数据、命令和事件进行序列化,而在传统接口中,这些像素数据、命令和事件通常需要附加控制信号才能在并行数据总线上传输。
-**图1** DSI发送、接收接口
+**图 1** DSI发送、接收接口
-![image](figures/DSI发送-接收接口.png "DSI发送-接收接口")
+![DSI发送、接收接口](figures/DSI发送-接收接口.png)
DSI标准对应D-PHY、DSI、DCS规范,可分为四层:
- PHY Layer
- 定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。
+ 定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。
- Lane Management层
- 负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode,High-Speed(Burst) mode,Control mode。
+ 负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode,High-Speed(Burst) mode,Control mode。
- Low Level Protocol层
- 定义了如何组帧和解析以及错误检测等。
+ 定义了如何组帧和解析以及错误检测等。
- Application层
- 描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。
+ 描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。
### 运作机制
MIPI DSI软件模块各分层的作用为:
- 接口层:提供打开设备、写入数据和关闭设备的接口。
+
- 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。
+
- 适配层:实现其它具体的功能。
![](../public_sys-resources/icon-note.gif) **说明:**
核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
-**图2** DSI无服务模式结构图
+**图 2** DSI无服务模式结构图
-![image](figures/无服务模式结构图.png "DSI无服务模式结构图")
+![DSI无服务模式结构图](figures/无服务模式结构图.png)
### 约束与限制
@@ -62,7 +64,7 @@ MIPI DSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_
**表1** MIPI DSI API接口功能介绍
-| 功能分类 | 接口名 |
+| 功能分类 | 接口名 |
| -------- | -------- |
| DevHandle MipiDsiOpen(uint8_t id) | 获取MIPI DSI操作句柄 |
| void MipiDsiClose(DevHandle handle) | 释放MIPI DSI操作句柄 |
@@ -77,25 +79,23 @@ MIPI DSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_
使用MIPI DSI的一般流程如下图所示。
-**图3** MIPI DSI使用流程图
-
-![image](figures/MIPI-DSI使用流程图.png "MIPI-DSI使用流程图")
+**图 3** MIPI DSI使用流程图
+![MIPI DSI使用流程图](figures/MIPI-DSI使用流程图.png)
#### 获取MIPI DSI操作句柄
在进行MIPI DSI进行通信前,首先要调用MipiDsiOpen获取操作句柄,该函数会返回指定通道ID的操作句柄。
-
```c
DevHandle MipiDsiOpen(uint8_t id);
```
- **表2** MipiDsiOpen的参数和返回值描述
+**表 2** MipiDsiOpen的参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| id | MIPI DSI通道ID |
+| id | uint8_t类型,MIPI DSI通道ID |
| **返回值** | **返回值描述** |
| NULL | 获取失败 |
| 设备句柄 | 获取到指令通道的操作句柄, 类型为DevHandle |
@@ -104,186 +104,180 @@ DevHandle MipiDsiOpen(uint8_t id);
```c
-DevHandle mipiDsiHandle = NULL; /* 设备句柄 */
-chnId = 0; /* MIPI DSI通道ID */
+DevHandle mipiDsiHandle = NULL; // 设备句柄
+chnId = 0; // MIPI DSI通道ID
-/* 获取操作句柄 */
+// 获取操作句柄
mipiDsiHandle = MipiDsiOpen(chnId);
if (mipiDsiHandle == NULL) {
- HDF_LOGE("MipiDsiOpen: failed\n");
- return;
+ HDF_LOGE("MipiDsiOpen: mipi dsi open fail.\n");
+ return NULL;
}
```
-
#### MIPI DSI相应配置
- 写入MIPI DSI配置
-
- ```c
- int32_t MipiDsiSetCfg(DevHandle handle, struct MipiCfg *cfg);
- ```
-
- **表3** MipiDsiSetCfg的参数和返回值描述
-
- | **参数** | **参数描述** |
- | -------- | -------- |
- | handle | 操作句柄 |
- | cfg | MIPI DSI相应配置buf 指针 |
- | **返回值** | **返回值描述** |
- | 0 | 设置成功 |
- | 负数 | 设置失败 |
-
- ```c
- int32_t ret;
- struct MipiCfg cfg = {0};
-
- /* 当前对接的屏幕配置如下 */
- cfg.lane = DSI_4_LANES;
- cfg.mode = DSI_CMD_MODE;
- cfg.burstMode = VIDEO_NON_BURST_MODE_SYNC_EVENTS;
- cfg.format = FORMAT_RGB_24_BIT;
- cfg.pixelClk = 174;
- cfg.phyDataRate = 384;
- cfg.timingInfo.hsaPixels = 50;
- cfg.timingInfo.hbpPixels = 55;
- cfg.timingInfo.hlinePixels = 1200;
- cfg.timingInfo.yResLines = 1800;
- cfg.timingInfo.vbpLines = 33;
- cfg.timingInfo.vsaLines = 76;
- cfg.timingInfo.vfpLines = 120;
- cfg.timingInfo.xResPixels = 1342;
- /* 写入配置数据 */
- ret = MipiDsiSetCfg(mipiDsiHandle, &cfg);
- if (ret != 0) {
- HDF_LOGE("%s: SetMipiCfg fail! ret=%d\n", __func__, ret);
- return -1;
- }
- ```
+ ```c
+ int32_t MipiDsiSetCfg(DevHandle handle, struct MipiCfg *cfg);
+ ```
-- 获取当前MIPI DSI的配置
+ **表 3** MipiDsiSetCfg的参数和返回值描述
- ```c
- int32_t MipiDsiGetCfg(DevHandle handle, struct MipiCfg *cfg);
- ```
+ | **参数** | **参数描述** |
+ | -------- | -------- |
+ | handle | DevHandle类型,操作句柄 |
+ | cfg | 结构体指针类型,MIPI DSI相应配置buf 指针 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 设置MIPI DSI配置成功 |
+ | 负数 | 设置MIPI DSI配置失败 |
+
+ ```c
+ int32_t ret;
+ struct MipiCfg cfg = {0};
- **表4** MipiDsiGetCfg的参数和返回值描述
-
- | **参数** | **参数描述** |
- | -------- | -------- |
- | handle | 操作句柄 |
- | cfg | MIPI DSI相应配置buf 指针 |
- | **返回值** | **返回值描述** |
- | 0 | 获取成功 |
- | 负数 | 获取失败 |
+ // 当前对接的屏幕配置如下
+ cfg.lane = DSI_4_LANES;
+ cfg.mode = DSI_CMD_MODE;
+ cfg.burstMode = VIDEO_NON_BURST_MODE_SYNC_EVENTS;
+ cfg.format = FORMAT_RGB_24_BIT;
+ cfg.pixelClk = 174;
+ cfg.phyDataRate = 384;
+ cfg.timingInfo.hsaPixels = 50;
+ cfg.timingInfo.hbpPixels = 55;
+ cfg.timingInfo.hlinePixels = 1200;
+ cfg.timingInfo.yResLines = 1800;
+ cfg.timingInfo.vbpLines = 33;
+ cfg.timingInfo.vsaLines = 76;
+ cfg.timingInfo.vfpLines = 120;
+ cfg.timingInfo.xResPixels = 1342;
+ // 写入配置数据
+ ret = MipiDsiSetCfg(mipiDsiHandle, &cfg);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiDsiSetCfg: set mipi cfg fail, ret:%d\n", ret);
+ return ret;
+ }
+ ```
-
- ```c
- int32_t ret;
- struct MipiCfg cfg;
- memset(&cfg, 0, sizeof(struct MipiCfg));
- ret = MipiDsiGetCfg(mipiDsiHandle, &cfg);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: GetMipiCfg fail!\n", __func__);
- return HDF_FAILURE;
- }
- ```
+- 获取当前MIPI DSI的配置
+ ```c
+ int32_t MipiDsiGetCfg(DevHandle handle, struct MipiCfg *cfg);
+ ```
+
+ **表 4** MipiDsiGetCfg的参数和返回值描述
+
+ | **参数** | **参数描述** |
+ | -------- | -------- |
+ | handle | DevHandle类型,操作句柄 |
+ | cfg | 结构体指针,MIPI DSI相应配置buf 指针 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 获取当前MIPI DSI的配置成功 |
+ | 负数 | 获取当前MIPI DSI的配置失败 |
+
+ ```c
+ int32_t ret;
+ struct MipiCfg cfg;
+ memset(&cfg, 0, sizeof(struct MipiCfg));
+ ret = MipiDsiGetCfg(mipiDsiHandle, &cfg);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGEMipiDsiGetCfg: get mipi cfg fail, ret:%d!\n", ret);
+ return ret;
+ }
+ ```
#### 发送/回读控制指令
- 发送指令
-
- ```c
- int32_t MipiDsiTx(PalHandle handle, struct DsiCmdDesc *cmd);
- ```
- **表5** MipiDsiTx的参数和返回值描述
-
- | **参数** | **参数描述** |
- | -------- | -------- |
- | handle | 操作句柄 |
- | cmd | 需要发送的指令数据指针 |
- | **返回值** | **返回值描述** |
- | 0 | 发送成功 |
- | 负数 | 发送失败 |
+ ```c
+ int32_t MipiDsiTx(PalHandle handle, struct DsiCmdDesc *cmd);
+ ```
-
- ```c
- int32_t ret;
- struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc));
- if (cmd == NULL) {
- return HDF_FAILURE;
- }
- cmd->dtype = DTYPE_DCS_WRITE;
- cmd->dlen = 1;
- cmd->payload = OsalMemCalloc(sizeof(uint8_t));
- if (cmd->payload == NULL) {
- HdfFree(cmd);
- return HDF_FAILURE;
- }
- *(cmd->payload) = DTYPE_GEN_LWRITE;
- MipiDsiSetLpMode(mipiHandle);
- ret = MipiDsiTx(mipiHandle, cmd);
- MipiDsiSetHsMode(mipiHandle);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: PalMipiDsiTx fail! ret=%d\n", __func__, ret);
- HdfFree(cmd->payload);
- HdfFree(cmd);
- return HDF_FAILURE;
- }
- HdfFree(cmd->payload);
- HdfFree(cmd);
- ```
+ **表 5** MipiDsiTx的参数和返回值描述
-- 回读指令
-
- ```c
- int32_t MipiDsiRx(DevHandle handle, struct DsiCmdDesc *cmd, uint32_t readLen, uint8_t *out);
- ```
+ | **参数** | **参数描述** |
+ | -------- | -------- |
+ | handle | DevHandle类型,操作句柄 |
+ | cmd | 结构体指针类型,需要发送的指令数据指针 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 发送成功 |
+ | 负数 | 发送失败 |
+
+ ```c
+ int32_t ret;
+ struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc));
+ if (cmd == NULL) {
+ return HDF_FAILURE;
+ }
+ cmd->dtype = DTYPE_DCS_WRITE;
+ cmd->dlen = 1;
+ cmd->payload = OsalMemCalloc(sizeof(uint8_t));
+ if (cmd->payload == NULL) {
+ HdfFree(cmd);
+ return HDF_FAILURE;
+ }
+ *(cmd->payload) = DTYPE_GEN_LWRITE;
+ MipiDsiSetLpMode(mipiHandle);
+ ret = MipiDsiTx(mipiHandle, cmd);
+ MipiDsiSetHsMode(mipiHandle);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiDsiTx: mipi dsi tx fail, ret:%d\n", ret);
+ HdfFree(cmd->payload);
+ HdfFree(cmd);
+ return ret;
+ }
+ HdfFree(cmd->payload);
+ HdfFree(cmd);
+ ```
- **表6** MipiDsiRx的参数和返回值描述
+- 回读指令
- | **参数** | **参数描述** |
- | -------- | -------- |
- | handle | 操作句柄 |
- | cmd | 需要回读的指令数据指针 |
- | readLen | 期望回读的数据长度 |
- | out | 回读的数据buf指针 |
- | **返回值** | **返回值描述** |
- | 0 | 获取成功 |
- | 负数 | 获取失败 |
+ ```c
+ int32_t MipiDsiRx(DevHandle handle, struct DsiCmdDesc *cmd, uint32_t readLen, uint8_t *out);
+ ```
+ **表 6** MipiDsiRx的参数和返回值描述
- ```c
- int32_t ret;
- uint8_t readVal = 0;
+ | **参数** | **参数描述** |
+ | -------- | -------- |
+ | handle | DevHandle类型,操作句柄 |
+ | cmd | 结构体指针类型,需要回读的指令数据指针 |
+ | readLen | uint32_t类型,期望回读的数据长度 |
+ | out | uint8_t类型指针,回读的数据 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 获取成功 |
+ | 负数 | 获取失败 |
- struct DsiCmdDesc *cmdRead = OsalMemCalloc(sizeof(struct DsiCmdDesc));
- if (cmdRead == NULL) {
- return HDF_FAILURE;
- }
- cmdRead->dtype = DTYPE_DCS_READ;
- cmdRead->dlen = 1;
- cmdRead->payload = OsalMemCalloc(sizeof(uint8_t));
- if (cmdRead->payload == NULL) {
- HdfFree(cmdRead);
- return HDF_FAILURE;
- }
- *(cmdRead->payload) = DDIC_REG_STATUS;
- MipiDsiSetLpMode(mipiDsiHandle);
- ret = MipiDsiRx(mipiDsiHandle, cmdRead, sizeof(readVal), &readVal);
- MipiDsiSetHsMode(mipiDsiHandle);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: MipiDsiRx fail! ret=%d\n", __func__, ret);
- HdfFree(cmdRead->payload);
- HdfFree(cmdRead);
- return HDF_FAILURE;
- }
- HdfFree(cmdRead->payload);
- HdfFree(cmdRead);
- ```
+ ```c
+ int32_t ret;
+ uint8_t readVal = 0;
+
+ struct DsiCmdDesc *cmdRead = OsalMemCalloc(sizeof(struct DsiCmdDesc));
+ if (cmdRead == NULL) {
+ return HDF_FAILURE;
+ }
+ cmdRead->dtype = DTYPE_DCS_READ;
+ cmdRead->dlen = 1;
+ cmdRead->payload = OsalMemCalloc(sizeof(uint8_t));
+ if (cmdRead->payload == NULL) {
+ HdfFree(cmdRead);
+ return HDF_FAILURE;
+ }
+ *(cmdRead->payload) = DDIC_REG_STATUS;
+ MipiDsiSetLpMode(mipiDsiHandle);
+ ret = MipiDsiRx(mipiDsiHandle, cmdRead, sizeof(readVal), &readVal);
+ MipiDsiSetHsMode(mipiDsiHandle);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("MipiDsiRx: mipi dsi rx fail, ret:%d\n", ret);
+ HdfFree(cmdRead->payload);
+ HdfFree(cmdRead);
+ return HDF_FAILURE;
+ }
+ HdfFree(cmdRead->payload);
+ HdfFree(cmdRead);
+ ```
#### 释放MIPI DSI操作句柄
@@ -296,17 +290,16 @@ void MipiDsiClose(DevHandle handle);
该函数会释放掉由MipiDsiOpen申请的资源。
- **表7** MipiDsiClose的参数和返回值描述
+**表 7** MipiDsiClose的参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | MIPI DSI操作句柄 |
+| handle | DevHandle类型,MIPI DSI操作句柄 |
```c
-MipiDsiClose(mipiHandle); /* 释放掉MIPI DSI操作句柄 */
+MipiDsiClose(mipiHandle); // 释放掉MIPI DSI操作句柄
```
-
## 使用实例
本例拟对Hi3516DV300开发板上MIPI DSI设备进行操作。
@@ -314,95 +307,105 @@ MipiDsiClose(mipiHandle); /* 释放掉MIPI DSI操作句柄 */
MIPI DSI完整的使用示例如下所示:
```c
-#include "hdf.h"
+#include "hdf_log.h"
#include "mipi_dsi_if.h"
+#include "osal_mem.h"
+
+#define DTYPE_DCS_WRITE 0x05
+#define DTYPE_DCS_READ 0x06
+#define DTYPE_GEN_LWRITE 0x29
+#define DDIC_REG_STATUS 0x0A
-void PalMipiDsiTestSample(void)
+
+int32_t PalMipiDsiTestSample(void)
{
uint8_t chnId;
int32_t ret;
DevHandle mipiDsiHandle = NULL;
- /* 设备通道编号 */
+ // 设备通道编号
chnId = 0;
- /* 获取操作句柄 */
+ // 获取操作句柄
mipiDsiHandle = MipiDsiOpen(chnId);
if (mipiDsiHandle == NULL) {
HDF_LOGE("MipiDsiOpen: failed!\n");
- return;
+ return HDF_FAILURE;
}
- /* 配置相应参数 */
+ // 配置相应参数
struct MipiCfg cfg = {0};
- cfg.lane = DSI_4_LANES;
- cfg.mode = DSI_CMD_MODE;
- cfg.burstMode = VIDEO_NON_BURST_MODE_SYNC_EVENTS;
+ cfg.lane = DSI_2_LANES;
+ cfg.mode = DSI_VIDEO_MODE;
cfg.format = FORMAT_RGB_24_BIT;
- cfg.pixelClk = 174;
- cfg.phyDataRate = 384;
- cfg.timingInfo.hsaPixels = 50;
- cfg.timingInfo.hbpPixels = 55;
- cfg.timingInfo.hlinePixels = 1200;
- cfg.timingInfo.yResLines = 1800;
- cfg.timingInfo.vbpLines = 33;
- cfg.timingInfo.vsaLines = 76;
- cfg.timingInfo.vfpLines = 120;
- cfg.timingInfo.xResPixels = 1342;
- /* 写入配置数据 */
+ cfg.burstMode = VIDEO_BURST_MODE;
+ cfg.timing.xPixels = 480; // 480: width
+ cfg.timing.hsaPixels = 10; // 10: horizontal sync porch
+ cfg.timing.hbpPixels = 20; // 20: horizontal back porch
+ cfg.timing.hlinePixels = 530; // 530: horizontal total width
+ cfg.timing.vsaLines = 2; // 2: vertiacl sync width
+ cfg.timing.vbpLines = 14; // 14: vertiacl back porch
+ cfg.timing.vfpLines = 16; // 16: vertiacl front porch
+ cfg.timing.ylines = 960; // 960: height
+ cfg.timing.edpiCmdSize = 0; // 0 : no care
+ cfg.pixelClk = 31546; // 31546: pixel clk
+ cfg.phyDataRate = 379; // 379: mipi clk
+ // 写入配置数据
ret = MipiDsiSetCfg(mipiDsiHandle, &cfg);
if (ret != 0) {
- HDF_LOGE("%s: SetMipiCfg fail! ret=%d\n", __func__, ret);
- return;
+ HDF_LOGE("PalMipiDsiTestSample: set mipi dsi cfg fail, ret:%d\n", ret);
+ return ret;
}
- /* 发送PANEL初始化指令 */
+ // 发送PANEL初始化指令
struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc));
if (cmd == NULL) {
- return;
+ return -1;
}
- cmd->dtype = DTYPE_DCS_WRITE;
- cmd->dlen = 1;
+ cmd->dataType = DTYPE_DCS_WRITE;
+ cmd->dataLen = 1;
cmd->payload = OsalMemCalloc(sizeof(uint8_t));
if (cmd->payload == NULL) {
- HdfFree(cmd);
- return;
+ OsalMemFree(cmd);
+ return -1;
}
*(cmd->payload) = DTYPE_GEN_LWRITE;
- MipiDsiSetLpMode(mipiHandle);
- ret = MipiDsiTx(mipiHandle, cmd);
- MipiDsiSetHsMode(mipiHandle);
+ MipiDsiSetLpMode(mipiDsiHandle);
+ ret = MipiDsiTx(mipiDsiHandle, cmd);
+ MipiDsiSetHsMode(mipiDsiHandle);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: MipiDsiTx fail! ret=%d\n", __func__, ret);
- HdfFree(cmd->payload);
- HdfFree(cmd);
- return;
+ HDF_LOGE("PalMipiDsiTestSample: mipi dsi tx fail, ret:%d\n", ret);
+ OsalMemFree(cmd->payload);
+ OsalMemFree(cmd);
+ return ret;
}
- HdfFree(cmd->payload);
- HdfFree(cmd);
- /* 回读panel状态寄存器 */
+ OsalMemFree(cmd->payload);
+ OsalMemFree(cmd);
+ // 回读panel状态寄存器
uint8_t readVal = 0;
struct DsiCmdDesc *cmdRead = OsalMemCalloc(sizeof(struct DsiCmdDesc));
if (cmdRead == NULL) {
- return;
+ return -1;
}
- cmdRead->dtype = DTYPE_DCS_READ;
- cmdRead->dlen = 1;
+ cmdRead->dataType = DTYPE_DCS_READ;
+ cmdRead->dataLen = 1;
cmdRead->payload = OsalMemCalloc(sizeof(uint8_t));
if (cmdRead->payload == NULL) {
- HdfFree(cmdRead);
- return;
+ OsalMemFree(cmdRead);
+ return -1;
}
*(cmdRead->payload) = DDIC_REG_STATUS;
MipiDsiSetLpMode(mipiDsiHandle);
ret = MipiDsiRx(mipiDsiHandle, cmdRead, sizeof(readVal), &readVal);
MipiDsiSetHsMode(mipiDsiHandle);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: MipiDsiRx fail! ret=%d\n", __func__, ret);
- HdfFree(cmdRead->payload);
- HdfFree(cmdRead);
- return;
+ HDF_LOGE("PalMipiDsiTestSample: mipi dsi rx fail, ret:%d\n", ret);
+ OsalMemFree(cmdRead->payload);
+ OsalMemFree(cmdRead);
+ return ret;
}
- HdfFree(cmdRead->payload);
- HdfFree(cmdRead);
- /* 释放MIPI DSI设备句柄 */
- MipiDsiClose(handle);
+ OsalMemFree(cmdRead->payload);
+ OsalMemFree(cmdRead);
+ HDF_LOGD("PalMipiDsiTestSample: mipi dsi tests end");
+ // 释放MIPI DSI设备句柄
+ MipiDsiClose(mipiDsiHandle);
+ return ret;
}
```
diff --git a/zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md b/zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md
index a83b5de1313a6454aafbd089ee54b4fc80086d57..7fd7ee7fc14683eb7f34df6a07549b6f96ba18e7 100755
--- a/zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md
@@ -10,46 +10,47 @@ MIPI DSI具备高速模式和低速模式两种工作模式,全部数据通道
图1显示了简化的DSI接口。从概念上看,符合DSI的接口与基于DBI-2和DPI-2标准的接口具有相同的功能。它向外围设备传输像素或命令数据,并且可以从外围设备读取状态或像素信息。主要区别在于,DSI对所有像素数据、命令和事件进行序列化,而在传统接口中,这些像素数据、命令和事件通常需要附加控制信号才能在并行数据总线上传输。
-**图1** DSI发送、接收接口
+**图 1** DSI发送、接收接口
-![image](figures/DSI发送-接收接口.png "DSI发送-接收接口")
+![DSI发送、接收接口](figures/DSI发送-接收接口.png)
DSI标准对应D-PHY、DSI、DCS规范,可分为四层:
- PHY Layer
- 定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。
+ 定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。
- Lane Management层
- 负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode, High-Speed(Burst) mode, Control mode 。
+ 负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode, High-Speed(Burst) mode, Control mode 。
- Low Level Protocol层
- 定义了如何组帧和解析以及错误检测等。
+ 定义了如何组帧和解析以及错误检测等。
- Application层
- 描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。
+ 描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。
### 运作机制
MIPI DSI软件模块各分层的作用为:
- 接口层:提供打开设备、写入数据和关闭设备的接口。
+
- 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。
+
- 适配层:实现其它具体的功能。
![](../public_sys-resources/icon-note.gif) **说明:**
核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
+**图 2** DSI无服务模式结构图
- **图2** DSI无服务模式结构图
-
- ![image](figures/无服务模式结构图.png "DSI无服务模式结构图")
+![DSI无服务模式结构图](figures/无服务模式结构图.png)
## 开发指导
- ### 场景介绍
+### 场景介绍
MIPI DSI仅是一个软件层面的概念,主要工作是MIPI DSI资源管理。开发者可以通过使用提供的提供的操作接口,实现DSI资源管理。当驱动开发者需要将MIPI DSI设备适配到OpenHarmony时,需要进行MIPI DSI驱动适配,下文将介绍如何进行MIPI DSI驱动适配。
@@ -73,222 +74,243 @@ struct MipiDsiCntlrMethod { // 核心层结构体的成员函数
};
```
- **表1** MipiDsiCntlrMethod成员的钩子函数功能说明
+**表 1** MipiDsiCntlrMethod成员的钩子函数功能说明
| 成员函数 | 入参 | 出参 | 返回状态 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
| setCntlrCfg | cntlr:结构体指针,MipiDsi控制器 | 无 | HDF_STATUS相关状态 | 设置控制器参数 |
| setCmd | cntlr:结构体指针,MipiDsi控制器
cmd:结构体指针,指令传入值 | 无 | HDF_STATUS相关状态 | 向显示设备发送指令 |
-| getCmd | cntlr:结构体指针,MipiDsi控制器
cmd:传入的命令描述结构体指针
readLen:读取的数据大小 | out:结构体指针,用于存储读取的数据 | HDF_STATUS相关状态 | 通过发送指令读取数据 |
+| getCmd | cntlr:结构体指针,MipiDsi控制器
cmd:传入的命令描述结构体指针
readLen:读取的数据大小 | out:uint8_t类型指针,用于存储读取的数据 | HDF_STATUS相关状态 | 通过发送指令读取数据 |
| toHs | cntlr:结构体指针,MipiDsi控制器 | 无 | HDF_STATUS相关状态 | 设置为高速模式 |
| toLp | cntlr:结构体指针,MipiDsi控制器 | 无 | HDF_STATUS相关状态 | 设置为低电模式 |
### 开发步骤
-MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动入口,以及实例化核心层接口函数。
+MIPI DSI模块适配包含以下四个步骤:
-1. 配置属性文件
- - 在device_info.hcs文件中添加deviceNode描述。
- - 【可选】添加mipidsi_config.hcs器件属性文件。
+1. 实例化驱动入口
-2. 实例化驱动入口
- - 实例化HdfDriverEntry结构体成员。
- - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
-
-3. 实例化MIPIDSI控制器对象
- - 初始化MipiDsiCntlr成员。
- - 实例化MipiDsiCntlr成员MipiDsiCntlrMethod。
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化MipiDsiCntlr成员MipiDsiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。
-
-4. 驱动调试
+ - 实例化HdfDriverEntry结构体成员。
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
+ - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
+2. 配置属性文件
-### 开发实例
+ - 在device_info.hcs文件中添加deviceNode描述。
-下方将以mipi_tx_hi35xx.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
+ - 【可选】添加mipi_dsi_config.hcs器件属性文件。
-1. 一般来说,驱动开发首先需要mipicsi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据。
+3. 实例化MIPI DSI控制器对象
- 器件属性值与核心层MipiDsiCntlr成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。
+ - 初始化MipiDsiCntlr成员。
- 但本例中MIPI控制器无需配置额外属性,驱动适配者如有需要,则需要在device_info.hcs文件的deviceNode增加deviceMatchAttr信息,以及增加mipidsi_config.hcs文件。
+ - 实例化MipiDsiCntlr成员MipiDsiCntlrMethod。
- device_info.hcs 配置参考:
-
- ```c
- root {
- device_info {
- match_attr = "hdf_manager";
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_mipi_dsi:: device {
- device0 :: deviceNode {
- policy = 0;
- priority = 150;
- permission = 0644;
- moduleName = "HDF_MIPI_TX"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
- serviceName = "HDF_MIPI_TX"; // 【必要且唯一】驱动对外发布服务的名称。
- }
- }
- }
- }
- }
- ```
-
-2. 完成器件属性文件的配置之后,下一步请实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。
-
- 一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
- MIPI DSI驱动入口参考:
-
- ```c
- struct HdfDriverEntry g_mipiTxDriverEntry = {
- .moduleVersion = 1,
- .Init = Hi35xxMipiTxInit, // 见Init开发参考
- .Release = Hi35xxMipiTxRelease, // 见Release开发参考
- .moduleName = "HDF_MIPI_TX", // 【必要】需要与device_info.hcs 中保持一致。
- };
- HDF_INIT(g_mipiTxDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
- ```
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化MipiDsiCntlr成员MipiDsiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。
-3. 完成驱动入口注册之后,下一步就是以核心层MipiDsiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化MipiDsiCntlr成员MipiDsiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+4. 驱动调试
- - 自定义结构体参考
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
- 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,但本例的mipidsi无器件属性文件,故基本成员结构与MipiDsiCntlr无太大差异。
-
- ```c
- typedef struct {
- unsigned int devno; // 设备号
- short laneId[LANE_MAX_NUM]; // Lane号
- OutPutModeTag outputMode; // 输出模式选择:刷新模式,命令行模式或视频流模式
- VideoModeTag videoMode; // 显示设备的同步模式
- OutputFormatTag outputFormat; // 输出DSI图像数据格式:RGB或YUV
- SyncInfoTag syncInfo; // 时序相关的设置
- unsigned int phyDataRate; // 数据速率,单位Mbps
- unsigned int pixelClk; // 时钟,单位KHz
- } ComboDevCfgTag;
-
- /* MipiDsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
- struct MipiDsiCntlr {
- struct IDeviceIoService service;
- struct HdfDeviceObject *device;
- unsigned int devNo; // 设备号
- struct MipiCfg cfg;
- struct MipiDsiCntlrMethod *ops;
- struct OsalMutex lock;
- void *priv;
- };
- ```
- - MipiDsiCntlr成员钩子函数结构体MipiDsiCntlrMethod的实例化,其他成员在Init函数中初始化。
-
-
- ```c
- static struct MipiDsiCntlrMethod g_method = {
- .setCntlrCfg = Hi35xxSetCntlrCfg,
- .setCmd = Hi35xxSetCmd,
- .getCmd = Hi35xxGetCmd,
- .toHs = Hi35xxToHs,
- .toLp = Hi35xxToLp,
- };
- ```
- - Init函数开发参考
+### 开发实例
- 入参:
+下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/mipi_dsi/mipi_tx_hi35xx.c驱动为示例,展示需要厂商提供哪些内容来完整实现设备功能。
- HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
+1. 实例化驱动入口
- 返回值:
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+ 一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
- **表2** HDF_STATUS返回值描述
+ MIPI DSI驱动入口参考:
+
+ ```c
+ struct HdfDriverEntry g_mipiTxDriverEntry = {
+ .moduleVersion = 1,
+ .Init = Hi35xxMipiTxInit, // 挂接MIPI DSI模块Init实例化
+ .Release = Hi35xxMipiTxRelease, // 挂接MIPI DSI模块Release实例化
+ .moduleName = "HDF_MIPI_TX", // 【必要且与HCS文件中里面的moduleName匹配】
+ };
+ HDF_INIT(g_mipiTxDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
+
+2. 配置属性文件
+ 一般来说,驱动开发首先需要mipi_dsi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据。器件属性值与核心层MipiDsiCntlr成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。但本例中MIPI DSI控制器无需配置额外属性,驱动适配者如有需要,则需要在device_info.hcs文件的deviceNode增加deviceMatchAttr信息,以及增加mipi_dsi_config.hcs文件。
+
+ 无服务模式device_info.hcs文件中设备节点也代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,MIPI DSI控制器具体配置为0,表示驱动不需要发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。MIPI DSI控制器具体配置为150 |
+ | permission | 驱动创建设备节点权限,MIPI DSI控制器具体配置为0664 |
+ | moduleName | 驱动名称,MIPI DSI控制器固定为HDF_MIPI_TX |
+ | serviceName | 驱动对外发布服务的名称,MIPI DSI控制器服务名设置为HDF_MIPI_TX |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,MIPI DSI控制器没有使用,可忽略 |
+
+ device_info.hcs 配置参考:
+
+ ```c
+ root {
+ device_info {
+ match_attr = "hdf_manager";
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_mipi_dsi:: device {
+ device0 :: deviceNode {
+ policy = 0;
+ priority = 150;
+ permission = 0644;
+ moduleName = "HDF_MIPI_TX"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
+ serviceName = "HDF_MIPI_TX"; // 【必要且唯一】驱动对外发布服务的名称。
+ }
+ }
+ }
+ }
+ }
+ ```
+
+3. 实例化MIPI DSI控制器对象
+
+ 完成驱动入口注册之后,下一步就是以核心层MipiDsiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化MipiDsiCntlr成员MipiDsiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+
+ - 自定义结构体参考
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,但本例的MIPI DSI无器件属性文件,故基本成员结构与MipiDsiCntlr无太大差异。
+
+ ```c
+ typedef struct {
+ unsigned int devno; // 设备号
+ short laneId[LANE_MAX_NUM]; // Lane号
+ OutPutModeTag outputMode; // 输出模式选择:刷新模式,命令行模式或视频流模式
+ VideoModeTag videoMode; // 显示设备的同步模式
+ OutputFormatTag outputFormat; // 输出DSI图像数据格式:RGB或YUV
+ SyncInfoTag syncInfo; // 时序相关的设置
+ unsigned int phyDataRate; // 数据速率,单位Mbps
+ unsigned int pixelClk; // 时钟,单位KHz
+ } ComboDevCfgTag;
+
+ struct MipiDsiCntlr {
+ struct IDeviceIoService service;
+ struct HdfDeviceObject *device;
+ unsigned int devNo; // 设备号
+ struct MipiCfg cfg;
+ struct MipiDsiCntlrMethod *ops;
+ struct OsalMutex lock;
+ void *priv;
+ };
+ ```
+
+ - MipiDsiCntlr成员钩子函数结构体MipiDsiCntlrMethod的实例化。
+
+ ```c
+ static struct MipiDsiCntlrMethod g_method = {
+ .setCntlrCfg = Hi35xxSetCntlrCfg,
+ .setCmd = Hi35xxSetCmd,
+ .getCmd = Hi35xxGetCmd,
+ .toHs = Hi35xxToHs,
+ .toLp = Hi35xxToLp,
+ };
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态 (表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ MipiDsiCntlrMethod的实例化对象的挂载,调用MipiDsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。
+
+ ```c
+ static int32_t Hi35xxMipiTxInit(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ g_mipiTx.priv = NULL; // g_mipiTx是定义的全局变量
+ // static struct MipiDsiCntlr g_mipiTx {
+ // .devNo=0
+ // };
+ g_mipiTx.ops = &g_method; // MipiDsiCntlrMethod的实例化对象的挂载
+ ret = MipiDsiRegisterCntlr(&g_mipiTx, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量
+ ......
+ return MipiTxDrvInit(0); // 【必要】驱动适配者对设备的初始化,形式不限
+ }
+
+ // mipi_dsi_core.c核心层
+ int32_t MipiDsiRegisterCntlr(struct MipiDsiCntlr *cntlr, struct HdfDeviceObject *device)
+ {
+ ......
+ // 定义的全局变量:static struct MipiDsiHandle g_mipiDsihandle[MAX_CNTLR_CNT];
+ if (g_mipiDsihandle[cntlr->devNo].cntlr == NULL) {
+ (void)OsalMutexInit(&g_mipiDsihandle[cntlr->devNo].lock);
+ (void)OsalMutexInit(&(cntlr->lock));
+
+ g_mipiDsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiDsiHandle成员
+ g_mipiDsihandle[cntlr->devNo].priv = NULL;
+ cntlr->device = device; // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提
+ device->service = &(cntlr->service); // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提
+ cntlr->priv = NULL;
+ ......
+ return HDF_SUCCESS;
+ }
+ ......
+ return HDF_FAILURE;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 无效对象 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 无效参数 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 执行成功 |
- | HDF_FAILURE | 执行失败 |
+ ```c
+ static void Hi35xxMipiTxRelease(struct HdfDeviceObject *device)
+ {
+ struct MipiDsiCntlr *cntlr = NULL;
+ ......
+ cntlr = MipiDsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiDsiCntlr的强制转化
+ // return (device == NULL) ? NULL : (struct MipiDsiCntlr *)device->service;
+ ......
+ MipiTxDrvExit(); // 【必要】对设备所占资源的释放
+ MipiDsiUnregisterCntlr(&g_mipiTx); // 空函数
+ g_mipiTx.priv = NULL;
+ HDF_LOGI("%s: unload mipi_tx driver 1212!", __func__);
+ }
+ ```
- 函数说明:
-
- MipiDsiCntlrMethod的实例化对象的挂载,调用MipiDsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。
+4. 驱动调试
-
- ```c
- static int32_t Hi35xxMipiTxInit(struct HdfDeviceObject *device)
- {
- int32_t ret;
- g_mipiTx.priv = NULL; // g_mipiTx是定义的全局变量
- // static struct MipiDsiCntlr g_mipiTx {
- // .devNo=0
- //};
- g_mipiTx.ops = &g_method; // MipiDsiCntlrMethod的实例化对象的挂载
- ret = MipiDsiRegisterCntlr(&g_mipiTx, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量
- ...
- return MipiTxDrvInit(0); // 【必要】驱动适配者对设备的初始化,形式不限
- }
-
- /* mipi_dsi_core.c核心层 */
- int32_t MipiDsiRegisterCntlr(struct MipiDsiCntlr *cntlr, struct HdfDeviceObject *device)
- {
- ...
- /* 定义的全局变量:static struct MipiDsiHandle g_mipiDsihandle[MAX_CNTLR_CNT]; */
- if (g_mipiDsihandle[cntlr->devNo].cntlr == NULL) {
- (void)OsalMutexInit(&g_mipiDsihandle[cntlr->devNo].lock);
- (void)OsalMutexInit(&(cntlr->lock));
-
- g_mipiDsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiDsiHandle成员
- g_mipiDsihandle[cntlr->devNo].priv = NULL;
- cntlr->device = device; // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提
- device->service = &(cntlr->service); // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提
- cntlr->priv = NULL;
- ...
- return HDF_SUCCESS;
- }
- ...
- return HDF_FAILURE;
- }
- ```
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。
-
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
-
- ```c
- static void Hi35xxMipiTxRelease(struct HdfDeviceObject *device)
- {
- struct MipiDsiCntlr *cntlr = NULL;
- ...
- cntlr = MipiDsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiDsiCntlr的强制转化
- // return (device == NULL) ? NULL : (struct MipiDsiCntlr *)device->service;
- ...
- MipiTxDrvExit(); // 【必要】对设备所占资源的释放
- MipiDsiUnregisterCntlr(&g_mipiTx); // 空函数
- g_mipiTx.priv = NULL;
- HDF_LOGI("%s: unload mipi_tx driver 1212!", __func__);
- }
- ```
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈。
diff --git a/zh-cn/device-dev/driver/driver-platform-mmc-develop.md b/zh-cn/device-dev/driver/driver-platform-mmc-develop.md
index 956b94fdedb0126b0d2261a004a714f1d6c0f9aa..1529e91ded8531b1c5e32d9348b6ed82f7a8da85 100755
--- a/zh-cn/device-dev/driver/driver-platform-mmc-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-mmc-develop.md
@@ -14,11 +14,11 @@ MMC、SD、SDIO总线,其总线规范类似,都是从MMC总线规范演化
- SD卡(Secure Digital Memory Card)
- SD卡即安全数码卡。它是在MMC的基础上发展而来,SD卡强调数据的安全安全,可以设定存储内容的使用权限,防止数据被他人复制。在数据传输和物理规范上,SD卡(24mm\*32mm\*2.1mm,比MMC卡更厚一点),向前兼容了MMC卡。所有支持SD卡的设备也支持MMC卡。
+ SD卡即安全数码卡。它是在MMC的基础上发展而来,SD卡强调数据的安全安全,可以设定存储内容的使用权限,防止数据被他人复制。在数据传输和物理规范上,SD卡(24mm\*32mm\*2.1mm,比MMC卡更厚一点),向前兼容了MMC卡。所有支持SD卡的设备也支持MMC卡。
- SDIO(Secure Digital Input and Output)
- 即安全数字输入输出接口。SDIO是在SD规范的标准上定义的一种外设接口,它相较于SD规范增加了低速标准,可以用最小的硬件开销支持低速I/O。SDIO接口兼容以前的SD内存卡,也可以连接SDIO接口的设备。
+ 即安全数字输入输出接口。SDIO是在SD规范的标准上定义的一种外设接口,它相较于SD规范增加了低速标准,可以用最小的硬件开销支持低速I/O。SDIO接口兼容以前的SD内存卡,也可以连接SDIO接口的设备。
### 运作机制
@@ -27,17 +27,20 @@ MMC、SD、SDIO总线,其总线规范类似,都是从MMC总线规范演化
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
MMC模块各分层作用:
- 接口层提供打开MMC设备、检查MMC控制器是否存在设备、关闭MMC设备的接口。
+
- 核心层主要提供MMC控制器、移除和管理的能力,还有公共控制器业务。通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
-**图1** MMC独立服务模式结构图
+**图 1** MMC独立服务模式结构图
-![img1](figures/独立服务模式结构图.png "MMC独立服务模式结构图")
+![MMC独立服务模式结构图](figures/独立服务模式结构图.png)
## 开发指导
@@ -71,15 +74,15 @@ struct MmcCntlrOps {
};
```
-**表1** MmcCntlrOps结构体成员的钩子函数功能说明
+**表 1** MmcCntlrOps结构体成员的钩子函数功能说明
| 成员函数 | 入参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- |
| doRequest | cntlr:结构体指针,核心层MMC控制器
cmd:结构体指针,传入命令值 | HDF_STATUS相关状态 | request相应处理 |
-| setClock | cntlr:结构体指针,核心层MMC控制器
clock:时钟传入值 | HDF_STATUS相关状态 | 设置时钟频率 |
+| setClock | cntlr:结构体指针,核心层MMC控制器
clock:uint32_t类型,时钟传入值 | HDF_STATUS相关状态 | 设置时钟频率 |
| setPowerMode | cntlr:结构体指针,核心层MMC控制器
mode:枚举值(见MmcPowerMode定义),功耗模式 | HDF_STATUS相关状态 | 设置功耗模式 |
-| setBusWidth | cntlr:核心层结构体指针,核心层MMMC控制器
width:枚举值(见MmcBusWidth定义),总线带宽 | HDF_STATUS相关状态 | 设置总线带宽 |
-| setBusTiming | cntlr:结构体指针,核心层MMC控制器
timing:枚举值(见MmcBusTiming定义),总线时序 | HDF_STATUS相关状态 | 设置总线时序 |
+| setBusWidth | cntlr:核心层结构体指针,核心层MMMC控制器
width:枚举类型(见MmcBusWidth定义),总线带宽 | HDF_STATUS相关状态 | 设置总线带宽 |
+| setBusTiming | cntlr:结构体指针,核心层MMC控制器
timing:枚举类型(见MmcBusTiming定义),总线时序 | HDF_STATUS相关状态 | 设置总线时序 |
| setSdioIrq | cntlr:结构体指针,核心层MMC控制器
enable:布尔值,控制中断 | HDF_STATUS相关状态 | 使能/去使能SDIO中断 |
| hardwareReset | cntlr:结构体指针,核心层MMC控制器 | HDF_STATUS相关状态 | 复位硬件 |
| systemInit | cntlr:结构体指针,核心层MMC控制器 | HDF_STATUS相关状态 | 系统初始化 |
@@ -95,335 +98,351 @@ struct MmcCntlrOps {
MMC模块适配包含以下四个步骤:
-- 实例化驱动入口。
-- 配置属性文件。
-- 实例化MMC控制器对象。
-- 驱动调试。
+- 实例化驱动入口
+
+- 配置属性文件
+
+- 实例化MMC控制器对象
+
+- 驱动调试
### 开发实例
下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/mmc/himci_v200/himci.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
- MMC驱动入口开发参考:
-
- ```c
- struct HdfDriverEntry g_mmcDriverEntry = {
- .moduleVersion = 1,
- .Bind = HimciMmcBind, // 见Bind参考
- .Init = HimciMmcInit, // 见Init参考
- .Release = HimciMmcRelease, // 见Release参考
- .moduleName = "hi3516_mmc_driver", // 【必要且与HCS文件中里面的moduleName匹配】
- };
- HDF_INIT(g_mmcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
- ```
-
-2. 配置属性文件。
-
- 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以三个MMC控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层MmcCntlr成员的默认值或限制范围有密切关系,需要在mmc_config.hcs中配置器件属性。
-
- - device_info.hcs 配置参考:
-
- 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
-
- ```c
- root {
- device_info {
- match_attr = "hdf_manager";
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_mmc:: device {
- device0 :: deviceNode { // 驱动的DeviceNode节点
- policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2
- priority = 10; // 驱动启动优先级
- permission = 0644; // 驱动创建设备节点权限
- moduleName = "hi3516_mmc_driver"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。
- serviceName = "HDF_PLATFORM_MMC_0"; // 【必要】驱动对外发布服务的名称,必须唯一。
- deviceMatchAttr = "hi3516_mmc_emmc"; // 【必要】用于配置控制器私有数据,要与mmc_config.hcs中对应控制器保持一致。
- }
- device1 :: deviceNode {
- policy = 1;
- priority = 20;
- permission = 0644;
- moduleName = "hi3516_mmc_driver";
- serviceName = "HDF_PLATFORM_MMC_1";
- deviceMatchAttr = "hi3516_mmc_sd"; // SD类型
- }
- device2 :: deviceNode {
- policy = 1;
- priority = 30;
- permission = 0644;
- moduleName = "hi3516_mmc_driver";
- serviceName = "HDF_PLATFORM_MMC_2";
- deviceMatchAttr = "hi3516_mmc_sdio"; // SDIO类型
- }
- ...
- }
- }
- }
- }
- ```
-
- - mmc_config.hcs配置参考:
-
- 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs文件配置器件属性,其中配置参数如下:
-
- ```c
- root {
- platform {
- mmc_config {
- template mmc_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值。
- match_attr = "";
- voltDef = 0; // MMC默认电压,0代表3.3V,1代表1.8V,2代表1.2V
- freqMin = 50000; // 【必要】最小频率值
- freqMax = 100000000; // 【必要】最大频率值
- freqDef = 400000; // 【必要】默认频率值
- maxBlkNum = 2048; // 【必要】最大的block号
- maxBlkSize = 512; // 【必要】最大的block个数
- ocrDef = 0x300000; // 【必要】工作电压设置相关
- caps2 = 0; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps2定义。
- regSize = 0x118; // 【必要】寄存器位宽
- hostId = 0; // 【必要】主机号
- regBasePhy = 0x10020000; // 【必要】寄存器物理基地址
- irqNum = 63; // 【必要】中断号
- devType = 2; // 【必要】模式选择:EMMC、SD、SDIO、COMBO
- caps = 0x0001e045; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps定义。
- }
- controller_0x10100000 :: mmc_controller {
- match_attr = "hi3516_mmc_emmc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
- hostId = 0;
- regBasePhy = 0x10100000;
- irqNum = 96;
- devType = 0; // eMMC类型
- caps = 0xd001e045;
- caps2 = 0x60;
- }
- controller_0x100f0000 :: mmc_controller {
- match_attr = "hi3516_mmc_sd";
- hostId = 1;
- regBasePhy = 0x100f0000;
- irqNum = 62;
- devType = 1; // SD类型
- caps = 0xd001e005;
- }
- controller_0x10020000 :: mmc_controller {
- match_attr = "hi3516_mmc_sdio";
- hostId = 2;
- regBasePhy = 0x10020000;
- irqNum = 63;
- devType = 2; // SDIO类型
- caps = 0x0001e04d;
- }
- }
- }
- }
- ```
-
- 需要注意的是,新增mmc_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs" // 配置文件相对路径
- ```
-
-3. 实例化MMC控制器对象。
-
- 完成配置属性文件之后,下一步就是以核心层MmcCntlr对象的初始化为核心,包括驱动适配自定义结构体(传递参数和数据),实例化MmcCntlr成员MmcCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
-
- - 驱动适配者自定义结构体参考
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,而且mmc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。
-
- ```c
- struct HimciHost {
- struct MmcCntlr *mmc; // 【必要】核心层控制对象
- struct MmcCmd *cmd; // 【必要】核心层结构体,传递命令,相关命令见枚举量MmcCmdCode
- void *base; // 地址映射需要,寄存器基地址
- enum HimciPowerStatus powerStatus;
- uint8_t *alignedBuff;
- uint32_t buffLen;
- struct scatterlist dmaSg;
- struct scatterlist *sg;
- uint32_t dmaSgNum;
- DMA_ADDR_T dmaPaddr;
- uint32_t *dmaVaddr;
- uint32_t irqNum;
- bool isTuning;
- uint32_t id;
- struct OsalMutex mutex;
- bool waitForEvent;
- HIMCI_EVENT himciEvent;
- };
- // MmcCntlr是核心层控制器结构体,其中的成员在Bind函数中会被赋值。
- struct MmcCntlr {
- struct IDeviceIoService service;
- struct HdfDeviceObject *hdfDevObj;
- struct PlatformDevice device;
- struct OsalMutex mutex;
- struct OsalSem released;
- uint32_t devType;
- struct MmcDevice *curDev;
- struct MmcCntlrOps *ops;
- struct PlatformQueue *msgQueue;
- uint16_t index;
- uint16_t voltDef;
- uint32_t vddBit;
- uint32_t freqMin;
- uint32_t freqMax;
- uint32_t freqDef;
- union MmcOcr ocrDef;
- union MmcCaps caps;
- union MmcCaps2 caps2;
- uint32_t maxBlkNum;
- uint32_t maxBlkSize;
- uint32_t maxReqSize;
- bool devPlugged;
- bool detecting;
- void *priv;
- };
- ```
-
- - MmcCntlr成员钩子函数结构体MmcCntlrOps的实例化,其他成员在Bind函数中初始化。
-
- ```c
- static struct MmcCntlrOps g_himciHostOps = {
- .request = HimciDoRequest,
- .setClock = HimciSetClock,
- .setPowerMode = HimciSetPowerMode,
- .setBusWidth = HimciSetBusWidth,
- .setBusTiming = HimciSetBusTiming,
- .setSdioIrq = HimciSetSdioIrq,
- .hardwareReset = HimciHardwareReset,
- .systemInit = HimciSystemInit,
- .setEnhanceStrobe = HimciSetEnhanceStrobe,
- .switchVoltage = HimciSwitchVoltage,
- .devReadOnly = HimciDevReadOnly,
- .devPlugged = HimciCardPlugged,
- .devBusy = HimciDevBusy,
- .tune = HimciTune,
- .rescanSdioDev = HimciRescanSdioDev,
- };
- ```
-
- - Bind函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- **表2** Bind函数说明
-
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- MmcCntlr、HimciHost、HdfDeviceObject之间互相赋值,方便其他函数可以相互转化,初始化自定义结构体HimciHost对象,初始化MmcCntlr成员,调用核心层MmcCntlrAdd函数,完成MMC控制器的添加。
-
- ```c
- static int32_t HimciMmcBind(struct HdfDeviceObject *obj)
- {
- struct MmcCntlr *cntlr = NULL;
- struct HimciHost *host = NULL;
- int32_t ret;
- cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr));
- host = (struct HimciHost *)OsalMemCalloc(sizeof(struct HimciHost));
-
- host->mmc = cntlr; // 【必要】使HimciHost与MmcCntlr可以相互转化的前提
- cntlr->priv = (void *)host; // 【必要】使HimciHost与MmcCntlr可以相互转化的前提
- cntlr->ops = &g_himciHostOps; // 【必要】MmcCntlrOps的实例化对象的挂载
- cntlr->hdfDevObj = obj; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
- obj->service = &cntlr->service; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
- ret = MmcCntlrParse(cntlr, obj); // 【必要】 初始化cntlr,失败就goto _ERR。
- ...
- ret = HimciHostParse(host, obj); // 【必要】 初始化host对象的相关属性,失败就goto _ERR。
- ...
- ret = HimciHostInit(host, cntlr); // 驱动适配者自定义的初始化,失败就goto _ERR。
- ...
- ret = MmcCntlrAdd(cntlr); // 调用核心层函数,失败就goto _ERR。
- ...
- (void)MmcCntlrAddDetectMsgToQueue(cntlr); // 将卡检测消息添加到队列中。
- HDF_LOGD("HimciMmcBind: success.");
- return HDF_SUCCESS;
- ERR:
- HimciDeleteHost(host);
- HDF_LOGD("HimciMmcBind: fail, err = %d.", ret);
- return ret;
- }
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- HDF_STATUS相关状态。
-
- 函数说明:
-
- 实现ProcMciInit。
-
- ```c
- static int32_t HimciMmcInit(struct HdfDeviceObject *obj)
- {
- static bool procInit = false;
- (void)obj;
- if (procInit == false) {
- if (ProcMciInit() == HDF_SUCCESS) {
- procInit = true;
- HDF_LOGD("HimciMmcInit: proc init success.");
- }
- }
- HDF_LOGD("HimciMmcInit: success.");
- return HDF_SUCCESS;
- }
- ```
-
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 释放内存和删除控制器等操作,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
-
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
-
- ```c
- static void HimciMmcRelease(struct HdfDeviceObject *obj)
- {
- struct MmcCntlr *cntlr = NULL;
- ...
- cntlr = (struct MmcCntlr *)obj->service; // 这里有HdfDeviceObject到MmcCntlr的强制转化,通过service成员,赋值见Bind函数。
- ...
- HimciDeleteHost((struct HimciHost *)cntlr->priv); // 驱动适配者自定义的内存释放函数,这里有MmcCntlr到HimciHost的强制转化。
- }
- ```
-
-4. 驱动调试。
-
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
\ No newline at end of file
+1. 实例化驱动入口
+
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+
+ MMC驱动入口开发参考:
+
+ ```c
+ struct HdfDriverEntry g_mmcDriverEntry = {
+ .moduleVersion = 1,
+ .Bind = HimciMmcBind, // 见Bind参考
+ .Init = HimciMmcInit, // 见Init参考
+ .Release = HimciMmcRelease, // 见Release参考
+ .moduleName = "hi3516_mmc_driver", // 【必要且与HCS文件中里面的moduleName匹配】
+ };
+ HDF_INIT(g_mmcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
+
+2. 配置属性文件
+
+ 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以三个MMC控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在mmc_config.hcs文件中增加对应的器件属性。器件属性值与核心层MmcCntlr成员的默认值或限制范围有密切关系,需要在mmc_config.hcs中配置器件属性。
+
+ 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,MMC控制器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。MMC控制器控制器具体配置为10 |
+ | permission | 驱动创建设备节点权限,MMC控制器控制器具体配置为0664 |
+ | moduleName | 驱动名称,MMC控制器控制器固定为hi3516_mmc_driver |
+ | serviceName | 驱动对外发布服务的名称,MMC控制器控制器服务名设置为HDF_PLATFORM_MMC_X,X代表MMC控制器号|
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,MMC控制器控制器设置为hi3516_mmc_X,X代表控制器类型名 |
+
+ - device_info.hcs 配置参考:
+
+ 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
+
+ ```c
+ root {
+ device_info {
+ match_attr = "hdf_manager";
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_mmc:: device {
+ device0 :: deviceNode { // 驱动的DeviceNode节点
+ policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2
+ priority = 10; // 驱动启动优先级
+ permission = 0644; // 驱动创建设备节点权限
+ moduleName = "hi3516_mmc_driver"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。
+ serviceName = "HDF_PLATFORM_MMC_0"; // 【必要】驱动对外发布服务的名称,必须唯一。
+ deviceMatchAttr = "hi3516_mmc_emmc"; // 【必要】用于配置控制器私有数据,要与mmc_config.hcs中对应控制器保持一致。emmc类型。
+ }
+ device1 :: deviceNode {
+ policy = 1;
+ priority = 20;
+ permission = 0644;
+ moduleName = "hi3516_mmc_driver";
+ serviceName = "HDF_PLATFORM_MMC_1";
+ deviceMatchAttr = "hi3516_mmc_sd"; // SD类型
+ }
+ device2 :: deviceNode {
+ policy = 1;
+ priority = 30;
+ permission = 0644;
+ moduleName = "hi3516_mmc_driver";
+ serviceName = "HDF_PLATFORM_MMC_2";
+ deviceMatchAttr = "hi3516_mmc_sdio"; // SDIO类型
+ }
+ ......
+ }
+ }
+ }
+ }
+ ```
+
+ - mmc_config.hcs配置参考:
+
+ 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs文件配置器件属性,其中配置参数如下:
+
+ ```c
+ root {
+ platform {
+ mmc_config {
+ template mmc_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值。
+ match_attr = "";
+ voltDef = 0; // MMC默认电压,0代表3.3V,1代表1.8V,2代表1.2V
+ freqMin = 50000; // 【必要】最小频率值
+ freqMax = 100000000; // 【必要】最大频率值
+ freqDef = 400000; // 【必要】默认频率值
+ maxBlkNum = 2048; // 【必要】最大的block号
+ maxBlkSize = 512; // 【必要】最大block大小
+ ocrDef = 0x300000; // 【必要】工作电压设置相关
+ caps2 = 0; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps2定义。
+ regSize = 0x118; // 【必要】寄存器位宽
+ hostId = 0; // 【必要】主机号
+ regBasePhy = 0x10020000; // 【必要】寄存器物理基地址
+ irqNum = 63; // 【必要】中断号
+ devType = 2; // 【必要】模式选择:EMMC、SD、SDIO、COMBO
+ caps = 0x0001e045; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps定义。
+ }
+ controller_0x10100000 :: mmc_controller {
+ match_attr = "hi3516_mmc_emmc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
+ hostId = 0;
+ regBasePhy = 0x10100000;
+ irqNum = 96;
+ devType = 0; // eMMC类型
+ caps = 0xd001e045;
+ caps2 = 0x60;
+ }
+ controller_0x100f0000 :: mmc_controller {
+ match_attr = "hi3516_mmc_sd";
+ hostId = 1;
+ regBasePhy = 0x100f0000;
+ irqNum = 62;
+ devType = 1; // SD类型
+ caps = 0xd001e005;
+ }
+ controller_0x10020000 :: mmc_controller {
+ match_attr = "hi3516_mmc_sdio";
+ hostId = 2;
+ regBasePhy = 0x10020000;
+ irqNum = 63;
+ devType = 2; // SDIO类型
+ caps = 0x0001e04d;
+ }
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增mmc_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化MMC控制器对象
+
+ 完成配置属性文件之后,下一步就是以核心层MmcCntlr对象的初始化为核心,包括驱动适配自定义结构体(传递参数和数据),实例化MmcCntlr成员MmcCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+
+ - 驱动适配者自定义结构体参考
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,而且mmc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。
+
+ ```c
+ struct HimciHost {
+ struct MmcCntlr *mmc; // 【必要】核心层控制对象
+ struct MmcCmd *cmd; // 【必要】核心层结构体,传递命令,相关命令见枚举量MmcCmdCode
+ void *base; // 地址映射需要,寄存器基地址
+ enum HimciPowerStatus powerStatus;
+ uint8_t *alignedBuff;
+ uint32_t buffLen;
+ struct scatterlist dmaSg;
+ struct scatterlist *sg;
+ uint32_t dmaSgNum;
+ DMA_ADDR_T dmaPaddr;
+ uint32_t *dmaVaddr;
+ uint32_t irqNum;
+ bool isTuning;
+ uint32_t id;
+ struct OsalMutex mutex;
+ bool waitForEvent;
+ HIMCI_EVENT himciEvent;
+ };
+ // MmcCntlr是核心层控制器结构体,其中的成员在Bind函数中会被赋值。
+ struct MmcCntlr {
+ struct IDeviceIoService service;
+ struct HdfDeviceObject *hdfDevObj;
+ struct PlatformDevice device;
+ struct OsalMutex mutex;
+ struct OsalSem released;
+ uint32_t devType;
+ struct MmcDevice *curDev;
+ struct MmcCntlrOps *ops;
+ struct PlatformQueue *msgQueue;
+ uint16_t index;
+ uint16_t voltDef;
+ uint32_t vddBit;
+ uint32_t freqMin;
+ uint32_t freqMax;
+ uint32_t freqDef;
+ union MmcOcr ocrDef;
+ union MmcCaps caps;
+ union MmcCaps2 caps2;
+ uint32_t maxBlkNum;
+ uint32_t maxBlkSize;
+ uint32_t maxReqSize;
+ bool devPlugged;
+ bool detecting;
+ void *priv;
+ };
+ ```
+
+ - MmcCntlr成员钩子函数结构体MmcCntlrOps的实例化。
+
+ ```c
+ static struct MmcCntlrOps g_himciHostOps = {
+ .request = HimciDoRequest,
+ .setClock = HimciSetClock,
+ .setPowerMode = HimciSetPowerMode,
+ .setBusWidth = HimciSetBusWidth,
+ .setBusTiming = HimciSetBusTiming,
+ .setSdioIrq = HimciSetSdioIrq,
+ .hardwareReset = HimciHardwareReset,
+ .systemInit = HimciSystemInit,
+ .setEnhanceStrobe = HimciSetEnhanceStrobe,
+ .switchVoltage = HimciSwitchVoltage,
+ .devReadOnly = HimciDevReadOnly,
+ .devPlugged = HimciCardPlugged,
+ .devBusy = HimciDevBusy,
+ .tune = HimciTune,
+ .rescanSdioDev = HimciRescanSdioDev,
+ };
+ ```
+
+ - Bind函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+ MmcCntlr、HimciHost、HdfDeviceObject之间互相赋值,方便其他函数可以相互转化,初始化自定义结构体HimciHost对象,初始化MmcCntlr成员,调用核心层MmcCntlrAdd函数,完成MMC控制器的添加。
+
+ ```c
+ static int32_t HimciMmcBind(struct HdfDeviceObject *obj)
+ {
+ struct MmcCntlr *cntlr = NULL;
+ struct HimciHost *host = NULL;
+ int32_t ret;
+ cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr));
+ host = (struct HimciHost *)OsalMemCalloc(sizeof(struct HimciHost));
+
+ host->mmc = cntlr; // 【必要】使HimciHost与MmcCntlr可以相互转化的前提
+ cntlr->priv = (void *)host; // 【必要】使HimciHost与MmcCntlr可以相互转化的前提
+ cntlr->ops = &g_himciHostOps; // 【必要】MmcCntlrOps的实例化对象的挂载
+ cntlr->hdfDevObj = obj; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
+ obj->service = &cntlr->service; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
+ ret = MmcCntlrParse(cntlr, obj); // 【必要】 初始化cntlr,失败就goto _ERR。
+ ......
+ ret = HimciHostParse(host, obj); // 【必要】 初始化host对象的相关属性,失败就goto _ERR。
+ ......
+ ret = HimciHostInit(host, cntlr); // 驱动适配者自定义的初始化,失败就goto _ERR。
+ ......
+ ret = MmcCntlrAdd(cntlr); // 调用核心层函数,失败就goto _ERR。
+ ......
+ (void)MmcCntlrAddDetectMsgToQueue(cntlr); // 将卡检测消息添加到队列中。
+ HDF_LOGD("HimciMmcBind: success.");
+ return HDF_SUCCESS;
+ ERR:
+ HimciDeleteHost(host);
+ HDF_LOGD("HimciMmcBind: fail, err = %d.", ret);
+ return ret;
+ }
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ HDF_STATUS相关状态。
+
+ 函数说明:
+
+ 实现ProcMciInit。
+
+ ```c
+ static int32_t HimciMmcInit(struct HdfDeviceObject *obj)
+ {
+ static bool procInit = false;
+ (void)obj;
+ if (procInit == false) {
+ if (ProcMciInit() == HDF_SUCCESS) {
+ procInit = true;
+ HDF_LOGD("HimciMmcInit: proc init success.");
+ }
+ }
+ HDF_LOGD("HimciMmcInit: success.");
+ return HDF_SUCCESS;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 释放内存和删除控制器等操作,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
+
+ ```c
+ static void HimciMmcRelease(struct HdfDeviceObject *obj)
+ {
+ struct MmcCntlr *cntlr = NULL;
+ ......
+ cntlr = (struct MmcCntlr *)obj->service; // 这里有HdfDeviceObject到MmcCntlr的强制转化,通过service成员,赋值见Bind函数。
+ ......
+ HimciDeleteHost((struct HimciHost *)cntlr->priv); // 驱动适配者自定义的内存释放函数,这里有MmcCntlr到HimciHost的强制转化。
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据读写成功与否等。
diff --git a/zh-cn/device-dev/driver/driver-platform-pin-des.md b/zh-cn/device-dev/driver/driver-platform-pin-des.md
index aaa3ee8e27dddec654de040ada8e910ad73b394a..9339aab419f8ef5f23980081c48ff43e17f0d90d 100644
--- a/zh-cn/device-dev/driver/driver-platform-pin-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-pin-des.md
@@ -9,8 +9,11 @@ PIN即管脚控制器,用于统一管理各SoC的管脚资源,对外提供
PIN接口定义了操作PIN管脚的通用方法集合,包括:
- 获取/释放管脚描述句柄:传入管脚名与链表中每个控制器下管脚名进行匹配,匹配则会获取一个管脚描述句柄,操作完PIN管脚后释放该管脚描述句柄。
+
- 设置/获取管脚推拉方式:推拉方式可以是上拉、下拉以及悬空。
+
- 设置/获取管脚推拉强度:用户可根据实际设置管脚推拉强度大小。
+
- 设置/获取管脚功能:通过管脚功能名设置/获取管脚功能,实现管脚复用。
### 基本概念
@@ -19,11 +22,11 @@ PIN是一个软件层面的概念,目的是为了统一各SoC的PIN管脚管
- SoC(System on Chip)
- 系统级芯片,也有称作片上系统,通常是面向特定用途将微处理器、模拟IP核、数字IP核和存储器集成在单一芯片的标准产品。
+ 系统级芯片,也有称作片上系统,通常是面向特定用途将微处理器、模拟IP核、数字IP核和存储器集成在单一芯片的标准产品。
- 管脚复用
- 由于芯片自身的引脚数量有限,无法满足日益增多的外接需求。此时可以通过软件层面的寄存器设置,让引脚工作在不同的状态,从而实现相同引脚完成不同功能的目的。
+ 由于芯片自身的引脚数量有限,无法满足日益增多的外接需求。此时可以通过软件层面的寄存器设置,让引脚工作在不同的状态,从而实现相同引脚完成不同功能的目的。
### 运作机制
@@ -34,11 +37,13 @@ PIN是一个软件层面的概念,目的是为了统一各SoC的PIN管脚管
PIN模块各分层作用:
- 接口层提供获取PIN管脚、设置PIN管脚推拉方式、获取PIN管脚推拉方式、设置PIN管脚推拉强度、获取PIN管脚推拉强度、设置PIN管脚功能、获取PIN管脚功能、释放PIN管脚的接口。
+
- 核心层主要提供PIN管脚资源匹配,PIN管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图 1** PIN统一服务模式
-![PIN统一服务模式](figures/统一服务模式结构图.png "统一服务模式")
+![PIN统一服务模式](figures/统一服务模式结构图.png)
### 约束与限制
@@ -52,21 +57,21 @@ PIN模块仅是一个软件层面的概念,主要工作是管脚资源管理
### 接口说明
-PIN模块提供的主要接口如表1所示,更多关于接口的介绍请参考对应的API接口文档。
+PIN模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/pin_if.h。
**表 1** PIN驱动API接口功能介绍
-| **接口名** | **描述** |
+| **接口名** | **描述** |
| ------------------------------------------------------------ | ---------------- |
-| DevHandle PinGet(const char *pinName) | 获取管脚描述句柄 |
-| void PinPut(DevHandle handle) | 释放管脚描述句柄 |
+| DevHandle PinGet(const char \*pinName) | 获取管脚描述句柄 |
+| void PinPut(DevHandle handle) | 释放管脚描述句柄 |
| int32_t PinSetPull(DevHandle handle, enum PinPullType pullType) | 设置管脚推拉方式 |
-| int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType) | 获取管脚推拉方式 |
+| int32_t PinGetPull(DevHandle handle, enum PinPullType \*pullType) | 获取管脚推拉方式 |
| int32_t PinSetStrength(DevHandle handle, uint32_t strength) | 设置管脚推拉强度 |
-| int32_t PinGetStrength(DevHandle handle, uint32_t *strength) | 获取管脚推拉强度 |
-| int32_t PinSetFunc(DevHandle handle, const char *funcName) | 设置管脚功能 |
-| int32_t PinGetFunc(DevHandle handle, const char **funcName) | 获取管脚功能 |
+| int32_t PinGetStrength(DevHandle handle, uint32_t \*strength) | 获取管脚推拉强度 |
+| int32_t PinSetFunc(DevHandle handle, const char \*funcName) | 设置管脚功能 |
+| int32_t PinGetFunc(DevHandle handle, const char \**funcName) | 获取管脚功能 |
>![](../public_sys-resources/icon-note.gif) **说明:**
>本文涉及PIN的所有接口,支持内核态及用户态使用。
@@ -75,8 +80,8 @@ PIN模块提供的主要接口如表1所示,更多关于接口的介绍请参
使用PIN设备的一般流程如图2所示。
- **图 2** PIN使用流程图
-![](figures/PIN使用流程图.png "PIN使用流程图")
+**图 2** PIN使用流程图
+![PIN使用流程图](figures/PIN使用流程图.png)
#### 获取PIN管脚描述句柄
@@ -90,12 +95,12 @@ DevHandle PinGet(const char *pinName);
-| 参数 | 描述 |
+| 参数 | 描述 |
| ---------- | ----------------------- |
-| pinName | 管脚名 |
-| **返回值** | **描述** |
-| NULL | 获取PIN管脚描述句柄失败 |
-| handle | PIN管脚描述句柄 |
+| pinName | 字符指针类型,管脚名 |
+| **返回值** | **描述** |
+| NULL | 获取PIN管脚描述句柄失败 |
+| handle | PIN管脚描述句柄 |
假设PIN需要操作的管脚名为P18,获取其管脚描述句柄的示例如下:
@@ -106,7 +111,7 @@ char pinName = "P18"; // PIN管脚名
handle = PinGet(pinName);
if (handle == NULL) {
HDF_LOGE("PinGet: get handle failed!\n");
- return;
+ return HDF_FAILURE;
}
```
@@ -122,13 +127,13 @@ int32_t PinSetPull(DevHandle handle, enum PinPullType pullType);
-| 参数 | 描述 |
+| 参数 | 描述 |
| ---------- | ----------------------- |
-| handle | PIN管脚描述句柄 |
-| pullType | PIN管脚推拉方式 |
+| handle | DevHandle类型,PIN管脚描述句柄 |
+| pullType | 枚举类型,PIN管脚推拉方式 |
| **返回值** | **描述** |
-| 0 | PIN设置管脚推拉方式成功 |
-| 负数 | PIN设置管脚推拉方式失败 |
+| HDF_SUCCESS | PIN设置管脚推拉方式成功 |
+| 负数 | PIN设置管脚推拉方式失败 |
假设PIN要设置的管脚推拉方式为上拉,其实例如下:
@@ -136,7 +141,7 @@ int32_t PinSetPull(DevHandle handle, enum PinPullType pullType);
int32_t ret;
enum PinPullType pullTypeNum;
-/* PIN设置管脚推拉方式 */
+// PIN设置管脚推拉方式
pullTypeNum = 1;
ret = PinSetPull(handle, pullTypeNum);
if (ret != HDF_SUCCESS) {
@@ -157,13 +162,13 @@ int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType);
-| 参数 | 描述 |
+| 参数 | 描述 |
| ---------- | ------------------------- |
-| handle | PIN管脚描述句柄 |
-| pullType | 接收PIN管脚推拉方式的指针 |
+| handle | DevHandle类型,PIN管脚描述句柄 |
+| pullType | 枚举类型指针,接收PIN管脚推拉方式 |
| **返回值** | **描述** |
-| 0 | PIN获取管脚推拉方式成功 |
-| 负数 | PIN获取管脚推拉方式失败 |
+| HDF_SUCCESS | PIN获取管脚推拉方式成功 |
+| 负数 | PIN获取管脚推拉方式失败 |
PIN获取管脚推拉方式的实例如下:
@@ -171,7 +176,7 @@ PIN获取管脚推拉方式的实例如下:
int32_t ret;
enum PinPullType pullTypeNum;
-/* PIN获取管脚推拉方式 */
+// PIN获取管脚推拉方式
ret = PinGetPull(handle, &pullTypeNum);
if (ret != HDF_SUCCESS) {
HDF_LOGE("PinGetPull: failed, ret %d\n", ret);
@@ -191,20 +196,20 @@ int32_t PinSetStrength(DevHandle handle, uint32_t strength);
-| 参数 | 描述 |
+| 参数 | 描述 |
| ---------- | ----------------------- |
-| handle | 管脚描述句柄 |
-| strength | PIN管脚推拉强度 |
+| handle | DevHandle类型,管脚描述句柄 |
+| strength | uint32_t类型,PIN管脚推拉强度 |
| **返回值** | **描述** |
-| 0 | PIN设置管脚推拉强度成功 |
-| 负数 | PIN设置管脚推拉强度失败 |
+| HDF_SUCCESS | PIN设置管脚推拉强度成功 |
+| 负数 | PIN设置管脚推拉强度失败 |
假设PIN要设置的管脚推拉强度为2,其实例如下:
```c
int32_t ret;
uint32_t strengthNum;
-/* PIN设置管脚推拉强度 */
+// PIN设置管脚推拉强度
strengthNum = 2;
ret = PinSetStrength(handle, strengthNum);
if (ret != HDF_SUCCESS) {
@@ -225,13 +230,13 @@ int32_t PinGetStrength(DevHandle handle, uint32_t *strength);
-| 参数 | 描述 |
+| 参数 | 描述 |
| ---------- | ------------------------- |
-| handle | 管脚描述句柄 |
-| strength | 接收PIN管脚推拉强度的指针 |
+| handle | DevHandle类型,管脚描述句柄 |
+| strength | uint32_t类型指针,接收PIN管脚推拉强度
| **返回值** | **描述** |
-| 0 | PIN获取管脚推拉强度成功 |
-| 负数 | PIN获取管脚推拉强度失败 |
+| HDF_SUCCESS | PIN获取管脚推拉强度成功 |
+| 负数 | PIN获取管脚推拉强度失败 |
PIN获取管脚推拉强度的实例如下:
@@ -239,7 +244,7 @@ PIN获取管脚推拉强度的实例如下:
int32_t ret;
uint32_t strengthNum;
-/* PIN获取管脚推拉强度 */
+// PIN获取管脚推拉强度
ret = PinGetStrength(handle, &strengthNum);
if (ret != HDF_SUCCESS) {
HDF_LOGE("PinGetStrength: failed, ret %d\n", ret);
@@ -261,13 +266,13 @@ int32_t PinSetFunc(DevHandle handle, const char *funcName);
-| 参数 | 描述 |
+| 参数 | 描述 |
| ---------- | ------------------- |
-| handle | 管脚描述句柄 |
-| funcName | PIN管脚功能名 |
+| handle | DevHandle类型,管脚描述句柄 |
+| funcName | 字符指针类型,PIN管脚功能名 |
| **返回值** | **描述** |
-| 0 | PIN设置管脚功能成功 |
-| 负数 | PIN设置管脚功能失败 |
+| HDF_SUCCESS | PIN设置管脚功能成功 |
+| 负数 | PIN设置管脚功能失败 |
假设PIN需要设置的管脚功能为LSADC_CH1(ADC通道1),其实例如下:
@@ -275,7 +280,7 @@ int32_t PinSetFunc(DevHandle handle, const char *funcName);
int32_t ret;
char funcName = "LSADC_CH1";
-/* PIN设置管脚功能 */
+// PIN设置管脚功能
ret = PinSetFunc(handle, funcName);
if (ret != HDF_SUCCESS) {
HDF_LOGE("PinSetFunc: failed, ret %d\n", ret);
@@ -295,13 +300,13 @@ int32_t PinGetFunc(DevHandle handle, const char **funcName);
-| 参数 | 描述 |
+| 参数 | 描述 |
| ---------- | --------------------- |
-| handle | 管脚描述句柄 |
-| funcName | 接收PIN管脚功能名指针 |
+| handle | DevHandle类型,管脚描述句柄 |
+| funcName | 字符类型双指针,接收PIN管脚功能 |
| **返回值** | **描述** |
-| 0 | PIN获取管脚功能成功 |
-| 负数 | PIN获取管脚功能失败 |
+| HDF_SUCCESS | PIN获取管脚功能成功 |
+| 负数 | PIN获取管脚功能失败 |
PIN获取管脚功能的实例如下:
@@ -309,7 +314,7 @@ PIN获取管脚功能的实例如下:
int32_t ret;
char *funcName = NULL;
-/* PIN获取管脚功能 */
+// PIN获取管脚功能
ret = PinGetFunc(handle, &funcName);
if (ret != HDF_SUCCESS) {
HDF_LOGE("PinGetFunc: failed, ret %d\n", ret);
@@ -329,11 +334,11 @@ void PinPut(DevHandle handle);
-| 参数 | 描述 |
+| 参数 | 描述 |
| ---------- | -------------- |
-| handle | 管脚描述句柄 |
+| handle | DevHandle类型,管脚描述句柄 |
| **返回值** | **描述** |
-| NA | 无返回值 |
+| NA | 无返回值 |
PIN销毁管脚描述句柄实例如下:
@@ -346,76 +351,84 @@ PinPut(handle);
下面将基于Hi3516DV300开发板展示使用PIN设置管脚相关属性完整操作,步骤主要如下:
1. 传入要设置的管脚名,获取PIN管脚描述句柄。
+
2. 通过PIN管脚描述句柄以及推拉方式pullTypeNum设置管脚推拉方式,如果操作失败则释放PIN管脚描述句柄。
+
3. 通过PIN管脚描述句柄,并用pullTypeNum承接获取的管脚推拉方式,如果操作失败则释放PIN管脚描述句柄。
+
4. 通过PIN管脚描述句柄以及推拉强度strengthNum设置管脚推拉强度,如果操作失败则释放PIN管脚描述句柄。
+
5. 通过PIN管脚描述句柄,并用strengthNum承接获取的管脚推拉强度,如果操作失败则释放PIN管脚描述句柄。
+
5. 通过PIN管脚描述句柄以及管脚功能名funName设置管脚功能,如果操作失败则释放PIN管脚描述句柄。
+
6. 通过PIN管脚描述句柄,并用funName承接获取的管脚功能名,如果操作失败则释放PIN管脚描述句柄。
+
7. 使用完PIN后,不再对管脚进行操作,释放PIN管脚描述句柄。
```c
-#include "hdf_log.h" /* 标准日志打印头文件 */
-#include "pin_if.h" /* PIN标准接口头文件 */
+#include "hdf_log.h" // 标准日志打印头文件
+#include "pin_if.h" // PIN标准接口头文件
int32_t PinTestSample(void)
{
int32_t ret;
uint32_t strengthNum;
enum PinPullType pullTypeNum;
- char pinName;
- char *funName;
+ char *pinName = NULL;
+ const char *funcName = NULL;
DevHandle handle = NULL;
- /* PIN管脚名,要填写实际要设置的管脚名 */
+ // PIN管脚名,要填写实际要设置的管脚名
pinName = "P18";
- /* PIN获取管脚描述句柄 */
+ // PIN获取管脚描述句柄
handle = PinGet(pinName);
if (handle == NULL) {
- HDF_LOGE("PinGet: pin get failed!\n");
- return;
+ HDF_LOGE("PinTestSample: pin get fail!\n");
+ return -1;
}
- /* PIN设置管脚推拉方式为上拉 */
+ // PIN设置管脚推拉方式为上拉
pullTypeNum = 1;
ret = PinSetPull(handle, pullTypeNum);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PinSetPull: failed, ret %d\n", ret);
+ HDF_LOGE("PinTestSample: pin set pull fail, ret:%d\n", ret);
goto ERR;
}
- /* PIN获取管脚推拉方式 */
+ // PIN获取管脚推拉方式
ret = PinGetPull(handle, &pullTypeNum);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PinGetPull: failed, ret %d\n", ret);
+ HDF_LOGE("PinTestSample: pin get pull fail, ret:%d\n", ret);
goto ERR;
}
- /* PIN设置管脚推拉强度为2 */
+ // PIN设置管脚推拉强度为2
strengthNum = 2;
ret = PinSetStrength(handle, strengthNum);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PinSetStrength: failed, ret %d\n", ret);
+ HDF_LOGE("PinTestSample: pin set strength fail, ret:%d\n", ret);
goto ERR;
}
- /* PIN获取管脚推拉强度 */
+ // PIN获取管脚推拉强度
ret = PinGetStrength(handle, &strengthNum);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PinGetStrength: failed, ret %d\n", ret);
+ HDF_LOGE("PinTestSample: pin get strength fail, ret:%d\n", ret);
goto ERR;
}
- /* PIN设置管脚功能为LSADC_CH1 */
- funName = "LSADC_CH1";
- ret = PinSetFunc(handle, funName);
+ // PIN设置管脚功能为LSADC_CH1
+ funcName = "LSADC_CH1";
+ ret = PinSetFunc(handle, funcName);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PinSetFunc: failed, ret %d\n", ret);
+ HDF_LOGE("PinTestSample: pin set func fail, ret:%d\n", ret);
goto ERR;
}
- /* PIN获取管脚功能 */
+ // PIN获取管脚功能
ret = PinGetFunc(handle, &funcName);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PinGetFunc: failed, ret %d\n", ret);
+ HDF_LOGE("PinTestSample: pin get func fail, ret:%d\n", ret);
goto ERR;
}
+ HDF_LOGD("PinTestSample: function tests end.", __func__);
ERR:
- /* 释放PIN管脚描述句柄 */
+ // 释放PIN管脚描述句柄
PinPut(handle);
return ret;
}
diff --git a/zh-cn/device-dev/driver/driver-platform-pin-develop.md b/zh-cn/device-dev/driver/driver-platform-pin-develop.md
index 00ae1b6b003d658265815ed4535c47845aa03a67..ba863806cf7d2a00cca4b005eb536911b3f7b841 100755
--- a/zh-cn/device-dev/driver/driver-platform-pin-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-pin-develop.md
@@ -12,11 +12,11 @@ PIN是一个软件层面的概念,目的是为了统一对各SoC的PIN管脚
- SoC(System on Chip)
- 系统级芯片,又称作片上系统,通常是面向特定用途将微处理器、模拟IP核、数字IP核和存储器集成在单一芯片的标准产品。
+ 系统级芯片,又称作片上系统,通常是面向特定用途将微处理器、模拟IP核、数字IP核和存储器集成在单一芯片的标准产品。
- 管脚复用
- 由于芯片自身的引脚数量有限,无法满足日益增多的外接需求。此时可以通过软件层面的寄存器设置,让引脚工作在不同的状态,从而实现相同引脚完成不同功能的目的。
+ 由于芯片自身的引脚数量有限,无法满足日益增多的外接需求。此时可以通过软件层面的寄存器设置,让引脚工作在不同的状态,从而实现相同引脚完成不同功能的目的。
### 运作机制
@@ -27,7 +27,9 @@ PIN是一个软件层面的概念,目的是为了统一对各SoC的PIN管脚
PIN模块各分层作用:
- 接口层提供获取PIN管脚、设置PIN管脚推拉方式、获取PIN管脚推拉方式、设置PIN管脚推拉强度、获取PIN管脚推拉强度、设置PIN管脚功能、获取PIN管脚功能、释放PIN管脚的接口。
+
- 核心层主要提供PIN管脚资源匹配,PIN管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图 1** 统一服务模式结构图
@@ -63,9 +65,9 @@ struct PinCntlrMethod {
**表 1** PinCntlrMethod成员的钩子函数功能说明
-| 成员函数 | 入参 | 出参 | 返回值 | 功能 |
+| 成员函数 | 入参 | 出参 | 返回值 | 功能 |
| ------------ | ------------------------------------------- | ------ | ---- | ---- |
-| SetPinPull | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号
pullType:枚举常量,PIN管脚推拉方式 | 无 |HDF_STATUS相关状态|PIN设置管脚推拉方式|
+| SetPinPull | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号
pullType:枚举常量,PIN管脚推拉方式 | 无 |HDF_STATUS相关状态|PIN设置管脚推拉方式 |
| GetPinPull | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号 | pullType:枚举常量指针,传出获取的PIN管脚推拉方式 | HDF_STATUS相关状态 | PIN获取管脚推拉方式 |
| SetPinStrength | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号
strength:uint32_t变量,PIN推拉强度 | 无 | HDF_STATUS相关状态 | PIN设置推拉强度 |
| GetPinStrength | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号 | strength:uint32_t变量指针,传出获取的PIN推拉强度 | HDF_STATUS相关状态 | PIN获取推拉强度 |
@@ -76,395 +78,414 @@ struct PinCntlrMethod {
PIN模块适配HDF框架包含以下四个步骤:
-- 实例化驱动入口。
-- 配置属性文件。
-- 实例化PIN控制器对象。
-- 驱动调试。
+- 实例化驱动入口
+
+- 配置属性文件
+
+- 实例化PIN控制器对象
+
+- 驱动调试
### 开发实例
下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/pin/pin_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 实例化驱动入口。
+1. 实例化驱动入口
+
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
- PIN驱动入口开发参考:
+ PIN驱动入口开发参考:
- ```c
- static struct HdfDriverEntry g_hi35xxPinDriverEntry = {
- .moduleVersion = 1,
- .Bind = Hi35xxPinBind,
- .Init = Hi35xxPinInit,
- .Release = Hi35xxPinRelease,
- .moduleName = "hi35xx_pin_driver", // 【必要且与HCS文件中里面的moduleName匹配】
- };
- HDF_INIT(g_hi35xxPinDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```c
+ static struct HdfDriverEntry g_hi35xxPinDriverEntry = {
+ .moduleVersion = 1,
+ .Bind = Hi35xxPinBind, // PIN不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作
+ .Init = Hi35xxPinInit, // 挂接PIN模块Init实例化
+ .Release = Hi35xxPinRelease, // 挂接PIN模块Release实例化
+ .moduleName = "hi35xx_pin_driver", // 【必要且与HCS文件中里面的moduleName匹配】
+ };
+ HDF_INIT(g_hi35xxPinDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
```
-2. 配置属性文件。
-
- 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个PIN控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值对于驱动适配者的驱动实现以及核心层PinCntlr相关成员的默认值或限制范围有密切关系,比如控制器号,控制器管脚数量、管脚等。需要在pin_config.hcs中配置器件属性。
-
- - device_info.hcs 配置参考:
-
- 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
-
- ```c
- root {
- device_info {
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_pin :: device {
- device0 :: deviceNode { // 用于统一管理PIN并发布服务
- policy = 2; // 2:用户态可见;1:内核态可见;0:不需要发布服务。
- priority = 8; // 启动优先级
- permission = 0644; // 创建设备节点权限
- moduleName = "HDF_PLATFORM_PIN_MANAGER";
- serviceName = "HDF_PLATFORM_PIN_MANAGER";
- }
- device1 :: deviceNode { // 为每一个PIN控制器配置一个HDF设备节点,存在多个时必须添加,否则不用。
- policy = 0;
- priority = 10; // 驱动启动优先级
- permission = 0644; // 驱动创建设备节点权限
- moduleName = "hi35xx_pin_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
- deviceMatchAttr = "hisilicon_hi35xx_pin_0"; // 【必要】用于配置控制器私有数据,要与pin_config.hcs中对应控制器保持一致,具体的控制器信息在pin_config.hcs中。
- }
- device2 :: deviceNode {
- policy = 0;
- priority = 10;
- permission = 0644;
- moduleName = "hi35xx_pin_driver";
- deviceMatchAttr = "hisilicon_hi35xx_pin_1";
- }
- ...
- }
- }
- }
- }
- ```
-
- - pin_config.hcs配置参考:
-
- 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs文件配置器件属性,其中配置参数如下:
-
- ```c
- root {
- platform {
- pin_config_hi35xx {
- template pin_controller { // 【必要】配置模板配,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值。
- number = 0; // 【必要】PIN控制器号
- regStartBasePhy = 0; // 【必要】寄存器物理基地址起始地址
- regSize = 0; // 【必要】寄存器位宽
- pinCount = 0; // 【必要】管脚数量
- match_attr = "";
- template pin_desc {
- pinName = ""; // 【必要】管脚名称
- init = 0; // 【必要】寄存器默认值
- F0 = ""; // 【必要】功能0
- F1 = ""; // 功能1
- F2 = ""; // 功能2
- F3 = ""; // 功能3
- F4 = ""; // 功能4
- F5 = ""; // 功能5
- }
- }
- controller_0 :: pin_controller {
- number = 0;
- regStartBasePhy = 0x10FF0000;
- regSize = 0x48;
- pinCount = 18;
- match_attr = "hisilicon_hi35xx_pin_0";
- T1 :: pin_desc {
- pinName = "T1";
- init = 0x0600;
- F0 = "EMMC_CLK";
- F1 = "SFC_CLK";
- F2 = "SFC_BOOT_MODE";
- }
- ... // 对应管脚控制器下的每个管脚,按实际添加。
- }
- ... // 每个管脚控制器对应一个控制器节点,如存在多个PIN控制器,请依次添加对应的控制器节点。
- }
- }
- }
- ```
-
- 需要注意的是,新增pin_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs" // 配置文件相对路径
- ```
-
-3. 实例化PIN控制器对象。
-
- 完成配置属性文件之后,下一步就是以核心层PinCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化PinCntlr成员PinCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
-
- - 驱动适配者自定义结构体参考
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,而且pin_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。
-
- 在Hi35xxPinCntlrInit函数中对PinCntlr成员进行初始化操作。
-
- ```c
- // 驱动适配者自定义管脚描述结构体
- struct Hi35xxPinDesc {
- const char *pinName; // 管脚名
- uint32_t init; // 初始化值
- uint32_t index; // 管脚索引
- int32_t pullType; // 管脚推拉方式
- int32_t strength; // 管脚推拉强度
- const char *func[HI35XX_PIN_FUNC_MAX]; // 管脚功能名字符串数组
- };
-
- // 驱动适配者自定义结构体
- struct Hi35xxPinCntlr {
- struct PinCntlr cntlr; // 是核心层控制对象,具体描述见下面
- struct Hi35xxPinDesc *desc; // 驱动适配者自定义管脚描述结构体指针
- volatile unsigned char *regBase; // 寄存器映射地址
- uint16_t number; // 管脚控制器编号
- uint32_t regStartBasePhy; // 寄存器物理基地址起始地址
- uint32_t regSize; // 寄存器位宽
- uint32_t pinCount; // 管脚数量
- };
-
- // PinCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
- struct PinCntlr {
- struct IDeviceIoService service; // 驱动服务
- struct HdfDeviceObject *device; // 驱动设备对象
- struct PinCntlrMethod *method; // 钩子函数
- struct DListHead node; // 链表节点
- OsalSpinlock spin; // 自旋锁
- uint16_t number; // 管脚控制器编号
- uint16_t pinCount; // 管脚数量
- struct PinDesc *pins; // 管脚资源
- void *priv; // 私有数据
- };
-
- // PIN管脚控制器初始化
- static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx)
- {
- struct DeviceResourceIface *drsOps = NULL;
- int32_t ret;
- // 从hcs文件读取管脚控制器相关属性
- drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
- HDF_LOGE("%s: invalid drs ops fail!", __func__);
- return HDF_FAILURE;
- }
- ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read number failed", __func__);
- return ret;
- }
-
- if (hi35xx->number > HI35XX_PIN_MAX_NUMBER) {
- HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number);
- return HDF_ERR_INVALID_PARAM;
- }
- ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read regStartBasePhy failed", __func__);
- return ret;
- }
- ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read regSize failed", __func__);
- return ret;
- }
- ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read pinCount failed", __func__);
- return ret;
- }
- if (hi35xx->pinCount > PIN_MAX_CNT_PER_CNTLR) {
- HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number);
- return HDF_ERR_INVALID_PARAM;
- }
- // 将读取的值赋值给管脚控制器的成员,完成管脚控制器初始化。
- hi35xx->cntlr.pinCount = hi35xx->pinCount;
- hi35xx->cntlr.number = hi35xx->number;
- hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // 管脚控制器映射
- if (hi35xx->regBase == NULL) {
- HDF_LOGE("%s: remap Pin base failed", __func__);
- return HDF_ERR_IO;
- }
- hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount);
- if (hi35xx->desc == NULL) {
- HDF_LOGE("%s: memcalloc hi35xx desc failed", __func__);
- return HDF_ERR_MALLOC_FAIL;
- }
- hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount);
- if (hi35xx->desc == NULL) {
- HDF_LOGE("%s: memcalloc hi35xx cntlr pins failed", __func__);
- return HDF_ERR_MALLOC_FAIL;
- }
- return HDF_SUCCESS;
- }
- ```
-
- - PinCntlr成员钩子函数结构体PinCntlrMethod的实例化,其他成员在Init函数中初始化。
-
- ```c
- static struct PinCntlrMethod g_method = {
- .SetPinPull = Hi35xxPinSetPull, // 设置推拉方式
- .GetPinPull = Hi35xxPinGetPull, // 获取推拉方式
- .SetPinStrength = Hi35xxPinSetStrength, // 设置推拉强度
- .GetPinStrength = Hi35xxPinGetStrength, // 获取推拉强度
- .SetPinFunc = Hi35xxPinSetFunc, // 设置管脚功能
- .GetPinFunc = Hi35xxPinGetFunc, // 获取管脚功能
- };
- ```
-
- - Init函数开发参考
-
- 入参
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- | **状态(值)** | **问题描述** |
- | ---------------------- | -------------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象和PinCntlr成员,并通过调用核心层PinCntlrAdd函数挂载PIN控制器。
-
- ```c
- static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps)
- {
- int32_t ret;
- uint32_t funcNum = 0;
- // 从hcs中读取管脚控制器子节点管脚功能名
- ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL");
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read F0 failed", __func__);
- return ret;
- }
-
- funcNum++;
- ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL");
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read F1 failed", __func__);
- return ret;
- }
-
- funcNum++;
- ...
- return HDF_SUCCESS;
- }
-
- static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index)
- {
- int32_t ret;
- struct DeviceResourceIface *drsOps = NULL;
- // 从hcs中读取管脚控制器子节点管脚相关属性
- drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) {
- HDF_LOGE("%s: invalid drs ops fail!", __func__);
- return HDF_FAILURE;
- }
- ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL");
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read pinName failed", __func__);
- return ret;
- }
- ...
- ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s:Pin read Func failed", __func__);
- return ret;
- }
- hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName;
- hi35xx->cntlr.pins[index].priv = (void *)node;
- ...
- return HDF_SUCCESS;
- }
-
- static int32_t Hi35xxPinInit(struct HdfDeviceObject *device)
- {
- ...
- struct Hi35xxPinCntlr *hi35xx = NULL;
- ...
- ret = Hi35xxPinCntlrInit(device, hi35xx); // 管脚控制器初始化
- ...
- DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // 遍历管脚控制器的每个子节点
- ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // 解析子节点
- ...
- }
-
- hi35xx->cntlr.method = &g_method; // PinCntlrMethod实例化实例化对象的挂载
- ret = PinCntlrAdd(&hi35xx->cntlr); // 添加控制器
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: add Pin cntlr: failed", __func__);
- ret = HDF_FAILURE;
- }
- return HDF_SUCCESS;
- }
- ```
-
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口。当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
-
- ```c
- static void Hi35xxPinRelease(struct HdfDeviceObject *device)
- {
- int32_t ret;
- uint16_t number;
- struct PinCntlr *cntlr = NULL;
- struct Hi35xxPinCntlr *hi35xx = NULL;
- struct DeviceResourceIface *drsOps = NULL;
-
- if (device == NULL || device->property == NULL) {
- HDF_LOGE("%s: device or property is null", __func__);
- return;
- }
- // 从hcs文件中读取管脚控制器编号
- drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) {
- HDF_LOGE("%s: invalid drs ops", __func__);
- return;
- }
- ret = drsOps->GetUint16(device->property, "number", &number, 0);
- if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: read cntlr number failed", __func__);
- return;
- }
-
- cntlr = PinCntlrGetByNumber(number); // 通过管脚控制器编号获取管脚控制器
- PinCntlrRemove(cntlr);
- hi35xx = (struct Hi35xxPinCntlr *)cntlr;
- if (hi35xx != NULL) {
- if (hi35xx->regBase != NULL) {
- OsalIoUnmap((void *)hi35xx->regBase);
- }
- OsalMemFree(hi35xx);
- }
- }
- ```
-
-4. 驱动调试。
-
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
\ No newline at end of file
+2. 配置属性文件
+
+ 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个PIN控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在gpio_config.hcs文件中增加对应的器件属性。器件属性值对于驱动适配者的驱动实现以及核心层PinCntlr相关成员的默认值或限制范围有密切关系,比如控制器号,控制器管脚数量、管脚等,需要在pin_config.hcs中配置器件属性。
+
+ 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为PIN管理器,其各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,PIN管理器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。PIN管理器具体配置为8 |
+ | permission | 驱动创建设备节点权限,PIN管理器具体配置为0664 |
+ | moduleName | 驱动名称,PIN管理器固定为HDF_PLATFORM_PIN_MANAGER |
+ | serviceName | 驱动对外发布服务的名称,PIN管理器服务名设置为HDF_PLATFORM_PIN_MANAGER |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,PIN管理器没有使用,可忽略 |
+
+ - device_info.hcs 配置参考:
+
+ 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
+
+ ```c
+ root {
+ device_info {
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_pin :: device {
+ device0 :: deviceNode { // 用于统一管理PIN并发布服务
+ policy = 2; // 2:用户态可见;1:内核态可见;0:不需要发布服务。
+ priority = 8; // 启动优先级
+ permission = 0644; // 创建设备节点权限
+ moduleName = "HDF_PLATFORM_PIN_MANAGER";
+ serviceName = "HDF_PLATFORM_PIN_MANAGER";
+ }
+ device1 :: deviceNode { // 为每一个PIN控制器配置一个HDF设备节点,存在多个时必须添加,否则不用。
+ policy = 0;
+ priority = 10; // 驱动启动优先级
+ permission = 0644; // 驱动创建设备节点权限
+ moduleName = "hi35xx_pin_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
+ deviceMatchAttr = "hisilicon_hi35xx_pin_0"; // 【必要】用于配置控制器私有数据,要与pin_config.hcs中对应控制器保持一致,具体的控制器信息在pin_config.hcs中。
+ }
+ device2 :: deviceNode {
+ policy = 0;
+ priority = 10;
+ permission = 0644;
+ moduleName = "hi35xx_pin_driver";
+ deviceMatchAttr = "hisilicon_hi35xx_pin_1";
+ }
+ ...... // 如果存在多个PIN控制器时【必须】添加节点,否则不用
+ }
+ }
+ }
+ }
+ ```
+
+ - pin_config.hcs配置参考:
+
+ 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs文件配置器件属性,其中配置参数如下:
+
+ ```c
+ root {
+ platform {
+ pin_config_hi35xx {
+ template pin_controller { // 【必要】配置模板配,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值。
+ number = 0; // 【必要】PIN控制器号
+ regStartBasePhy = 0; // 【必要】寄存器物理基地址起始地址
+ regSize = 0; // 【必要】寄存器位宽
+ pinCount = 0; // 【必要】管脚数量
+ match_attr = "";
+ template pin_desc {
+ pinName = ""; // 【必要】管脚名称
+ init = 0; // 【必要】寄存器默认值
+ F0 = ""; // 【必要】功能0
+ F1 = ""; // 功能1
+ F2 = ""; // 功能2
+ F3 = ""; // 功能3
+ F4 = ""; // 功能4
+ F5 = ""; // 功能5
+ }
+ }
+ controller_0 :: pin_controller {
+ number = 0;
+ regStartBasePhy = 0x10FF0000;
+ regSize = 0x48;
+ pinCount = 18;
+ match_attr = "hisilicon_hi35xx_pin_0";
+ T1 :: pin_desc {
+ pinName = "T1";
+ init = 0x0600;
+ F0 = "EMMC_CLK";
+ F1 = "SFC_CLK";
+ F2 = "SFC_BOOT_MODE";
+ }
+ ...... // 对应管脚控制器下的每个管脚,按实际添加。
+ }
+ ...... // 每个管脚控制器对应一个控制器节点,如存在多个PIN控制器,请依次添加对应的控制器节点。
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增pin_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化PIN控制器对象
+
+ 完成配置属性文件之后,下一步就是以核心层PinCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化PinCntlr成员PinCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+
+ - 驱动适配者自定义结构体参考
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,而且pin_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。
+
+ 在Hi35xxPinCntlrInit函数中对PinCntlr成员进行初始化操作。
+
+ ```c
+ // 驱动适配者自定义管脚描述结构体
+ struct Hi35xxPinDesc {
+ const char *pinName; // 管脚名
+ uint32_t init; // 初始化值
+ uint32_t index; // 管脚索引
+ int32_t pullType; // 管脚推拉方式
+ int32_t strength; // 管脚推拉强度
+ const char *func[HI35XX_PIN_FUNC_MAX]; // 管脚功能名字符串数组
+ };
+
+ // 驱动适配者自定义结构体
+ struct Hi35xxPinCntlr {
+ struct PinCntlr cntlr; // 是核心层控制对象,具体描述见下面
+ struct Hi35xxPinDesc *desc; // 驱动适配者自定义管脚描述结构体指针
+ volatile unsigned char *regBase; // 寄存器映射地址
+ uint16_t number; // 管脚控制器编号
+ uint32_t regStartBasePhy; // 寄存器物理基地址起始地址
+ uint32_t regSize; // 寄存器位宽
+ uint32_t pinCount; // 管脚数量
+ };
+
+ // PinCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct PinCntlr {
+ struct IDeviceIoService service; // 驱动服务
+ struct HdfDeviceObject *device; // 驱动设备对象
+ struct PinCntlrMethod *method; // 钩子函数
+ struct DListHead node; // 链表节点
+ OsalSpinlock spin; // 自旋锁
+ uint16_t number; // 管脚控制器编号
+ uint16_t pinCount; // 管脚数量
+ struct PinDesc *pins; // 管脚资源
+ void *priv; // 私有数据
+ };
+
+ // PIN管脚控制器初始化
+ static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx)
+ {
+ struct DeviceResourceIface *drsOps = NULL;
+ int32_t ret;
+ // 从hcs文件读取管脚控制器相关属性
+ drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
+ if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
+ HDF_LOGE("%s: invalid drs ops fail!", __func__);
+ return HDF_FAILURE;
+ }
+ ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read number failed", __func__);
+ return ret;
+ }
+
+ if (hi35xx->number > HI35XX_PIN_MAX_NUMBER) {
+ HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number);
+ return HDF_ERR_INVALID_PARAM;
+ }
+ ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read regStartBasePhy failed", __func__);
+ return ret;
+ }
+ ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read regSize failed", __func__);
+ return ret;
+ }
+ ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read pinCount failed", __func__);
+ return ret;
+ }
+ if (hi35xx->pinCount > PIN_MAX_CNT_PER_CNTLR) {
+ HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number);
+ return HDF_ERR_INVALID_PARAM;
+ }
+ // 将读取的值赋值给管脚控制器的成员,完成管脚控制器初始化。
+ hi35xx->cntlr.pinCount = hi35xx->pinCount;
+ hi35xx->cntlr.number = hi35xx->number;
+ hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // 管脚控制器映射
+ if (hi35xx->regBase == NULL) {
+ HDF_LOGE("%s: remap Pin base failed", __func__);
+ return HDF_ERR_IO;
+ }
+ hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount);
+ if (hi35xx->desc == NULL) {
+ HDF_LOGE("%s: memcalloc hi35xx desc failed", __func__);
+ return HDF_ERR_MALLOC_FAIL;
+ }
+ hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount);
+ if (hi35xx->desc == NULL) {
+ HDF_LOGE("%s: memcalloc hi35xx cntlr pins failed", __func__);
+ return HDF_ERR_MALLOC_FAIL;
+ }
+ return HDF_SUCCESS;
+ }
+ ```
+
+ - PinCntlr成员钩子函数结构体PinCntlrMethod的实例化。
+
+ ```c
+ static struct PinCntlrMethod g_method = {
+ .SetPinPull = Hi35xxPinSetPull, // 设置推拉方式
+ .GetPinPull = Hi35xxPinGetPull, // 获取推拉方式
+ .SetPinStrength = Hi35xxPinSetStrength, // 设置推拉强度
+ .GetPinStrength = Hi35xxPinGetStrength, // 获取推拉强度
+ .SetPinFunc = Hi35xxPinSetFunc, // 设置管脚功能
+ .GetPinFunc = Hi35xxPinGetFunc, // 获取管脚功能
+ };
+ ```
+
+ - Init函数开发参考
+
+ 入参
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象和PinCntlr成员,并通过调用核心层PinCntlrAdd函数挂载PIN控制器。
+
+ ```c
+ static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps)
+ {
+ int32_t ret;
+ uint32_t funcNum = 0;
+ // 从hcs中读取管脚控制器子节点管脚功能名
+ ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL");
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read F0 failed", __func__);
+ return ret;
+ }
+
+ funcNum++;
+ ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL");
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read F1 failed", __func__);
+ return ret;
+ }
+
+ funcNum++;
+ ......
+ return HDF_SUCCESS;
+ }
+
+ static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index)
+ {
+ int32_t ret;
+ struct DeviceResourceIface *drsOps = NULL;
+ // 从hcs中读取管脚控制器子节点管脚相关属性
+ drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
+ if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) {
+ HDF_LOGE("%s: invalid drs ops fail!", __func__);
+ return HDF_FAILURE;
+ }
+ ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL");
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read pinName failed", __func__);
+ return ret;
+ }
+ ...
+ ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s:Pin read Func failed", __func__);
+ return ret;
+ }
+ hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName;
+ hi35xx->cntlr.pins[index].priv = (void *)node;
+ ......
+ return HDF_SUCCESS;
+ }
+
+ static int32_t Hi35xxPinInit(struct HdfDeviceObject *device)
+ {
+ ......
+ struct Hi35xxPinCntlr *hi35xx = NULL;
+ ......
+ ret = Hi35xxPinCntlrInit(device, hi35xx); // 管脚控制器初始化
+ ......
+ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // 遍历管脚控制器的每个子节点
+ ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // 解析子节点
+ ......
+ }
+
+ hi35xx->cntlr.method = &g_method; // PinCntlrMethod实例化实例化对象的挂载
+ ret = PinCntlrAdd(&hi35xx->cntlr); // 添加控制器
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: add Pin cntlr: failed", __func__);
+ ret = HDF_FAILURE;
+ }
+ return HDF_SUCCESS;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口。当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+
+ ```c
+ static void Hi35xxPinRelease(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ uint16_t number;
+ struct PinCntlr *cntlr = NULL;
+ struct Hi35xxPinCntlr *hi35xx = NULL;
+ struct DeviceResourceIface *drsOps = NULL;
+
+ if (device == NULL || device->property == NULL) {
+ HDF_LOGE("%s: device or property is null", __func__);
+ return;
+ }
+ // 从hcs文件中读取管脚控制器编号
+ drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
+ if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) {
+ HDF_LOGE("%s: invalid drs ops", __func__);
+ return;
+ }
+ ret = drsOps->GetUint16(device->property, "number", &number, 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("%s: read cntlr number failed", __func__);
+ return;
+ }
+
+ cntlr = PinCntlrGetByNumber(number); // 通过管脚控制器编号获取管脚控制器
+ PinCntlrRemove(cntlr);
+ hi35xx = (struct Hi35xxPinCntlr *)cntlr;
+ if (hi35xx != NULL) {
+ if (hi35xx->regBase != NULL) {
+ OsalIoUnmap((void *)hi35xx->regBase);
+ }
+ OsalMemFree(hi35xx);
+ }
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,PIN管脚设置推拉方式、推拉强度等。
diff --git a/zh-cn/device-dev/driver/driver-platform-pwm-des.md b/zh-cn/device-dev/driver/driver-platform-pwm-des.md
index 18dd7fd810d9762fecd60cd9783acbf6e48baca2..73420ff86e0c90b608b8766e85f9b2bd781e70b0 100644
--- a/zh-cn/device-dev/driver/driver-platform-pwm-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-pwm-des.md
@@ -9,13 +9,16 @@ PWM即脉冲宽度调制(Pulse Width Modulation)的缩写,是一种对模
PWM接口定义了操作PWM设备的通用方法集合,包括:
- PWM设备句柄获取和释放
+
- PWM周期、占空比、极性的设置
+
- PWM使能和关闭
+
- PWM配置信息的获取和设置
### 基本概念
-脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为煎复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。
+脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为重复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。
### 运作机制
@@ -24,17 +27,20 @@ PWM接口定义了操作PWM设备的通用方法集合,包括:
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
PWM模块各分层作用:
- 接口层提供打开PWM设备、设置PWM设备周期、设置PWM设备占空时间、设置PWM设备极性、设置PWM设备参数、获取PWM设备参数、使能PWM设备、禁止PWM设备、关闭PWM设备的接口。
+
- 核心层主要提供PWM控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
-**图1** PWM独立服务模式结构图
+**图 1** PWM独立服务模式结构图
-![image1](figures/独立服务模式结构图.png "PWM独立服务模式结构图")
+![PWM独立服务模式结构图](figures/独立服务模式结构图.png)
## 使用指导
@@ -44,31 +50,31 @@ PWM模块各分层作用:
### 接口说明
-PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示。
+PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示,具体API详见//drivers/hdf_core/framework/include/platform/pwm_if.h。
-**表1** PwmConfig结构体介绍
+**表 1** PwmConfig结构体介绍
| 名称 | 描述 |
| -------- | -------- |
-| duty | 占空时间,以纳秒为单位。 |
-| period | PWM周期,以纳秒为单位。 |
-| number | 要生成的方波数:
- 正值:表示将生成指定数量的方波
- 0:表示方波将不断产生 |
-| polarity | 极性:正极性/反极性。 |
-| status | 状态:启用状态/禁用状态。 |
+| duty | uint32_t类型,占空时间,以纳秒为单位。 |
+| period | uint32_t类型,PWM周期,以纳秒为单位。 |
+| number | uint32_t类型,要生成的方波数:
- 正值:表示将生成指定数量的方波
- 0:表示方波将不断产生 |
+| polarity | uint8_t类型,极性:正极性/反极性。 |
+| status | uint8_t类型,状态:启用状态/禁用状态。 |
-**表2** PWM驱动API接口功能介绍
+**表 2** PWM驱动API接口功能介绍
-| 接口名 | |
+| 接口名 | 接口描述|
| ------------------------------------------------------------ | ------------------- |
-| DevHandle PwmOpen(uint32_t num) | 打开PWM设备 |
-| void PwmClose(DevHandle handle) | 关闭PWM设备 |
-| int32_t PwmSetPeriod(DevHandle handle, uint32_t period) | 设置PWM设备周期 |
-| int32_t PwmSetDuty(DevHandle handle, uint32_t duty) | 设置PWM设备占空时间 |
-| int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity) | 设置PWM设备极性 |
-| int32_t PwmEnable(DevHandle handle) | 使能PWM设备 |
-| int32_t PwmDisable(DevHandle handle) | 禁用PWM设备 |
-| int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config) | 设置PWM设备参数 |
-| int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config) | 获取PWM设备参数 |
+| DevHandle PwmOpen(uint32_t num) | 打开PWM设备 |
+| void PwmClose(DevHandle handle)| 关闭PWM设备 |
+| int32_t PwmSetPeriod(DevHandle handle, uint32_t period) | 设置PWM设备周期 |
+| int32_t PwmSetDuty(DevHandle handle, uint32_t duty) | 设置PWM设备占空时间 |
+| int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity) | 设置PWM设备极性 |
+| int32_t PwmEnable(DevHandle handle) | 使能PWM设备 |
+| int32_t PwmDisable(DevHandle handle) | 禁用PWM设备 |
+| int32_t PwmSetConfig(DevHandle handle, struct PwmConfig \*config) | 设置PWM设备参数 |
+| int32_t PwmGetConfig(DevHandle handle, struct PwmConfig \*config) | 获取PWM设备参数 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 本文涉及PWM的所有接口,支持内核态及用户态使用。
@@ -77,9 +83,9 @@ PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示
使用PWM的一般流程如下图所示。
-**图2** PWM使用流程图
+**图 2** PWM使用流程图
-![image2](figures/PWM设备使用流程图.png "PWM设备使用流程图")
+![PWM使用流程图](figures/PWM设备使用流程图.png)
#### 获取PWM设备句柄
@@ -89,14 +95,14 @@ PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示
DevHandle PwmOpen(uint32_t num);
```
-**表3** PwmOpen参数和返回值描述
+**表 3** PwmOpen参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| num | PWM设备号 |
-| **返回值** | **返回值描述** |
-| handle | 打开PWM设备成功,返回PWM设备句柄 |
-| NULL | 打开PWM设备失败 |
+| num | uint32_t类型,PWM设备号 |
+| **返回值** | **返回值描述** |
+| handle | 打开PWM设备成功,返回PWM设备句柄 |
+| NULL | 打开PWM设备失败 |
假设系统中的PWM设备号为0,获取该PWM设备句柄的示例如下:
@@ -105,9 +111,9 @@ uint32_t num = 0; // PWM设备号
DevHandle handle = NULL;
handle = PwmOpen(num); // 打开PWM 0设备并获取PWM设备句柄
-if (handle == NULL) {
+if (handle == NULL) {
HDF_LOGE("PwmOpen: open pwm_%u failed.\n", num);
- return;
+ return HDF_FAILURE;
}
```
@@ -119,11 +125,11 @@ if (handle == NULL) {
void PwmClose(DevHandle handle);
```
-**表4** PwmClose参数描述
+**表 4** PwmClose参数描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | PWM设备句柄 |
+| handle | DevHandle类型,PWM设备句柄 |
```c
PwmClose(handle); // 关闭PWM设备销毁PWM设备句柄
@@ -135,14 +141,14 @@ PwmClose(handle); // 关闭PWM设备销毁PWM设备句柄
int32_t PwmEnable(DevHandle handle);
```
-**表5** PwmEnable参数和返回值描述
+**表 5** PwmEnable参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | PWM设备句柄 |
+| handle | DevHandle类型,PWM设备句柄 |
| **返回值** | **返回值描述** |
-| HDF_SUCCESS | 使能成功 |
-| 负数 | 使能失败 |
+| HDF_SUCCESS | 使能PWM设备成功 |
+| 负数 | 使能PWM设备失败 |
```c
int32_t ret;
@@ -160,14 +166,14 @@ if (ret != HDF_SUCCESS) {
int32_t PwmDisable(DevHandle handle);
```
-**表6** PwmDisable参数和返回值描述
+**表 6** PwmDisable参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | PWM设备句柄 |
+| handle | DevHandle类型,PWM设备句柄 |
| **返回值** | **返回值描述** |
-| HDF_SUCCESS | 禁用成功 |
-| 负数 | 禁用失败 |
+| HDF_SUCCESS | 禁用PWM设备成功 |
+| 负数 | 禁用PWM设备失败 |
```c
int32_t ret;
@@ -185,15 +191,15 @@ if (ret != HDF_SUCCESS) {
int32_t PwmSetPeriod(DevHandle handle, uint32_t period);
```
-**表7** PwmSetPeriod参数和返回值描述
+**表 7** PwmSetPeriod参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | PWM设备句柄 |
-| period | 要设置的周期,单位为纳秒 |
-| **返回值** | **返回值描述** |
-| HDF_SUCCESS | 设置成功 |
-| 负数 | 设置失败 |
+| handle | DevHandle类型,PWM设备句柄 |
+| period | uint32_t类型,要设置的周期,单位为纳秒 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 设置PWM设备周期成功 |
+| 负数 | 设置PWM设备周期失败 |
```c
int32_t ret;
@@ -211,15 +217,15 @@ if (ret != HDF_SUCCESS) {
int32_t PwmSetDuty(DevHandle handle, uint32_t duty);
```
-**表8** PwmSetDuty参数和返回值描述
+**表 8** PwmSetDuty参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | PWM设备句柄 |
-| duty | 要设置的占空时间,单位为纳秒 |
-| **返回值** | **返回值描述** |
-| HDF_SUCCESS | 设置成功 |
-| 负数 | 设置失败 |
+| handle | DevHandle类型,PWM设备句柄 |
+| duty | uint32_t类型,要设置的占空时间,单位为纳秒 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 设置PWM设备占空时间成功 |
+| 负数 | 设置PWM设备占空时间失败 |
```c
@@ -238,15 +244,15 @@ if (ret != HDF_SUCCESS) {
int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity);
```
-**表9** PwmSetPolarity参数和返回值描述
+**表 9** PwmSetPolarity参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | PWM设备句柄 |
-| polarity | 要设置的极性,正/反 |
-| **返回值** | **返回值描述** |
-| HDF_SUCCESS | 设置成功 |
-| 负数 | 设置失败 |
+| handle | DevHandle类型,PWM设备句柄 |
+| polarity | uint8_t类型,要设置的极性,正/反 |
+| **返回值** | **返回值描述** |
+| HDF_SUCCESS | 设置PWM设备极性成功 |
+| 负数 | 设置PWM设备极性失败 |
```c
@@ -265,15 +271,15 @@ if (ret != HDF_SUCCESS) {
int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config);
```
-**表10** PwmSetConfig参数和返回值描述
+**表 10** PwmSetConfig参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | PWM设备句柄 |
-| \*config | 参数指针 |
+| handle | DevHandle类型,PWM设备句柄 |
+| config | 结构体指针类型,配置参数 |
| **返回值** | **返回值描述** |
-| HDF_SUCCESS | 设置成功 |
-| 负数 | 设置失败 |
+| HDF_SUCCESS | 设置PWM设备参数成功 |
+| 负数 | 设置PWM设备参数失败 |
```c
int32_t ret;
@@ -298,15 +304,15 @@ if (ret != HDF_SUCCESS) {
int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config);
```
-**表11** PwmGetConfig参数和返回值描述
+**表 11** PwmGetConfig参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | PWM设备句柄 |
-| \*config | 参数指针 |
+| handle | DevHandle类型,PWM设备句柄 |
+| config | 结构体指针类型,配置参数 |
| **返回值** | **返回值描述** |
-| HDF_SUCCESS | 获取成功 |
-| 负数 | 获取失败 |
+| HDF_SUCCESS | 获取PWM设备参数成功 |
+| 负数 | 获取PWM设备参数失败 |
```c
int32_t ret;
@@ -324,13 +330,21 @@ if (ret != HDF_SUCCESS) {
下面将基于Hi3516DV300开发板展示使用PWM完整操作,步骤主要如下:
1. 传入PWM设备号,打开PWM设备并获得PWM设备句柄。
+
2. 通过PWM设备句柄及待设置的周期,设置PWM设备周期。
+
3. 通过PWM设备句柄及待设置的占空时间,设置PWM设备占空时间。
+
4. 通过PWM设备句柄及待设置的极性,设置PWM设备极性。
+
5. 通过PWM设备句柄及待获取的设备参数,获取PWM设备参数。
+
6. 通过PWM设备句柄,使能PWM设备。
+
7. 通过PWM设备句柄及待设置的设备参数,设置PWM设备参数。
+
8. 通过PWM设备句柄,禁用PWM设备。
+
9. 通过PWM设备句柄,关闭PWM设备。
```c
@@ -341,7 +355,8 @@ static int32_t PwmTestSample(void)
{
int32_t ret;
uint32_t num;
- uint32_t period
+ uint32_t period;
+ uint32_t duty;
DevHandle handle = NULL;
struct PwmConfig pcfg;
@@ -355,52 +370,55 @@ static int32_t PwmTestSample(void)
handle = PwmOpen(num); // 获取PWM设备句柄
if (handle == NULL) {
- HDF_LOGE("PwmOpen: open pwm_%u failed!\n", num);
- return;
+ HDF_LOGE("PwmTestSample: open pwm_%u fail!\n", num);
+ return HDF_FAILURE;
}
- ret = PwmSetPeriod(handle, 50000000); // 设置周期为50000000纳秒
+ period = 50000000; // 设置周期为50000000纳秒
+ ret = PwmSetPeriod(handle, period);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PwmSetPeriod: pwm set period failed, ret %d\n", ret);
+ HDF_LOGE("PwmTestSample: pwm set period fail, ret:%d\n", ret);
goto ERR;
}
- ret = PwmSetDuty(handle, 25000000); // 设置占空时间为25000000纳秒
+ duty = 25000000; // 设置占空时间为25000000纳秒
+ ret = PwmSetDuty(handle, duty);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PwmSetDuty: pwm set duty failed, ret %d\n", ret);
+ HDF_LOGE("PwmTestSample: pwm set duty fail, ret:%d\n", ret);
goto ERR;
}
ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); // 设置极性为反
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PwmSetPolarity: pwm set polarity failed, ret %d\n", ret);
+ HDF_LOGE("PwmTestSample: pwm set polarity fail, ret:%d\n", ret);
goto ERR;
}
ret = PwmGetConfig(handle, &pcfg); // 获取PWM设备参数
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PwmGetConfig: get pwm config failed, ret %d\n", ret);
+ HDF_LOGE("PwmTestSample: get pwm config fail, ret:%d\n", ret);
goto ERR;
}
ret = PwmEnable(handle); // 启用PWM设备
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PwmEnable: enable pwm failed, ret %d\n", ret);
+ HDF_LOGE("PwmEnable: enable pwm fail, ret:%d\n", ret);
goto ERR;
}
ret = PwmSetConfig(handle, &pcfg); // 设置PWM设备参数
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PwmSetConfig: set pwm config failed, ret %d\n", ret);
+ HDF_LOGE("PwmTestSample: set pwm config fail, ret:%d\n", ret);
goto ERR;
}
ret = PwmDisable(handle); // 禁用PWM设备
if (ret != HDF_SUCCESS) {
- HDF_LOGE("PwmDisable: disable pwm failed, ret %d\n", ret);
+ HDF_LOGE("PwmTestSample: disable pwm fail, ret:%d\n", ret);
goto ERR;
}
+ HDF_LOGD("PwmTestSample: all tests end.");
ERR:
PwmClose(handle); // 销毁PWM设备句柄
return ret;
diff --git a/zh-cn/device-dev/driver/driver-platform-pwm-develop.md b/zh-cn/device-dev/driver/driver-platform-pwm-develop.md
index 192c93865f7973df001e0f478bded02db9fbb949..119375ff1de1a70f3e7ac49afcf3792609a54717 100755
--- a/zh-cn/device-dev/driver/driver-platform-pwm-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-pwm-develop.md
@@ -8,7 +8,7 @@ PWM(Pulse Width Modulation)即脉冲宽度调制,是一种对模拟信号
### 基本概念
-脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为煎复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。
+脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为重复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。
### 运作机制
@@ -17,17 +17,20 @@ PWM(Pulse Width Modulation)即脉冲宽度调制,是一种对模拟信号
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
PWM模块各分层作用:
- 接口层提供打开PWM设备、设置PWM设备周期、设置PWM设备占空时间、设置PWM设备极性、设置PWM设备参数、获取PWM设备参数、使能PWM设备、禁止PWM设备、关闭PWM设备的接口。
+
- 核心层主要提供PWM控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
-**图1** PWM独立服务模式结构图
+**图 1** PWM独立服务模式结构图
-![image](figures/独立服务模式结构图.png "PWM独立服务模式结构图")
+![PWM独立服务模式结构图](figures/独立服务模式结构图.png)
## 开发指导
@@ -49,7 +52,7 @@ struct PwmMethod {
};
```
-**表1** PwmMethod结构体成员的钩子函数功能说明
+**表 1** PwmMethod结构体成员的钩子函数功能说明
| 成员函数 | 入参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- |
@@ -61,249 +64,269 @@ struct PwmMethod {
PWM模块适配包含以下四个步骤:
-- 驱实例化驱动入口。
-- 配置属性文件。
-- 实例化PWM控制器对象。
-- 驱动调试。
+- 驱实例化驱动入口
+
+- 配置属性文件
+
+- 实例化PWM控制器对象
+
+- 驱动调试
### 开发实例
下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/pwm/pwm_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 驱实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
- PWM驱动入口开发参考:
-
- ```c
- struct HdfDriverEntry g_hdfPwm = {
- .moduleVersion = 1,
- .moduleName = "HDF_PLATFORM_PWM", // 【必要且与HCS文件中里面的moduleName匹配】
- .Bind = HdfPwmBind, // 见Bind参考
- .Init = HdfPwmInit, // 见Init参考
- .Release = HdfPwmRelease, // 见Release参考
- };
- HDF_INIT(g_hdfPwm); // 调用HDF_INIT将驱动入口注册到HDF框架中
- ```
-
-2. 配置属性文件。
-
- 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个PWM控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层PwmDev成员的默认值或限制范围有密切关系,比如PWM设备号,需要在pwm_config.hcs文件中增加对应的器件属性。
-
- - device_info.hcs 配置参考:
-
- 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
-
- ```c
- root {
- device_info {
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_pwm :: device { // 为每一个PWM控制器配置一个HDF设备节点
- device0 :: deviceNode {
- policy = 1; // 等于1,向内核态发布服务
- priority = 80; // 驱动启动优先级
- permission = 0644; // 驱动创建设备节点权限
- moduleName = "HDF_PLATFORM_PWM"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致
- serviceName = "HDF_PLATFORM_PWM_0"; // 【必要且唯一】驱动对外发布服务的名称
- deviceMatchAttr = "hisilicon_hi35xx_pwm_0"; // 【必要】用于配置控制器私有数据,要与pwm_config.hcs中对应控制器保持一致,具体的控制器信息在pwm_config.hcs中
- }
- device1 :: deviceNode {
- policy = 1;
- priority = 80;
- permission = 0644;
- moduleName = "HDF_PLATFORM_PWM";
- serviceName = "HDF_PLATFORM_PWM_1";
- deviceMatchAttr = "hisilicon_hi35xx_pwm_1";
- }
- ...
- }
- }
- }
- }
- ```
-
- - pwm_config.hcs 配置参考
-
- 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs文件配置器件属性,其中配置参数如下:
-
- ```c
- root {
- platform {
- pwm_config {
- template pwm_device { // 【必要】配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值
- serviceName = "";
- match_attr = "";
- num = 0; // 【必要】设备号
- base = 0x12070000; // 【必要】地址映射需要
- }
- device_0x12070000 :: pwm_device { // 存在多个设备时,请逐一添加相关HDF节点和设备节点信息。
- match_attr = "hisilicon_hi35xx_pwm_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
- }
- device_0x12070020 :: pwm_device {
- match_attr = "hisilicon_hi35xx_pwm_1";
- num = 1;
- base = 0x12070020; // 【必要】地址映射需要
- }
- }
- }
- }
- ```
-
- 需要注意的是,新增pwm_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs" // 配置文件相对路径
- ```
-
-3. 实例化PWM控制器对象。
-
- 完成驱动入口注册之后,下一步就是以核心层PwmDev对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化PwmDev成员PwmMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
-
- - 驱动适配者自定义结构体参考。
-
- 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且pwm_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如PWM设备号。
-
- ```c
- struct HiPwm {
- struct PwmDev dev; // 【必要】 核是核心层控制对象
- volatile unsigned char *base; // 【必要】地址映射需要,寄存器基地址
- struct HiPwmRegs *reg; // 设备属性结构体,可自定义。
- bool supportPolarity; // 是否支持极性
- };
-
- struct PwmDev { // PwmDev是核心层控制器结构体,其中的成员在Init函数中会被赋值。
- struct IDeviceIoService service; // 驱动服务
- struct HdfDeviceObject *device; // 驱动设备对象
- struct PwmConfig cfg; // 设备属性结构体,相关定义见下。
- struct PwmMethod *method; // 钩子函数
- bool busy; // 是否繁忙
- uint32_t num; // 设备号
- OsalSpinlock lock; // 自旋锁
- void *priv; // 私有数据
- };
-
- struct PwmConfig { // PWM设备属性
- uint32_t duty; // 占空时间 nanoseconds
- uint32_t period; // pwm 周期 nanoseconds
- uint32_t number; // pwm 连续个数
- uint8_t polarity; // Polarity
- // ------------------- | --------------
- // PWM_NORMAL_POLARITY | Normal polarity
- // PWM_INVERTED_POLARITY | Inverted polarity
- //
- uint8_t status; // 运行状态
- // ------------------ | -----------------
- // PWM_DISABLE_STATUS | Disabled
- // PWM_ENABLE_STATUS | Enabled
- };
- ```
-
- - PwmDev成员钩子函数结构体PwmMethod的实例化,其他成员在Init函数中初始化。
+1. 驱实例化驱动入口
+
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+
+ PWM驱动入口开发参考:
+
+ ```c
+ struct HdfDriverEntry g_hdfPwm = {
+ .moduleVersion = 1,
+ .moduleName = "HDF_PLATFORM_PWM", // 【必要且与HCS文件中里面的moduleName匹配】
+ .Bind = HdfPwmBind, // 挂接PWM模块Bind实例化
+ .Init = HdfPwmInit, // 挂接PWM模块Init实例化
+ .Release = HdfPwmRelease, // 挂接PWM模块Release实例化
+ };
+ HDF_INIT(g_hdfPwm); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
+
+2. 配置属性文件
+
+ 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个PWM控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在pwm_config.hcs文件中增加对应的器件属性。器件属性值与核心层PwmDev成员的默认值或限制范围有密切关系,比如PWM设备号,需要在pwm_config.hcs文件中增加对应的器件属性。
+
+ 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,PWM控制器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。PWM控制器具体配置为80 |
+ | permission | 驱动创建设备节点权限,PWM控制器具体配置为0664 |
+ | moduleName | 驱动名称,PWM控制器固定为HDF_PLATFORM_PWM |
+ | serviceName | 驱动对外发布服务的名称,PWM控制器服务名设置为HDF_PLATFORM_PWM_X,X代表PWM控制器编号 |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,PWM控制器设置为hisilicon_hi35xx_pwm_X,X代表PWM控制器编号 |
+
+
+ - device_info.hcs 配置参考:
+
+ 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
+
+ ```c
+ root {
+ device_info {
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_pwm :: device { // 为每一个PWM控制器配置一个HDF设备节点
+ device0 :: deviceNode {
+ policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2
+ priority = 80; // 驱动启动优先级
+ permission = 0644; // 驱动创建设备节点权限
+ moduleName = "HDF_PLATFORM_PWM"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致
+ serviceName = "HDF_PLATFORM_PWM_0"; // 【必要且唯一】驱动对外发布服务的名称
+ deviceMatchAttr = "hisilicon_hi35xx_pwm_0"; // 【必要】用于配置控制器私有数据,要与pwm_config.hcs中对应控制器保持一致,具体的控制器信息在pwm_config.hcs中
+ }
+ device1 :: deviceNode {
+ policy = 2;
+ priority = 80;
+ permission = 0644;
+ moduleName = "HDF_PLATFORM_PWM";
+ serviceName = "HDF_PLATFORM_PWM_1";
+ deviceMatchAttr = "hisilicon_hi35xx_pwm_1";
+ }
+ ...... // 如果存在多个PWM设备时【必须】添加节点,否则不用
+ }
+ }
+ }
+ }
+ ```
+
+ - pwm_config.hcs 配置参考
+
+ 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs文件配置器件属性,其中配置参数如下:
+
+ ```c
+ root {
+ platform {
+ pwm_config {
+ template pwm_device { // 【必要】配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值
+ serviceName = "";
+ match_attr = "";
+ num = 0; // 【必要】设备号
+ base = 0x12070000; // 【必要】地址映射需要
+ }
+ device_0x12070000 :: pwm_device { // 存在多个设备时,请逐一添加相关HDF节点和设备节点信息。
+ match_attr = "hisilicon_hi35xx_pwm_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
+ }
+ device_0x12070020 :: pwm_device {
+ match_attr = "hisilicon_hi35xx_pwm_1";
+ num = 1;
+ base = 0x12070020; // 【必要】地址映射需要
+ }
+ ...... // 如果存在多个PWM设备时【必须】添加节点,否则不用
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增pwm_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化PWM控制器对象
+
+ 完成驱动入口注册之后,下一步就是以核心层PwmDev对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化PwmDev成员PwmMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+
+ - 驱动适配者自定义结构体参考。
+
+ 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且pwm_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如PWM设备号。
+
+ ```c
+ struct HiPwm {
+ struct PwmDev dev; // 【必要】 核是核心层控制对象
+ volatile unsigned char *base; // 【必要】地址映射需要,寄存器基地址
+ struct HiPwmRegs *reg; // 设备属性结构体,可自定义。
+ bool supportPolarity; // 是否支持极性
+ };
+
+ struct PwmDev { // PwmDev是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct IDeviceIoService service; // 驱动服务
+ struct HdfDeviceObject *device; // 驱动设备对象
+ struct PwmConfig cfg; // 设备属性结构体,相关定义见下。
+ struct PwmMethod *method; // 钩子函数
+ bool busy; // 是否繁忙
+ uint32_t num; // 设备号
+ OsalSpinlock lock; // 自旋锁
+ void *priv; // 私有数据
+ };
+
+ struct PwmConfig { // PWM设备属性
+ uint32_t duty; // 占空时间 nanoseconds
+ uint32_t period; // pwm 周期 nanoseconds
+ uint32_t number; // pwm 连续个数
+ uint8_t polarity; // Polarity
+ // ------------------- | --------------
+ // PWM_NORMAL_POLARITY | Normal polarity
+ // PWM_INVERTED_POLARITY | Inverted polarity
+ //
+ uint8_t status; // 运行状态
+ // ------------------ | -----------------
+ // PWM_DISABLE_STATUS | Disabled
+ // PWM_ENABLE_STATUS | Enabled
+ };
+ ```
+
+ - PwmDev成员钩子函数结构体PwmMethod的实例化,其他成员在Init函数中初始化。
- ```c
- struct PwmMethod g_pwmOps = { // pwm_hi35xx.c中的示例:钩子函数实例化
- .setConfig = HiPwmSetConfig, // 配置属性
- };
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化PwmDev成员,调用核心层PwmDeviceAdd函数,完成PWM控制器的添加。
-
- ```c
- // 此处Bind函数为空函数,可与Init函数结合,也可根据驱动适配者需要实现相关操作。
- static int32_t HdfPwmBind(struct HdfDeviceObject *obj)
- {
- (void)obj;
- return HDF_SUCCESS;
- }
-
- static int32_t HdfPwmInit(struct HdfDeviceObject *obj)
- {
- int ret;
- struct HiPwm *hp = NULL;
- ...
- hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp));
- ...
- ret = HiPwmProbe(hp, obj); // 【必要】实现见下
- ...
- return ret;
- }
-
- static int32_t HiPwmProbe(struct HiPwm *hp, struct HdfDeviceObject *obj)
- {
- uint32_t tmp;
- struct DeviceResourceIface *iface = NULL;
-
- iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); //初始化自定义结构体HiPwm
- ...
-
- hp->reg = (struct HiPwmRegs *)hp->base; // 初始化自定义结构体HiPwm
- hp->supportPolarity = false; // 初始化自定义结构体HiPwm
- hp->dev.method = &g_pwmOps; // PwmMethod的实例化对象的挂载
- hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE; // 初始化PwmDev
- hp->dev.cfg.period = PWM_DEFAULT_PERIOD; // 初始化PwmDev
- hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY; // 初始化PwmDev
- hp->dev.cfg.status = PWM_DISABLE_STATUS; // 初始化PwmDev
- hp->dev.cfg.number = 0; // 初始化PwmDev
- hp->dev.busy = false; // 初始化PwmDev
- if (PwmDeviceAdd(obj, &(hp->dev)) != HDF_SUCCESS) { // 【重要】调用核心层函数,初始化hp->dev的设备和服务。
- OsalIoUnmap((void *)hp->base);
- return HDF_FAILURE;
- }
- return HDF_SUCCESS;
- }
- ```
-
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
-
- ```c
- static void HdfPwmRelease(struct HdfDeviceObject *obj)
- {
- struct HiPwm *hp = NULL;
- ...
- hp = (struct HiPwm *)obj->service; // 这里有HdfDeviceObject到HiPwm的强制转化
- ...
- PwmDeviceRemove(obj, &(hp->dev)); // 【必要】调用核心层函数,释放PwmDev的设备和服务,这里有HiPwm到PwmDev的强制转化。
- HiPwmRemove(hp); // 释放HiPwm
- }
- ```
-
-4. 驱动调试。
-
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如PWM控制状态,中断响应情况等。
\ No newline at end of file
+ ```c
+ struct PwmMethod g_pwmOps = { // pwm_hi35xx.c中的示例:钩子函数实例化
+ .setConfig = HiPwmSetConfig, // 配置属性
+ };
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化PwmDev成员,调用核心层PwmDeviceAdd函数,完成PWM控制器的添加。
+
+ ```c
+ // 此处Bind函数为空函数,可与Init函数结合,也可根据驱动适配者需要实现相关操作。
+ static int32_t HdfPwmBind(struct HdfDeviceObject *obj)
+ {
+ (void)obj;
+ return HDF_SUCCESS;
+ }
+
+ static int32_t HdfPwmInit(struct HdfDeviceObject *obj)
+ {
+ int ret;
+ struct HiPwm *hp = NULL;
+ ......
+ hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp));
+ ......
+ ret = HiPwmProbe(hp, obj); // 【必要】实现见下
+ ......
+ return ret;
+
+ static int32_t HiPwmProbe(struct HiPwm *hp, struct HdfDeviceObject *obj)
+ {
+ uint32_t tmp;
+ struct DeviceResourceIface *iface = NULL;
+
+ iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); // 初始化自定义结构体HiPwm
+ ......
+
+ hp->reg = (struct HiPwmRegs *)hp->base; // 初始化自定义结构体HiPwm
+ hp->supportPolarity = false; // 初始化自定义结构体HiPwm
+ hp->dev.method = &g_pwmOps; // PwmMethod的实例化对象的挂载
+ hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE; // 初始化PwmDev
+ hp->dev.cfg.period = PWM_DEFAULT_PERIOD; // 初始化PwmDev
+ hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY; // 初始化PwmDev
+ hp->dev.cfg.status = PWM_DISABLE_STATUS; // 初始化PwmDev
+ hp->dev.cfg.number = 0; // 初始化PwmDev
+ hp->dev.busy = false; // 初始化PwmDev
+ if (PwmDeviceAdd(obj, &(hp->dev)) != HDF_SUCCESS) { // 【重要】调用核心层函数,初始化hp->dev的设备和服务。
+ OsalIoUnmap((void *)hp->base);
+ return HDF_FAILURE;
+ }
+ return HDF_SUCCESS;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+
+ ```c
+ static void HdfPwmRelease(struct HdfDeviceObject *obj)
+ {
+ struct HiPwm *hp = NULL;
+ ......
+ hp = (struct HiPwm *)obj->service; // 这里有HdfDeviceObject到HiPwm的强制转化
+ ......
+ PwmDeviceRemove(obj, &(hp->dev)); // 【必要】调用核心层函数,释放PwmDev的设备和服务,这里有HiPwm到PwmDev的强制转化。
+ HiPwmRemove(hp); // 释放HiPwm
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如PWM控制状态等。
diff --git a/zh-cn/device-dev/driver/driver-platform-regulator-des.md b/zh-cn/device-dev/driver/driver-platform-regulator-des.md
index 058114fcbb760cfa5ea74bd4d4610893a46dc147..81eb60909ce94a4651ecbf36859cfbb4801e8daf 100755
--- a/zh-cn/device-dev/driver/driver-platform-regulator-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-regulator-des.md
@@ -1,15 +1,19 @@
# Regulator
-
## 概述
### 功能简介
Regulator模块用于控制系统中某些设备的电压/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过Regulator关闭其电源供应;或者降低提供给该模块的电压、电流大小。
+
Regulator接口定义了操作Regulator设备的通用方法集合,包括:
+
- Regulator设备句柄获取和销毁。
+
- Regulator设备电压、电流的设置。
+
- Regulator设备使能和关闭。
+
- Regulator设备电压、电流和状态的获取。
@@ -17,32 +21,35 @@ Regulator接口定义了操作Regulator设备的通用方法集合,包括:
- 校准器
- 当输入电压和输出负载发生变化时可以通过软件调整,使其能够提供稳定的输出电压。
+ 当输入电压和输出负载发生变化时可以通过软件调整,使其能够提供稳定的输出电压。
- Consumer
- 由Regulator供电的设备统称为Consumer, 其可分为静态和动态两类:
+ 由Regulator供电的设备统称为Consumer, 其可分为静态和动态两类:
+
+ * 静态:不需要改变电压电流,只需要开关电源,通常在bootloader、firmware、kernel board阶段被设置。
- * 静态:不需要改变电压电流,只需要开关电源,通常在bootloader、firmware、kernel board阶段被设置。
- * 动态:根据操作需求改变电压电流。
+ * 动态:根据操作需求改变电压电流。
- PMIC(Power Management IC)
- 电源管理芯片,内含多个电源甚至其他子系统。
+ 电源管理芯片,内含多个电源甚至其他子系统。
### 运作机制
-在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问,实现便捷管理和节约资源的目的。
+在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1所示),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问,实现便捷管理和节约资源的目的。
Regulator模块各分层的作用为:
- 接口层:提供打开设备,操作Regulator,关闭设备的能力。
+
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取Regulator设备的能力。
+
- 适配层:由驱动适配者实现与硬件相关的具体功能,如设备的初始化等。
**图 1** Regulator统一服务模式结构图
-![image1](figures/统一服务模式结构图.png)
+![Regulator统一服务模式结构图](figures/统一服务模式结构图.png)
### 约束与限制
@@ -55,13 +62,14 @@ Regulator模块API当前仅支持内核态调用。
Regulator主要用于:
1. 用于控制系统中某些设备的电压/电流供应。
+
2. 用于稳压电源的管理。
### 接口说明
Regulator模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/regulator_if.h。
-**表1** Regulator设备API接口说明
+**表 1** Regulator设备API接口说明
| 接口名 | 接口描述 |
| --------------------- | ------------------------- |
@@ -80,9 +88,9 @@ Regulator模块提供的主要接口如表1所示,具体API详见//drivers/hdf
使用Regulator设备的一般流程如图2所示。
-**图 2** Regulator设备使用流程图
+**图 2** Regulator设备使用流程图
-![](figures/REGULATOR设备使用流程图.png)
+![Regulator设备使用流程图](figures/REGULATOR设备使用流程图.png)
#### 获取Regulator设备句柄
@@ -92,26 +100,26 @@ Regulator模块提供的主要接口如表1所示,具体API详见//drivers/hdf
DevHandle RegulatorOpen(const char *name);
```
-**表2** RegulatorOpen参数和返回值描述
+**表 2** RegulatorOpen参数和返回值描述
| 参数 | 参数描述 |
| ---------- | ----------------------------- |
-| name | Regulator设备名称 |
+| name | 字符指针,Regulator设备名称 |
| **返回值** | **返回值描述** |
| handle | 获取成功返回Regulator设备句柄 |
| NULL | 获取失败 |
-
-
```c
-/* Regulator设备名称 */
+// Regulator设备名称
const char *name = "regulator_virtual_1";
DevHandle handle = NULL;
-/* 获取Regulator设备句柄 */
+// 获取Regulator设备句柄
handle = RegulatorOpen(name);
-if (handle == NULL) {
- /* 错误处理 */
+if (handle == NULL) {
+ // 错误处理
+ HDF_LOGE("RegulatorOpen: open regulator fail.\n");
+ return HDF_FAILURE;
}
```
@@ -123,14 +131,14 @@ if (handle == NULL) {
void RegulatorClose(DevHandle handle);
```
-**表3** RegulatorClose参数描述
+**表 3** RegulatorClose参数描述
| 参数 | 参数描述 |
| ------ | ----------------- |
-| handle | Regulator设备句柄 |
+| handle | DevHandle类型,Regulator设备句柄 |
```c
-/* 销毁Regulator设备句柄 */
+// 销毁Regulator设备句柄
RegulatorClose(handle);
```
@@ -142,24 +150,24 @@ RegulatorClose(handle);
int32_t RegulatorEnable(DevHandle handle);
```
-**表4** RegulatorEnable参数描述
+**表 4** RegulatorEnable参数描述
| 参数 | 参数描述 |
| ---------- | ----------------- |
-| handle | Regulator设备句柄 |
+| handle | DevHandle类型,Regulator设备句柄 |
| **返回值** | **返回值描述** |
-| 0 | 使能成功 |
-| 负数 | 使能失败 |
-
-
+| HDF_SUCCESS | 使能Regulator设备成功 |
+| 负数 | 使能Regulator设备失败 |
```c
int32_t ret;
-/* 启用Regulator设备 */
+// 启用Regulator设备
ret = RegulatorEnable(handle);
-if (ret != 0) {
- /* 错误处理 */
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("RegulatorEnable: enable regulator fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -171,22 +179,24 @@ if (ret != 0) {
int32_t RegulatorDisable(DevHandle handle);
```
-**表5** RegulatorDisable参数描述
+**表 5** RegulatorDisable参数描述
| 参数 | 参数描述 |
| ---------- | ----------------- |
-| handle | Regulator设备句柄 |
+| handle | DevHandle类型,Regulator设备句柄 |
| **返回值** | **返回值描述** |
-| 0 | 禁用成功 |
-| 负数 | 禁用失败 |
+| HDF_SUCCESS | 禁用Regulator设备成功 |
+| 负数 | 禁用Regulator设备失败 |
```c
int32_t ret;
-/* 禁用Regulator设备 */
+// 禁用Regulator设备
ret = RegulatorDisable(handle);
-if (ret != 0) {
- /* 错误处理 */
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("RegulatorDisable: disable regulator fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -198,23 +208,25 @@ if (ret != 0) {
int32_t RegulatorForceDisable(DevHandle handle);
```
-**表6** RegulatorForceDisable参数描述
+**表 6** RegulatorForceDisable参数描述
| 参数 | 参数描述 |
| ---------- | ----------------- |
-| handle | Regulator设备句柄 |
+| handle | DevHandle类型,Regulator设备句柄 |
| **返回值** | **返回值描述** |
-| 0 | 禁用成功 |
-| 负数 | 禁用失败 |
+| HDF_SUCCESS | 强制禁用Regulator设备成功 |
+| 负数 | 强制禁用Regulator设备失败 |
```c
int32_t ret;
-/* 强制禁用Regulator设备 */
+// 强制禁用Regulator设备
ret = RegulatorForceDisable(handle);
-if (ret != 0) {
- /* 错误处理 */
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("RegulatorForceDisable: force disable regulator fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -224,26 +236,28 @@ if (ret != 0) {
int32_t RegulatorSetVoltage(DevHandle handle, uint32_t minUv, uint32_t maxUv);
```
-**表7** RegulatorSetVoltage参数描述
+**表 7** RegulatorSetVoltage参数描述
| 参数 | 参数描述 |
| ---------- | ----------------- |
-| handle | Regulator设备句柄 |
-| minUv | 最小电压 |
-| maxUv | 最大电压 |
+| handle | DevHandle类型,Regulator设备句柄 |
+| minUv | uint32_t类型,最小电压 |
+| maxUv | uint32_t类型,最大电压 |
| **返回值** | **返回值描述** |
-| 0 | 设置成功 |
-| 负数 | 设置失败 |
+| HDF_SUCCESS | 设置Regulator输出电压范围成功 |
+| 负数 | 设置Regulator输出电压范围失败 |
```c
int32_t ret;
int32_t minUv = 0; // 最小电压为0µV
int32_t maxUv = 20000; // 最大电压为20000µV
-/* 设置Regulator电压输出电压范围 */
+// 设置Regulator电压输出电压范围
ret = RegulatorSetVoltage(handle, minUv, maxUv);
-if (ret != 0) {
- /* 错误处理 */
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("RegulatorSetVoltage: regulator set Voltage fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -253,25 +267,27 @@ if (ret != 0) {
int32_t RegulatorGetVoltage(DevHandle handle, uint32_t *voltage);
```
-**表8** RegulatorGetVoltage参数描述
+**表 8** RegulatorGetVoltage参数描述
| 参数 | 参数描述 |
| ---------- | ----------------- |
-| handle | Regulator设备句柄 |
-| *voltage | 参数指针 |
+| handle | DevHandle类型,Regulator设备句柄 |
+| voltage | uint32_t类型指针,待获取的Regulator电压 |
| **返回值** | **返回值描述** |
-| 0 | 获取成功 |
-| 负数 | 获取失败 |
+| HDF_SUCCESS | 获取Regulator电压成功 |
+| 负数 | 获取Regulator电压失败 |
```c
int32_t ret;
uint32_t voltage;
-/*获取Regulator电压*/
+// 获取Regulator电压
ret = RegulatorGetVoltage(handle, &voltage);
-if (ret != 0) {
- /*错误处理*/
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("RegulatorGetVoltage: regulator get Voltage fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -281,26 +297,28 @@ if (ret != 0) {
int32_t RegulatorSetCurrent(DevHandle handle, uint32_t minUa, uint32_t maxUa);
```
-**表9** RegulatorSetCurrent参数描述
+**表 9** RegulatorSetCurrent参数描述
| 参数 | 参数描述 |
| ---------- | ----------------- |
-| handle | Regulator设备句柄 |
-| minUa | 最小电流 |
-| maxUa | 最大电流 |
+| handle | DevHandle类型,Regulator设备句柄 |
+| minUa | uint32_t类型,最小电流 |
+| maxUa | uint32_t类型,最大电流 |
| **返回值** | **返回值描述** |
-| 0
| 设置成功 |
-| 负数 | 设置失败 |
+| HDF_SUCCESS | 设置Regulator输出电流范围成功 |
+| 负数 | 设置Regulator输出电流范围失败 |
```c
int32_t ret;
int32_t minUa = 0; // 最小电流为0μA
int32_t maxUa = 200; // 最大电流为200μA
-/* 设置Regulator输出电流范围 */
+// 设置Regulator输出电流范围
ret = RegulatorSetCurrent(handle, minUa, maxUa);
-if (ret != 0) {
- /* 错误处理 */
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("RegulatorSetCurrent: regulator set current fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -310,24 +328,26 @@ if (ret != 0) {
int32_t RegulatorGetCurrent(DevHandle handle, uint32_t *regCurrent);
```
-**表10** RegulatorGetCurrent参数描述
+**表 10** RegulatorGetCurrent参数描述
| 参数 | 参数描述 |
| ----------- | ----------------- |
-| handle | Regulator设备句柄 |
-| *regCurrent | 参数指针 |
+| handle | DevHandle类型,Regulator设备句柄 |
+| regCurrent | uint32_t类型指针,待获取的Regulator电流 |
| **返回值** | **返回值描述** |
-| 0 | 获取成功 |
+| HDF_SUCCESS | 获取成功 |
| 负数 | 获取失败 |
```c
int32_t ret;
uint32_t regCurrent;
-/* 获取Regulator电流 */
+// 获取Regulator电流
ret = RegulatorGetCurrent(handle, ®Current);
-if (ret != 0) {
- /* 错误处理 */
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("RegulatorGetCurrent: regulator get current fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -337,29 +357,29 @@ if (ret != 0) {
int32_t RegulatorGetStatus(DevHandle handle, uint32_t *status);
```
-**表11** RegulatorGetStatus参数描述
+**表 11** RegulatorGetStatus参数描述
| 参数 | 参数描述 |
| ---------- | ----------------- |
-| handle | Regulator设备句柄 |
-| *status | 参数指针 |
+| handle | DevHandle类型,Regulator设备句柄 |
+| status | uint32_t类型指针,待获取Regulator状态 |
| **返回值** | **返回值描述** |
-| 0 | 获取成功 |
+| HDF_SUCCESS | 获取成功 |
| 负数 | 获取失败 |
```c
int32_t ret;
uint32_t status;
-/* 获取Regulator状态 */
+// 获取Regulator状态
ret = RegulatorGetStatus(handle, &status);
-if (ret != 0) {
- /* 错误处理 */
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("RegulatorGetStatus: regulator get status fail, ret:%d\n", ret);
+ return ret;
}
```
-
-
## 使用实例
本例拟对Hi3516DV300开发板上Regulator设备进行简单的读取操作。
diff --git a/zh-cn/device-dev/driver/driver-platform-regulator-develop.md b/zh-cn/device-dev/driver/driver-platform-regulator-develop.md
index 4db05339c9ca1561690cca164f41cb788c862e7c..346a285a0380c67b7b2a7fdf8b742af90b8433e0 100755
--- a/zh-cn/device-dev/driver/driver-platform-regulator-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-regulator-develop.md
@@ -1,6 +1,5 @@
# Regulator
-
## 概述
### 功能简介
@@ -9,19 +8,21 @@ Regulator模块用于控制系统中各类设备的电压/电流供应。在嵌
### 运作机制
-在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
+在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1所示),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
Regulator模块各分层的作用为:
- 接口层:提供打开设备,操作Regulator,关闭设备的能力。
+
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取Regulator设备的能力。
+
- 适配层:由驱动适配者实现与硬件相关的具体功能,如设备的初始化等。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
-**图 1** 统一服务模式结构图
+**图 1** Regulator统一服务模式结构图
-![image1](figures/统一服务模式结构图.png)
+![Regulator统一服务模式结构图](figures/统一服务模式结构图.png)
### 约束与限制
@@ -57,30 +58,33 @@ struct RegulatorMethod {
**表 1** RegulatorMethod 结构体成员的钩子函数功能说明
-| 成员函数 | 入参 | 返回值 | 功能 |
+| 成员函数 | 入参 | 返回值 | 功能 |
| ------------ | ----------------------------------------------------------- | ----------------- | ---------------- |
-| open | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 打开设备 |
-| close | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 关闭设备 |
-| release | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 释放设备句柄 |
-| enable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 使能 |
-| disable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 禁用 |
-| forceDisable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 强制禁用 |
-| setVoltage | node:结构体指针,核心层Regulator节点
minUv:uint32_t变量,最小电压
maxUv:uint32_t变量,最大电压 | HDF_STATUS相关状态 | 设置输出电压范围 |
-| getVoltage | node:结构体指针,核心层Regulator节点
voltage:uint32_t指针,传出电压值 | HDF_STATUS相关状态 | 获取电压 |
-| setCurrent | node:结构体指针,核心层Regulator节点
minUa:uint32_t变量,最小电流
maxUa:uint32_t变量,最大电流 | HDF_STATUS相关状态 | 设置输出电流范围 |
-| getCurrent | node:结构体指针,核心层Regulator节点
regCurrent:uint32_t指针,传出电流值 | HDF_STATUS相关状态 | 获取电流 |
-| getStatus | node:结构体指针,核心层Regulator节点
status:uint32_t指针,传出状态值 | HDF_STATUS相关状态 | 获取设备状态 |
+| open | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 打开设备 |
+| close | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 关闭设备 |
+| release | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 释放设备句柄 |
+| enable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 使能 |
+| disable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 禁用 |
+| forceDisable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 强制禁用 |
+| setVoltage | node:结构体指针,核心层Regulator节点
minUv:uint32_t类型,最小电压
maxUv:uint32_t类型,最大电压 | HDF_STATUS相关状态 | 设置输出电压范围 |
+| getVoltage | node:结构体指针,核心层Regulator节点
voltage:uint32_t类型指针,传出电压值 | HDF_STATUS相关状态 | 获取电压 |
+| setCurrent | node:结构体指针,核心层Regulator节点
minUa:uint32_t类型,最小电流
maxUa:uint32_t类型,最大电流 | HDF_STATUS相关状态 | 设置输出电流范围 |
+| getCurrent | node:结构体指针,核心层Regulator节点
regCurrent:uint32_t类型指针,传出电流值 | HDF_STATUS相关状态 | 获取电流 |
+| getStatus | node:结构体指针,核心层Regulator节点
status:uint32_t类型指针,传出状态值 | HDF_STATUS相关状态 | 获取设备状态 |
### 开发步骤
Regulator模块适配包含以下四个步骤:
-- 实例化驱动入口。
-- 配置属性文件。
-- 实例化核心层接口函数。
-- 驱动调试。
+- 实例化驱动入口
+
+- 配置属性文件
+
+- 实例化核心层接口函数
-1. 实例化驱动入口:
+- 驱动调试
+
+1. 实例化驱动入口
驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。
@@ -91,129 +95,128 @@ Regulator模块适配包含以下四个步骤:
```c
struct HdfDriverEntry g_regulatorDriverEntry = {
.moduleVersion = 1,
- .moduleName = "virtual_regulator_driver", // 【必要且与HCS文件中里面的moduleName匹配】
- .Init = VirtualRegulatorInit,
- .Release = VirtualRegulatorRelease,
+ .moduleName = "virtual_regulator_driver", // 【必要且与HCS文件中里面的moduleName匹配】
+ .Init = VirtualRegulatorInit, // 见Init参考
+ .Release = VirtualRegulatorRelease, // 见Release参考
};
- /* 调用HDF_INIT将驱动入口注册到HDF框架中 */
- HDF_INIT(g_regulatorDriverEntry);
+ HDF_INIT(g_regulatorDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
```
-2. 配置属性文件:
+2. 配置属性文件
+
+ 以Hi3516DV300开发板为例,在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
- 以Hi3516DV300开发板为例,在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
+ deviceNode信息与驱动入口注册相关,器件属性值与核心层RegulatorNode成员的默认值或限制范围有密切关系。
- deviceNode信息与驱动入口注册相关,器件属性值与核心层RegulatorNode成员的默认值或限制范围有密切关系。
+ 由于采用了统一服务模式,device_info.hcs文件中第一个设备节点必须为Regulator管理器,其各项参数必须如如表2所示:
- 由于采用了统一服务模式,device_info.hcs文件中第一个设备节点必须为Regulator管理器,其各项参数必须如下设置:
+ **表 2** device_info.hcs节点参数说明
- | 成员名 | 值 |
- | --------------- | ------------------------------------------------------------ |
- | policy | 具体配置为0,不发布服务 |
- | priority | 驱动启动优先级(0-200)。值越大优先级越低,优先级相同则不保证device的加载顺序 |
- | permission | 驱动权限 |
- | moduleName | 固定为HDF_PLATFORM_REGULATOR_MANAGER |
- | serviceName | 固定为HDF_PLATFORM_REGULATOR_MANAGER |
- | deviceMatchAttr | 没有使用,可忽略 |
+ | 成员名 | 值 |
+ | --------------- | ------------------------------------------------------------ |
+ | policy | 驱动服务发布的策略,Regulator管理器具体配置为1,表示驱动对内核态发布服务 |
+ | priority | 驱动启动优先级(0-200)。值越大优先级越低,优先级相同则不保证device的加载顺序,regulator管理器具体配置为50 |
+ | permission | 驱动创建设备节点权限,Regulator管理器具体配置为0664 |
+ | moduleName | 驱动名称,Regulator管理器固定为HDF_PLATFORM_REGULATOR_MANAGER |
+ | serviceName | 驱动对外发布服务的名称,Regulator管理器固定为HDF_PLATFORM_REGULATOR_MANAGER |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,Regulator管理器设置为hdf_platform_regulator_manager |
- 从第二个节点开始配置具体Regulator控制器信息,此节点并不表示某一路Regulator控制器,而是代表一个资源性质设备,用于描述一类Regulator控制器的信息。本例只有一个Regulator设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在regulator\_config文件中增加对应的器件属性。
+ 从第二个节点开始配置具体Regulator控制器信息,此节点并不表示某一路Regulator控制器,而是代表一个资源性质设备,用于描述一类Regulator控制器的信息。本例只有一个Regulator设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在regulator_config.hcs文件中增加对应的器件属性。
- device_info.hcs 配置参考
- ```c
- root {
- device_info {
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_regulator :: device {
- device0 :: deviceNode { // 为每一个Regulator控制器配置一个HDF设备节点,存在多个时添加,否则不用。
- policy = 1; // 2:用户态、内核态均可见;1:内核态可见;0:不需要发布服务。
- priority = 50; // 驱动启动优先级
- permission = 0644; // 驱动创建设备节点权限
- /* 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 */
- moduleName = "HDF_PLATFORM_REGULATOR_MANAGER";
- serviceName = "HDF_PLATFORM_REGULATOR_MANAGER"; // 【必要且唯一】驱动对外发布服务的名称
- /* 【必要】用于配置控制器私有数据,要与regulator_config.hcs中对应控制器保持一致,具体的控制器信息在regulator_config.hcs中。 */
- deviceMatchAttr = "hdf_platform_regulator_manager";
- }
- device1 :: deviceNode {
- policy = 0;
- priority = 55;
- permission = 0644;
- moduleName = "linux_regulator_adapter";
- deviceMatchAttr = "linux_regulator_adapter";
- }
- }
- }
- }
- }
- ```
-
- - regulator\_config.hcs配置参考
-
- ```c
- root {
- platform {
- regulator_config {
- match_attr = "linux_regulator_adapter";
- template regulator_controller { // 【必要】模板配置,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。
- device_num = 1;
- name = "";
- devName = "regulator_adapter_consumer01";
- supplyName = "";
- mode = 1;
- minUv = 0;
- maxUv = 20000;
- minUa = 0;
- maxUa = 0;
- }
- controller_0x130d0000 :: regulator_controller {
- device_num = 1;
- name = "regulator_adapter_1";
- devName = "regulator_adapter_consumer01";
- supplyName = "virtual-regulator-hdf-adapter";
- mode = 1;
- minUv = 1000;
- maxUv = 50000;
- minUa = 0;
- maxUa = 0;
- }
- /* 每个Regulator控制器对应一个controller节点,如存在多个Regulator控制器,请依次添加对应的controller节点。 */
- controller_0x130d0001 :: regulator_controller {
- device_num = 1;
- name = "regulator_adapter_2";
- devName = "regulator_adapter_consumer01";
- supplyName = "virtual2-regulator-hdf-adapter";
- mode = 2;
- minUv = 0;
- maxUv = 0;
- minUa = 1000;
- maxUa = 50000;
- }
- }
- }
- }
- ```
-
- 需要注意的是,新增regulator_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
-
- 例如:本例中regulator_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs" // 配置文件相对路径
- ```
-
-3. 实例化核心层接口函数:
+ ```c
+ root {
+ device_info {
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_regulator :: device {
+ device0 :: deviceNode { // 为每一个Regulator控制器配置一个HDF设备节点,存在多个时添加,否则不用。
+ policy = 1; // 2:用户态、内核态均可见;1:内核态可见;0:不需要发布服务。
+ priority = 50; // 驱动启动优先级
+ permission = 0644; // 驱动创建设备节点权限
+ moduleName = "HDF_PLATFORM_REGULATOR_MANAGER"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。
+ serviceName = "HDF_PLATFORM_REGULATOR_MANAGER"; // 【必要且唯一】驱动对外发布服务的名称
+ deviceMatchAttr = "hdf_platform_regulator_manager"; // 【必要】用于配置控制器私有数据,要与regulator_config.hcs中对应控制器保持一致,具体的控制器信息在regulator_config.hcs中。
+ }
+ device1 :: deviceNode {
+ policy = 0;
+ priority = 55;
+ permission = 0644;
+ moduleName = "linux_regulator_adapter";
+ deviceMatchAttr = "linux_regulator_adapter";
+ }
+ }
+ }
+ }
+ }
+ ```
+
+ - regulator_config.hcs配置参考
+
+ ```c
+ root {
+ platform {
+ regulator_config {
+ match_attr = "linux_regulator_adapter";
+ template regulator_controller { // 【必要】模板配置,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。
+ device_num = 1;
+ name = "";
+ devName = "regulator_adapter_consumer01";
+ supplyName = "";
+ mode = 1;
+ minUv = 0; // 最小电压
+ maxUv = 20000; // 最大电压
+ minUa = 0; // 最小电流
+ maxUa = 0; // 最大电流
+ }
+ controller_0x130d0000 :: regulator_controller {
+ device_num = 1;
+ name = "regulator_adapter_1";
+ devName = "regulator_adapter_consumer01";
+ supplyName = "virtual-regulator-hdf-adapter";
+ mode = 1;
+ minUv = 1000;
+ maxUv = 50000;
+ minUa = 0;
+ maxUa = 0;
+ }
+ // 每个Regulator控制器对应一个controller节点,如存在多个Regulator控制器,请依次添加对应的controller节点。
+ controller_0x130d0001 :: regulator_controller {
+ device_num = 1;
+ name = "regulator_adapter_2";
+ devName = "regulator_adapter_consumer01";
+ supplyName = "virtual2-regulator-hdf-adapter";
+ mode = 2;
+ minUv = 0;
+ maxUv = 0;
+ minUa = 1000;
+ maxUa = 50000;
+ }
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增regulator_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
+
+ 例如:本例中regulator_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化核心层接口函数
完成驱动入口注册之后,下一步就是对核心层RegulatorNode对象的初始化,包括驱动适配者自定义结构体(传递参数和数据),实例化RegulatorNode成员RegulatorMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
- 自定义结构体参考。
- 从驱动的角度看,RegulatorNode结构体是参数和数据的载体,HDF框架通过DeviceResourceIface将regulator\_config.hcs文件中的数值读入其中。
+ 从驱动的角度看,RegulatorNode结构体是参数和数据的载体,HDF框架通过DeviceResourceIface将regulator_config.hcs文件中的数值读入其中。
```c
- /* RegulatorNode是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
+ // RegulatorNode是核心层控制器结构体,其中的成员在Init函数中会被赋值。
struct RegulatorNode {
struct RegulatorDesc regulatorInfo;
struct DListHead node;
@@ -246,45 +249,45 @@ Regulator模块适配包含以下四个步骤:
};
```
- - 实例化RegulatorNode成员RegulatorMethod,其他成员在Init函数中初始化。
+ - 实例化RegulatorNode成员RegulatorMethod。
- ```c
- /* regulator_virtual.c中的示例:钩子函数的填充 */
- static struct RegulatorMethod g_method = {
- .enable = VirtualRegulatorEnable,
- .disable = VirtualRegulatorDisable,
- .setVoltage = VirtualRegulatorSetVoltage,
- .getVoltage = VirtualRegulatorGetVoltage,
- .setCurrent = VirtualRegulatorSetCurrent,
- .getCurrent = VirtualRegulatorGetCurrent,
- .getStatus = VirtualRegulatorGetStatus,
- };
- ```
+ ```c
+ // regulator_virtual.c中的示例:钩子函数的填充
+ static struct RegulatorMethod g_method = {
+ .enable = VirtualRegulatorEnable,
+ .disable = VirtualRegulatorDisable,
+ .setVoltage = VirtualRegulatorSetVoltage,
+ .getVoltage = VirtualRegulatorGetVoltage,
+ .setCurrent = VirtualRegulatorSetCurrent,
+ .getCurrent = VirtualRegulatorGetCurrent,
+ .getStatus = VirtualRegulatorGetStatus,
+ };
+ ```
- Init函数开发参考
- 入参:
+ 入参:
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
- 返回值:
+ 返回值:
- HDF\_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf\_core/framework/include/utils/hdf\_base.h中HDF\_STATUS定义)。
+ HDF_STATUS相关状态(表4为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
- **表 2** HDF\_STATUS相关状态
-
- | 状态(值) | 描述 |
- | ---------------------- | -------------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 描述 |
+ | ---------------------- | -------------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
- 函数说明:
+ 函数说明:
- 初始化自定义结构体和RegulatorNode成员,并通过调用核心层RegulatorNodeAdd函数挂载Regulator控制器。
+ 初始化自定义结构体和RegulatorNode成员,并通过调用核心层RegulatorNodeAdd函数挂载Regulator控制器。
```c
static int32_t VirtualRegulatorInit(struct HdfDeviceObject *device)
@@ -293,10 +296,10 @@ Regulator模块适配包含以下四个步骤:
const struct DeviceResourceNode *childNode = NULL;
...
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
- ret = VirtualRegulatorParseAndInit(device, childNode); // 【必要】实现见下
- ...
+ ret = VirtualRegulatorParseAndInit(device, childNode); // 【必要】实现见下
+ ......
}
- ...
+ ......
}
static int32_t VirtualRegulatorParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
@@ -306,39 +309,39 @@ Regulator模块适配包含以下四个步骤:
(void)device;
regNode = (struct RegulatorNode *)OsalMemCalloc(sizeof(*regNode)); //加载HCS文件
- ...
+ ......
ret = VirtualRegulatorReadHcs(regNode, node); // 读取HCS文件信息
- ...
+ ......
regNode->priv = (void *)node; // 实例化节点
regNode->ops = &g_method; // 实例化ops
ret = RegulatorNodeAdd(regNode); // 挂载节点
- ...
+ ......
}
```
- - Release函数开发参考
+ - Release函数开发参考
- 入参:
+ 入参:
- HdfDeviceObject是整个驱动对外提供的接口参数,其包含了HCS配置文件中的相关配置信息。
+ HdfDeviceObject是整个驱动对外提供的接口参数,其包含了HCS配置文件中的相关配置信息。
- 返回值:
+ 返回值:
- 无。
+ 无。
- 函数说明:
+ 函数说明:
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
```c
static void VirtualRegulatorRelease(struct HdfDeviceObject *device)
{
- ...
+ ......
RegulatorNodeRemoveAll(); // 【必要】调用核心层函数,释放RegulatorNode的设备和服务
}
```
-4. 驱动调试:
+4. 驱动调试
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。
diff --git a/zh-cn/device-dev/driver/driver-platform-rtc-des.md b/zh-cn/device-dev/driver/driver-platform-rtc-des.md
index de7a4a2ce02486f883ca3074cfcfc7738fe286f2..07325ed521523c0e056492389e6ce236a4352421 100644
--- a/zh-cn/device-dev/driver/driver-platform-rtc-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-rtc-des.md
@@ -10,6 +10,24 @@ RTC(real-time clock)为操作系统中的实时时钟设备,为操作系
在HDF框架中,RTC模块采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多会增加内存占用。通常,一个硬件系统中只需要一个RTC设备,因此RTC模块采用独立服务模式较为合适。
+独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
+
+- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
+- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
+
+**图 1** RTC独立服务模式结构图
+
+![RTC独立服务模式结构图](figures/独立服务模式结构图.png)
+
+RTC模块各分层作用:
+
+- 接口层提供打开RTC设备、RTC设备读取时间、RTC设备设置时间、RTC设备读取警报时间、RTC设备设置警报时间、RTC设备定时报警回调函数、RTC设备设置定时报警中断使能去使能、RTC设备设置RTC外频、RTC设备读取RTC外频、复位RTC、设置RTC自定义寄存器配置,读取RTC自定义寄存器配置以及关闭RTC设备的接口。
+
+- 核心层主要提供RTC控制器的创建、销毁,通过钩子函数与适配层交互。
+
+- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
+
## 使用指导
### 场景介绍
@@ -20,7 +38,7 @@ RTC主要用于提供实时时间和定时报警功能。
RTC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/rtc_if.h。
-**表1** RTC设备API接口功能介绍
+**表 1** RTC设备API接口功能介绍
| 接口名 | 接口描述 |
| -------- | -------- |
@@ -40,40 +58,42 @@ RTC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
### 使用流程
-使用RTC设备的一般流程如下图所示。
+使用RTC设备的一般流程如图2所示。
-**图1** RTC设备使用流程图
+**图 2** RTC设备使用流程图
-![image](figures/RTC设备使用流程图.png "RTC设备使用流程图")
+![RTC设备使用流程图](figures/RTC设备使用流程图.png)
#### 创建RTC设备句柄
RTC驱动加载成功后,使用驱动框架提供的查询接口并调用RTC设备驱动接口。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
-> 当前操作系统仅支持一个RTC设备。
+> 当前操作系统仅支持一个RTC设备。本文涉及的RTC的所有接口,支持内核态及用户态使用。
```c
DevHandle RtcOpen(void);
```
- **表2** RtcOpen参数和返回值描述
+**表 2** RtcOpen参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
| void | NA |
| **返回值** | **描述** |
-| handle | 操作成功返回 指针类型 |
-| NULL | 操作失败 |
+| handle | 获取对应的RTC设备句柄成功 |
+| NULL | 获取对应的RTC设备句柄成功失败 |
```c
-DevHandle handle = NULL;
+DevHandle handle = NULL;
-/* 获取RTC句柄 */
+// 获取RTC句柄
handle = RtcOpen();
-if (handle == NULL) {
- /* 错误处理 */
+if (handle == NULL) {
+ // 错误处理
+ HDF_LOGE("open rtc fail!");
+ return HDF_FAILURE;
}
```
@@ -85,37 +105,45 @@ if (handle == NULL) {
int32_t RtcRegisterAlarmCallback(DevHandle handle, enum RtcAlarmIndex alarmIndex, RtcAlarmCallback cb);
```
- **表3** RtcRegisterAlarmCallback参数和返回值描述
+**表 3** RtcRegisterAlarmCallback参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
-| handle | RTC设备句柄 |
-| alarmIndex | 报警索引 |
+| handle | DevHandle类型,RTC设备句柄 |
+| alarmIndex | 枚举类型,报警索引 |
| cb | 定时报警回调函数 |
| **返回值** | **描述** |
-| 0 | 操作成功 |
+| HDF_SUCCESS | 操作成功 |
| 负数 | 操作失败 |
- 注册RTC_ALARM_INDEX_A的定时报警处理函数, 示例如下:
+注册RTC_ALARM_INDEX_A的定时报警处理函数, 示例如下:
```c
-/* 用户注册RTC定时报警回调函数的方法 */
+// 用户注册RTC定时报警回调函数的方法
int32_t RtcAlarmACallback(enum RtcAlarmIndex alarmIndex)
{
if (alarmIndex == RTC_ALARM_INDEX_A) {
- /* 报警A的处理 */
+ // 报警A的处理
+ HDF_LOGD("RTC Alarm A callback function\n\r");
+ return HDF_SUCCESS;
} else if (alarmIndex == RTC_ALARM_INDEX_B) {
- /* 报警B的处理 */
+ // 报警B的处理
+ HDF_LOGD("RTC Alarm B callback function\n\r");
+ return HDF_SUCCESS;
} else {
- /* 错误处理 */
+ // 错误处理
+ HDF_LOGE("RTC Alarm callback function fail!\n");
+ return HDF_FAILURE;
}
- return 0;
+ return HDF_SUCCESS;
}
int32_t ret;
-/* 注册报警A的定时回调函数 */
+// 注册报警A的定时回调函数
ret = RtcRegisterAlarmCallback(handle, RTC_ALARM_INDEX_A, RtcAlarmACallback);
-if (ret != 0) {
- /* 错误处理 */
+if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("register alarm callback fail, ret:%d", ret);
+ return ret;
}
```
@@ -127,27 +155,29 @@ if (ret != 0) {
系统从RTC读取时间信息,包括年、月、星期、日、时、分、秒、毫秒,则可以通过以下函数完成:
```c
- int32_t RtcReadTime(DevHandle handle, struct RtcTime \*time);
+ int32_t RtcReadTime(DevHandle handle, struct RtcTime *time);
```
- **表4** RtcReadTime参数和返回值描述
+ **表 4** RtcReadTime参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | time | RTC读取时间信息,包括年、月、星期、日、时、分、秒、毫秒 |
- | **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | time | 结构体指针类型,RTC读取时间信息,包括年、月、星期、日、时、分、秒、毫秒 |
+ | **返回值** | **描述** |
+ | HDF_SUCCESS | 读取RTC时间成功 |
+ | 负数 | 读取RTC时间失败 |
```c
int32_t ret;
struct RtcTime tm;
- /* 系统从RTC读取时间信息 */
+ // 系统从RTC读取时间信息
ret = RtcReadTime(handle, &tm);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("%s:read time fail, ret:%d", __func__, ret);
+ return ret;
}
```
@@ -156,18 +186,18 @@ if (ret != 0) {
设置RTC时间,则可以通过以下函数完成:
```c
- int32_t RtcWriteTime(DevHandle handle, struct RtcTime \*time);
+ int32_t RtcWriteTime(DevHandle handle, struct RtcTime *time);
```
- **表5** RtcWriteTime参数和返回值描述
+ **表 5** RtcWriteTime参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | time | 写RTC时间信息,包括年、月、星期、日、时、分、秒、毫秒 |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | time | 结构体指针类型,写RTC时间信息,包括年、月、星期、日、时、分、秒、毫秒 |
| **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | HDF_SUCCESS | 设置RTC时间成功 |
+ | 负数 | 设置RTC时间失败 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> RTC起始时间为UTC 1970/01/01 Thursday 00:00:00,年的最大取值按照用户器件手册要求计算配置,星期不用配置。
@@ -176,7 +206,7 @@ if (ret != 0) {
int32_t ret;
struct RtcTime tm;
- /* 设置RTC时间为 UTC 2020/01/01 00:59:00 .000 */
+ // 设置RTC时间为 UTC 2020/01/01 00:59:00 .000
tm.year = 2020;
tm.month = 01;
tm.day = 01;
@@ -184,10 +214,12 @@ if (ret != 0) {
tm.minute = 59;
tm.second = 00;
tm.millisecond = 0;
- /* 写RTC时间信息 */
+ // 写RTC时间信息
ret = RtcWriteTime(handle, &tm);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("write time fail, ret:%d", ret);
+ return ret;
}
```
@@ -196,28 +228,30 @@ if (ret != 0) {
如果需要读取定时报警时间,则可以通过以下函数完成:
```c
- int32_t RtcReadAlarm(DevHandle handle, enum RtcAlarmIndex alarmIndex, struct RtcTime \*time);
+ int32_t RtcReadAlarm(DevHandle handle, enum RtcAlarmIndex alarmIndex, struct RtcTime *time);
```
- **表6** RtcReadAlarm参数和返回值描述
+ **表 6** RtcReadAlarm参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | alarmIndex | 报警索引 |
- | time | RTC报警时间信息,包括年、月、星期、日、时、分、秒、毫秒 |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | alarmIndex | 枚举类型,报警索引 |
+ | time | 结构体指针类型,RTC报警时间信息,包括年、月、星期、日、时、分、秒、毫秒 |
| **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | HDF_SUCCESS | 读取RTC报警时间成功 |
+ | 负数 | 读取RTC报警时间失败 |
```c
int32_t ret;
struct RtcTime alarmTime;
- /* 读RTC_ALARM_INDEX_A索引的RTC定时报警时间信息 */
+ // 读RTC_ALARM_INDEX_A索引的RTC定时报警时间信息
ret = RtcReadAlarm(handle, RTC_ALARM_INDEX_A, &alarmTime);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("read alarm fail, ret:%d", ret);
+ return ret;
}
```
@@ -226,19 +260,19 @@ if (ret != 0) {
根据报警索引设置RTC报警时间,通过以下函数完成:
```c
- int32_t RtcWriteAlarm(DevHandle handle, enum RtcAlarmIndex alarmIndex, struct RtcTime \*time);
+ int32_t RtcWriteAlarm(DevHandle handle, enum RtcAlarmIndex alarmIndex, struct RtcTime *time);
```
- **表7** RtcWriteAlarm参数和返回值描述
+ **表 7** RtcWriteAlarm参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | alarmIndex | 报警索引 |
- | time | RTC报警时间信息,包括年、月、星期、日、时、分、秒、毫秒 |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | alarmIndex | 枚举类型,报警索引 |
+ | time | 结构体指针类型,RTC报警时间信息,包括年、月、星期、日、时、分、秒、毫秒 |
| **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | HDF_SUCCESS | 设置RTC报警时间成功 |
+ | 负数 | 设置RTC报警时间失败 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> RTC起始时间为UTC 1970/01/01 Thursday 00:00:00,年的最大取值按照用户器件手册要求计算配置,星期不用配置。
@@ -247,7 +281,7 @@ if (ret != 0) {
int32_t ret;
struct RtcTime alarmTime;
- /* 设置RTC报警时间为2020/01/01 00:59:59 .000 */
+ // 设置RTC报警时间为2020/01/01 00:59:59 .000
alarmTime.year = 2020;
alarmTime.month = 01;
alarmTime.day = 01;
@@ -255,10 +289,12 @@ if (ret != 0) {
alarmTime.minute = 59;
alarmTime.second = 59;
alarmTime.millisecond = 0;
- /* 设置RTC_ALARM_INDEX_A索引的定时报警时间 */
+ // 设置RTC_ALARM_INDEX_A索引的定时报警时间
ret = RtcWriteAlarm(handle, RTC_ALARM_INDEX_A, &alarmTime);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("write alarm fail, ret:%d", ret);
+ return ret;
}
```
@@ -270,24 +306,26 @@ if (ret != 0) {
int32_t RtcAlarmInterruptEnable(DevHandle handle, enum RtcAlarmIndex alarmIndex, uint8_t enable);
```
- **表8** RtcAlarmInterruptEnable参数和返回值描述
+ **表 8** RtcAlarmInterruptEnable参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | alarmIndex | 报警索引 |
- | enable | RTC报警中断配置,1:使能,0:去使能 |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | alarmIndex | 枚举类型,报警索引 |
+ | enable | uint8_t类型,RTC报警中断配置,1:使能,0:去使能 |
| **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | HDF_SUCCESS | 设置定时报警中断使能或去使能成功 |
+ | 负数 | 设置定时报警中断使能或去使能失败 |
```c
int32_t ret;
- /* 设置RTC报警中断使能 */
+ // 设置RTC报警中断使能
ret = RtcAlarmInterruptEnable(handle, RTC_ALARM_INDEX_A, 1);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("alarm interrupt enable fail, ret:%d", ret);
+ return ret;
}
```
@@ -296,27 +334,28 @@ if (ret != 0) {
读取RTC外接晶体振荡频率,可以通过以下函数完成:
```c
- int32_t RtcGetFreq(DevHandle handle, uint32_t \*freq);
+ int32_t RtcGetFreq(DevHandle handle, uint32_t *freq);
```
- **表9** RtcGetFreq参数和返回值描述
+ **表 9** RtcGetFreq参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | freq | RTC的外接晶体振荡频率,单位(HZ) |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | freq | uint32_t类型指针,RTC的外接晶体振荡频率,单位(HZ) |
| **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | HDF_SUCCESS | 读取RTC外频成功 |
+ | 负数 | 读取RTC外频失败 |
```c
int32_t ret;
uint32_t freq = 0;
- /* 读取RTC外接晶体振荡频率 */
+ // 读取RTC外接晶体振荡频率
ret = RtcGetFreq(handle, &freq);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("get freq fail, ret:%d", ret);
}
```
@@ -328,24 +367,26 @@ if (ret != 0) {
int32_t RtcSetFreq(DevHandle handle, uint32_t freq);
```
- **表10** RtcSetFreq参数和返回值描述
+ **表 10** RtcSetFreq参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | freq | RTC的外接晶体振荡频率,单位(HZ) |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | freq | uint32_t类型,RTC的外接晶体振荡频率,单位(HZ) |
| **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | HDF_SUCCESS | 配置RTC外频成功 |
+ | 负数 | 配置RTC外频失败 |
```c
int32_t ret;
- uint32_t freq = 32768; /* 32768 Hz */
+ uint32_t freq = 32768; // 32768 Hz
- /* 设置RTC外接晶体振荡频率,注意按照器件手册要求配置RTC外频 */
+ // 设置RTC外接晶体振荡频率,注意按照器件手册要求配置RTC外频
ret = RtcSetFreq(handle, freq);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("set freq fail, ret:%d", ret);
+ return ret;
}
```
@@ -357,22 +398,24 @@ if (ret != 0) {
int32_t RtcReset(DevHandle handle);
```
- **表11** RtcReset参数和返回值描述
+ **表 11** RtcReset参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
+ | handle | DevHandle类型,RTC设备句柄 |
| **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | HDF_SUCCESS | 复位RTC成功 |
+ | 负数 | 复位RTC失败 |
```c
int32_t ret;
- /* 复位RTC,各配置寄存器恢复默认值 */
+ // 复位RTC,各配置寄存器恢复默认值
ret = RtcReset(handle);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("reset fail, ret:%d", ret);
+ return ret;
}
```
@@ -381,29 +424,31 @@ if (ret != 0) {
按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值,通过以下函数完成:
```c
- int32_t RtcReadReg(DevHandle handle, uint8_t usrDefIndex, uint8_t \*value);
+ int32_t RtcReadReg(DevHandle handle, uint8_t usrDefIndex, uint8_t *value);
```
- **表12** RtcReadReg参数和返回值描述
+ **表 12** RtcReadReg参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | usrDefIndex | 用户定义的寄存器对应索引 |
- | value | 寄存器值 |
- | **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | usrDefIndex | uint8_t类型,用户定义的寄存器对应索引 |
+ | value | uint8_t类型指针,待读取寄存器值 |
+ | **返回值** | **描述** |
+ | HDF_SUCCESS | 读取RTC自定义寄存器配置成功 |
+ | 负数 | 读取RTC自定义寄存器配置失败 |
```c
int32_t ret;
- uint8_t usrDefIndex = 0; /* 定义0索引对应用户定义的第一个寄存器*/
+ uint8_t usrDefIndex = 0; // 定义0索引对应用户定义的第一个寄存器
uint8_t value = 0;
- /* 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值 */
+ // 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值
ret = RtcReadReg(handle, usrDefIndex, &value);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("read reg fail, ret:%d", ret);
+ return ret;
}
```
@@ -415,26 +460,28 @@ if (ret != 0) {
int32_t RtcWriteReg(DevHandle handle, uint8_t usrDefIndex, uint8_t value);
```
- **表13** RtcWriteReg参数和返回值描述
+ **表 13** RtcWriteReg参数和返回值描述
| **参数** | **描述** |
| -------- | -------- |
- | handle | RTC设备句柄 |
- | usrDefIndex | 用户定义的寄存器对应索引 |
- | value | 寄存器值 |
- | **返回值** | **描述** |
- | 0 | 操作成功 |
- | 负数 | 操作失败 |
+ | handle | DevHandle类型,RTC设备句柄 |
+ | usrDefIndex | uint8_t类型,用户定义的寄存器对应索引 |
+ | value | uint8_t类型,寄存器值 |
+ | **返回值** | **描述** |
+ | HDF_SUCCESS | 设置RTC自定义寄存器配置成功 |
+ | 负数 | 设置RTC自定义寄存器配置失败 |
```c
int32_t ret;
- uint8_t usrDefIndex = 0; /* 定义0索引对应用户定义第一个寄存器*/
+ uint8_t usrDefIndex = 0; // 定义0索引对应用户定义第一个寄存器
uint8_t value = 0x10;
- /* 按照用户的定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值 */
+ // 按照用户的定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值
ret = RtcWriteReg(handle, usrDefIndex, value);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ // 错误处理
+ HDF_LOGE("write reg fail, ret:%d", ret);
+ return ret;
}
```
@@ -446,14 +493,14 @@ if (ret != 0) {
void RtcClose(DevHandle handle);
```
- **表14** RtcClose参数描述
+**表 14** RtcClose参数描述
| **参数** | **描述** |
| -------- | -------- |
-| handle | RTC设备句柄 |
+| handle | DevHandle类型,RTC设备句柄 |
```c
-/* 销毁RTC句柄 */
+// 销毁RTC句柄
RtcClose(handle);
```
@@ -471,53 +518,73 @@ RtcClose(handle);
示例如下:
-```c
+```c有问题
+#include "hdf_log.h" // 标准日志打印头文件
+#include "osal_time.h" // 标准延迟&睡眠接口头文件
#include "rtc_if.h"
int32_t RtcAlarmACallback(enum RtcAlarmIndex alarmIndex)
{
if (alarmIndex == RTC_ALARM_INDEX_A) {
- /* 报警A的处理 */
- printf("RTC Alarm A callback function\n\r");
+ // 报警A的处理
+ HDF_LOGD("RtcAlarmACallback: RTC Alarm A callback function\n\r");
+ return HDF_SUCCESS;
} else if (alarmIndex == RTC_ALARM_INDEX_B) {
- /* 报警B的处理 */
- printf("RTC Alarm B callback function\n\r");
+ // 报警B的处理
+ HDF_LOGD("RtcAlarmACallback:RTC Alarm B callback function\n\r");
+ return HDF_SUCCESS;
} else {
- /* 错误处理 */
+ // 错误处理
+ HDF_LOGE("RtcAlarmACallback:RTC Alarm callback function fail!\n\r");
+ return HDF_FAILURE;
}
- return 0;
+ return HDF_SUCCESS;
}
-void RtcTestSample(void)
+int32_t RtcTestSample(void)
{
int32_t ret;
struct RtcTime tm;
struct RtcTime alarmTime;
uint32_t freq;
+ uint8_t usrDefIndex = 0;
+ uint8_t value = 0;
DevHandle handle = NULL;
- /* 获取RTC设备句柄 */
+ // 获取RTC设备句柄
handle = RtcOpen();
if (handle == NULL) {
- /* 错误处理 */
+ HDF_LOGE("RtcTestSample:open rtc fail!");
+ return HDF_FAILURE;
}
- /* 注册报警A的定时回调函数 */
+ // 注册报警A的定时回调函数
ret = RtcRegisterAlarmCallback(handle, RTC_ALARM_INDEX_A, RtcAlarmACallback);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample:register alarm callback fail, ret:%d", ret);
+ goto ERR;
}
- /* 设置RTC外接晶体振荡频率,注意按照器件手册要求配置RTC外频 */
- freq = 32768; /* 32768 Hz */
+ // 设置RTC外接晶体振荡频率,注意按照器件手册要求配置RTC外频
+ freq = 32768; // 32768 Hz
ret = RtcSetFreq(handle, freq);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample:set freq fail, ret:%d", ret);
+ goto ERR;
}
- /* 设置RTC报警中断使能 */
+
+ freq = 0;
+ ret = RtcGetFreq(handle, &freq);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample:get freq fail, ret:%d", ret);
+ goto ERR;
+ }
+
+ // 设置RTC报警中断使能
ret = RtcAlarmInterruptEnable(handle, RTC_ALARM_INDEX_A, 1);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample:alarm interrupt enable fail, ret:%d", ret);
+ goto ERR;
}
- /* 设置RTC时间为2020/01/01 00:00:10 .990 */
+ // 设置RTC时间为2020/01/01 00:00:10 .990
tm.year = 2020;
tm.month = 01;
tm.day = 01;
@@ -525,12 +592,13 @@ void RtcTestSample(void)
tm.minute = 0;
tm.second = 10;
tm.millisecond = 990;
- /* 写RTC时间信息 */
+ // 写RTC时间信息
ret = RtcWriteTime(handle, &tm);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample:write time fail, ret:%d", ret);
+ goto ERR;
}
- /* 设置RTC报警时间为2020/01/01 00:00:30 .100 */
+ // 设置RTC报警时间为2020/01/01 00:00:30 .100
alarmTime.year = 2020;
alarmTime.month = 01;
alarmTime.day = 01;
@@ -538,22 +606,46 @@ void RtcTestSample(void)
alarmTime.minute = 0;
alarmTime.second = 30;
alarmTime.millisecond = 100;
- /* 设置RTC_ALARM_INDEX_A索引定时报警时间信息, 定时时间到后会打印"RTC Alarm A callback function" */
+ // 设置RTC_ALARM_INDEX_A索引定时报警时间信息, 定时时间到后会打印"RTC Alarm A callback function"
ret = RtcWriteAlarm(handle, RTC_ALARM_INDEX_A, &alarmTime);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample:write alarm fail, ret:%d", ret);
+ goto ERR;
}
+ OsalSleep(5);
- /* 读取RTC实时时间 */
+ ret = RtcReadAlarm(handle, RTC_ALARM_INDEX_A, &alarmTime);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample: read alarm fail, ret: %d!", ret);
+ goto ERR;
+ }
+ // 读取RTC实时时间
ret = RtcReadTime(handle, &tm);
- if (ret != 0) {
- /* 错误处理 */
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample:read time fail, ret:%d", ret);
+ goto ERR;
+ }
+
+ ret = RtcWriteReg(handle, usrDefIndex, value);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample: write reg fail, ret: %d!", ret);
+ return ret;
+ }
+
+ ret = RtcReadReg(handle, usrDefIndex, &value);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("RtcTestSample: read reg fail, ret :%d!", ret);
+ return ret;
}
- sleep(5)
- printf("RTC read time:\n\r");
- printf("year-month-date-weekday hour:minute:second .millisecond %04u-%02u-%02u-%u %02u:%02u:%02u .%03u",
+
+ HDF_LOGD("RtcTestSample: RTC read time:\n\r");
+ HDF_LOGD("RtcTestSample: year-month-date-weekday hour:minute:second .millisecond %04u-%02u-%02u-%u %02u:%02u:%02u .%03u",
tm.year, tm.month, tm.day, tm.weekday, tm.hour, tm.minute, tm.second, tm.millisecond);
- /* 销毁RTC设备句柄 */
+ HDF_LOGD("RtcTestSample: all test end.");
+
+ERR:
+ // 销毁RTC设备句柄
RtcClose(handle);
+ return ret;
}
-```
\ No newline at end of file
+```
diff --git a/zh-cn/device-dev/driver/driver-platform-rtc-develop.md b/zh-cn/device-dev/driver/driver-platform-rtc-develop.md
index 16eeb076e3ead1bf7096f48b9073db5e0c0d6148..ee4851727e4b8e11f14eacfb972fbbe9697fc8c3 100755
--- a/zh-cn/device-dev/driver/driver-platform-rtc-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-rtc-develop.md
@@ -13,11 +13,20 @@ RTC(real-time clock)为操作系统中的实时时钟设备,为操作系
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
-**图1** RTC独立服务模式结构图
+**图 1** RTC独立服务模式结构图
+
+![RTC独立服务模式结构图](figures/独立服务模式结构图.png)
+
+RTC模块各分层作用:
+
+- 接口层提供打开RTC设备、RTC设备读取时间、RTC设备设置时间、RTC设备读取警报时间、RTC设备设置警报时间、RTC设备定时报警回调函数、RTC设备设置定时报警中断使能去使能、RTC设备设置RTC外频、RTC设备读取RTC外频、复位RTC、设置RTC自定义寄存器配置,读取RTC自定义寄存器配置以及关闭RTC设备的接口。
-![image](figures/独立服务模式结构图.png "RTC独立服务模式结构图")
+- 核心层主要提供RTC控制器的创建、销毁,通过钩子函数与适配层交互。
+
+- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
## 开发指导
@@ -47,7 +56,7 @@ struct RtcMethod {
};
```
- **表1** RtcMethod结构体成员的钩子函数功能说明
+**表 1** RtcMethod结构体成员的钩子函数功能说明
| 函数 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
@@ -57,30 +66,37 @@ struct RtcMethod {
| WriteAlarm | host:结构体指针,核心层RTC控制器
alarmIndex:枚举值,闹钟报警索引
time:结构体指针,时间传入值 | 无 | HDF_STATUS相关状态 | 写RTC报警时间信息 |
| RegisterAlarmCallback | host:结构体指针,核心层RTC控制器
alarmIndex:枚举值,闹钟报警索引
cb:函数指针,回调函数 | 无 | HDF_STATUS相关状态 | 注册报警超时回调函数 |
| AlarmInterruptEnable | host:结构体指针,核心层RTC控制器
alarmIndex:枚举值,闹钟报警索引
enable:布尔值,控制报警 | 无 | HDF_STATUS相关状态 | 使能/去使能RTC报警中断 |
-| GetFreq | host:结构体指针,核心层RTC控制器 | freq:uint32_t指针,传出的频率值 | HDF_STATUS相关状态 | 读RTC外接晶振频率 |
-| SetFreq | host:结构体指针,核心层RTC控制器
freq:uint32_t,频率传入值 | 无 | HDF_STATUS相关状态 | 配置RTC外接晶振频率 |
+| GetFreq | host:结构体指针,核心层RTC控制器 | freq:uint32_t类型指针,传出的频率值 | HDF_STATUS相关状态 | 读RTC外接晶振频率 |
+| SetFreq | host:结构体指针,核心层RTC控制器
freq:uint32_t类型,频率传入值 | 无 | HDF_STATUS相关状态 | 配置RTC外接晶振频率 |
| Reset | host:结构体指针,核心层RTC控制器 | 无 | HDF_STATUS相关状态 | RTC复位 |
-| ReadReg | host:结构体指针,核心层RTC控制器
usrDefIndex:结构体,用户自定义寄存器索引 | value:uint8_t指针,传出的寄存器值 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值 |
-| WriteReg | host:结构体指针,核心层RTC控制器
usrDefIndex:结构体,用户自定义寄存器索引
value:uint8_t,寄存器传入值 | 无 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值 |
+| ReadReg | host:结构体指针,核心层RTC控制器
usrDefIndex:结构体,用户自定义寄存器索引 | value:uint8_t类型指针,传出的寄存器值 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值 |
+| WriteReg | host:结构体指针,核心层RTC控制器
usrDefIndex:结构体,用户自定义寄存器索引
value:uint8_t类型,寄存器传入值 | 无 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值 |
### 开发步骤
-RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置属性文件,以及填充核心层接口函数。
+RTC模块适配HDF框架包含以下四个步骤:
1. 实例化驱动入口
- - 实例化HdfDriverEntry结构体成员。
- - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
+
+ - 实例化HdfDriverEntry结构体成员。
+
+ - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2. 配置属性文件
- - 在device_info.hcs文件中添加deviceNode描述。
- - 【可选】添加rtc_config.hcs器件属性文件。
+
+ - 在device_info.hcs文件中添加deviceNode描述。
+
+ - 【可选】添加rtc_config.hcs器件属性文件。
3. 实例化RTC控制器对象
- - 初始化RtcHost成员。
- - 实例化RtcHost成员RtcMethod。
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化RtcHost成员RtcMethod,其定义和成员说明见[接口说明](#接口说明)。
+
+ - 初始化RtcHost成员。
+
+ - 实例化RtcHost成员RtcMethod。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化RtcHost成员RtcMethod,其定义和成员说明见[接口说明](#接口说明)。
4. 驱动调试
@@ -91,249 +107,265 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
下方将以Hi3516DV300的驱动//device/soc/hisilicon/common/platform/rtc/rtc_hi35xx.c为示例,展示驱动适配者需要提供哪些内容来完整实现设备功能。
-1. 驱动开发首先需要实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。
+1. 实例化驱动入口
- HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
- RTC驱动入口参考:
+ RTC驱动入口参考:
- ```c
- struct HdfDriverEntry g_rtcDriverEntry = {
- .moduleVersion = 1,
- .Bind = HiRtcBind, // 见Bind开发参考
- .Init = HiRtcInit, // 见Init开发参考
- .Release = HiRtcRelease, // 见Release开发参考
- .moduleName = "HDF_PLATFORM_RTC",// 【必要】且与HCS里面的名字匹配
- };
- /* 调用HDF_INIT将驱动入口注册到HDF框架中 */
- HDF_INIT(g_rtcDriverEntry);
- ```
+ ```c
+ struct HdfDriverEntry g_rtcDriverEntry = {
+ .moduleVersion = 1,
+ .Bind = HiRtcBind, // 挂接RTC模块Bind实例化
+ .Init = HiRtcInit, // 挂接RTC模块Init实例化
+ .Release = HiRtcRelease, // 挂接RTC模块Release实例化
+ .moduleName = "HDF_PLATFORM_RTC", // 【必要且与HCS文件中里面的moduleName匹配】
+ };
+ HDF_INIT(g_rtcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
+
+2. 配置属性文件
+
+ 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode描述。deviceNode信息与驱动入口注册相关。本例只有一个RTC控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在rtc_config.hcs文件中增加对应的器件属性。器件属性值与核心层RtcHost成员的默认值或限制范围有密切关系,比如RTC中断号,需要在rtc_config.hcs文件中增加对应的器件属性。
-2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在rtc_config.hcs中配置器件属性。
+ 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
- deviceNode信息与驱动入口注册相关,器件属性值与核心层RtcHost成员的默认值或限制范围有密切关系。
+ **表 2** device_info.hcs节点参数说明
- 本例只有一个RTC控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在rtc_config文件中增加对应的器件属性。
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,RTC控制器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。RTC控制器具体配置为30 |
+ | permission | 驱动创建设备节点权限,RTC控制器具体配置为0664 |
+ | moduleName | 驱动名称,RTC控制器固定为HDF_PLATFORM_RTC |
+ | serviceName | 驱动对外发布服务的名称,RTC控制器服务名设置为HDF_PLATFORM_RTC |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,RTC控制器设置为hisilicon_hi35xx_rtc |
- - device_info.hcs配置参考
+ - device_info.hcs配置参考
-
- ```c
- root {
- device_info {
- platform :: host {
- device_rtc :: device {
- device0 :: deviceNode {
- policy = 1; // 2:用户态可见;1:内核态可见;0:不需要发布服务。
- priority = 30; // 值越小,优先级越高。
- permission = 0644; // 驱动创建设备节点权限
- moduleName = "HDF_PLATFORM_RTC"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。
- serviceName = "HDF_PLATFORM_RTC"; // 【必要】驱动对外发布服务的名称,必须唯一。
- deviceMatchAttr = "hisilicon_hi35xx_rtc"; // 【必要】需要与设备hcs文件中的match_attr匹配。
- }
- }
- }
- }
- }
- ```
+ 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
+
+ ```c
+ root {
+ device_info {
+ platform :: host {
+ device_rtc :: device {
+ device0 :: deviceNode { // 驱动的DeviceNode节点
+ policy = 1; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2
+ priority = 30; // 驱动启动优先级
+ permission = 0644; // 驱动创建设备节点权限
+ moduleName = "HDF_PLATFORM_RTC"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。
+ serviceName = "HDF_PLATFORM_RTC"; // 【必要】驱动对外发布服务的名称,必须唯一。
+ deviceMatchAttr = "hisilicon_hi35xx_rtc"; // 【必要】用于配置控制器私有数据,必须和驱动私有数据配置表rtc_config.hcs中的match_attr值保持一致。
+ }
+ }
+ }
+ }
+ }
+ ```
- - rtc_config.hcs配置参考
+ - rtc_config.hcs配置参考
-
- ```c
- root {
- platform {
- rtc_config {
- controller_0x12080000 {
- match_attr = "hisilicon_hi35xx_rtc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
- rtcSpiBaseAddr = 0x12080000; // 地址映射相关
- regAddrLength = 0x100; // 地址映射相关
- irq = 37; // 中断号
- supportAnaCtrl = false;
- supportLock = false;
- anaCtrlAddr = 0xff;
- lock0Addr = 0xff;
- lock1Addr = 0xff;
- lock2Addr = 0xff;
- lock3Addr = 0xff;
- }
- }
- }
- }
- ```
-
- 需要注意的是,新增rtc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
-
- 例如:本例中rtc_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs" // 配置文件相对路径
- ```
-
-3. 完成属性文件配置之后,下一步就是以核心层RtcHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化RtcHost成员RtcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
-
- - 自定义结构体参考。
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,而且rtc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员。
-
- ```c
- struct RtcConfigInfo {
- uint32_t spiBaseAddr; // 地址映射相关
- volatile void *remapBaseAddr; // 地址映射相关
- uint16_t regAddrLength; // 地址映射相关
- uint8_t supportAnaCtrl; // 是否支持anactrl
- uint8_t supportLock; // 是否支持锁
- uint8_t irq; // 中断号
- uint8_t alarmIndex; // 闹钟索引
- uint8_t anaCtrlAddr; // anactrl地址
- struct RtcLockAddr lockAddr; // 锁地址
- RtcAlarmCallback cb; // 回调函数
- struct OsalMutex mutex; // 互斥锁
- };
-
- /* RtcHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
- struct RtcHost {
- struct IDeviceIoService service;
- struct HdfDeviceObject *device;
- struct RtcMethod *method;
- void *data;
- };
- ```
-
- - RtcHost成员钩子函数结构体RtcMethod的实例化,其他成员在Init函数中初始化。
-
- ```c
- /* rtc_hi35xx.c中的示例:钩子函数的填充 */
- static struct RtcMethod g_method = {
- .ReadTime = HiRtcReadTime,
- .WriteTime = HiRtcWriteTime,
- .ReadAlarm = HiReadAlarm,
- .WriteAlarm = HiWriteAlarm,
- .RegisterAlarmCallback = HiRegisterAlarmCallback,
- .AlarmInterruptEnable = HiAlarmInterruptEnable,
- .GetFreq = HiGetFreq,
- .SetFreq = HiSetFreq,
- .Reset = HiReset,
- .ReadReg = HiReadReg,
- .WriteReg = HiWriteReg,
- };
- ```
-
- - Bind函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- **表2** HDF_STATUS返回值描述
-
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- 关联HdfDeviceObject对象和RtcHost。
-
- ```c
- static int32_t HiRtcBind(struct HdfDeviceObject *device)
- {
- struct RtcHost *host = NULL;
- host = RtcHostCreate(device); // 实际是申请内存并挂接device: host->device = device
- // 使HdfDeviceObject与RtcHost可以相互转化的前提
- ...
- device->service = &host->service; // 使HdfDeviceObject与RtcHost可以相互转化的前提
- // 方便后续通过调用RtcHostFromDevice实现全局性质的host
- return HDF_SUCCESS;
- }
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- HDF_STATUS相关状态。
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化RtcHost成员。
-
- ```c
- static int32_t HiRtcInit(struct HdfDeviceObject *device)
- {
- struct RtcHost *host = NULL;
- struct RtcConfigInfo *rtcInfo = NULL;
- ...
- host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换
- rtcInfo = OsalMemCalloc(sizeof(*rtcInfo));
- ...
- /* HiRtcConfigData会从设备配置树中读取属性填充rtcInfo的supportAnaCtrl、supportLock、spiBaseAddr、regAddrLength、irq,
- * 为HiRtcSwInit和HiRtcSwInit提供参数,当函数HiRtcSwInit和HiRtcSwInit内部执行失败后进行内存释放等操作。
- */
- if (HiRtcConfigData(rtcInfo, device->property) != 0) {
- ...
- }
- if (HiRtcSwInit(rtcInfo) != 0) { // 地址映射以及中断注册相关
- ...
- }
- if (HiRtcHwInit(rtcInfo) != 0) { // 初始化anaCtrl和lockAddr相关内容
- ...
- }
+ 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs文件配置器件属性,其中配置参数如下:
+
+ ```c
+ root {
+ platform {
+ rtc_config {
+ controller_0x12080000 {
+ match_attr = "hisilicon_hi35xx_rtc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
+ rtcSpiBaseAddr = 0x12080000; // 地址映射相关
+ regAddrLength = 0x100; // 地址映射相关
+ irq = 37; // 中断号
+ supportAnaCtrl = false;
+ supportLock = false;
+ anaCtrlAddr = 0xff;
+ lock0Addr = 0xff;
+ lock1Addr = 0xff;
+ lock2Addr = 0xff;
+ lock3Addr = 0xff;
+ }
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增rtc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
+
+ 例如:本例中rtc_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化RTC控制器对象
+
+ 完成属性文件配置之后,下一步就是以核心层RtcHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化RtcHost成员RtcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+
+ - 自定义结构体参考。
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,而且rtc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员。
+
+ ```c
+ struct RtcConfigInfo {
+ uint32_t spiBaseAddr; // 地址映射相关
+ volatile void *remapBaseAddr; // 地址映射相关
+ uint16_t regAddrLength; // 地址映射相关
+ uint8_t supportAnaCtrl; // 是否支持anactrl
+ uint8_t supportLock; // 是否支持锁
+ uint8_t irq; // 中断号
+ uint8_t alarmIndex; // 闹钟索引
+ uint8_t anaCtrlAddr; // anactrl地址
+ struct RtcLockAddr lockAddr; // 锁地址
+ RtcAlarmCallback cb; // 回调函数
+ struct OsalMutex mutex; // 互斥锁
+ };
- host->method = &g_method; // RtcMethod的实例化对象的挂载
- host->data = rtcInfo; // 使RtcConfigInfo与RtcHost可以相互转化的前提
- HDF_LOGI("Hdf dev service:%s init success!", HdfDeviceGetServiceName(device));
- return HDF_SUCCESS;
- }
- ```
- - Release函数开发参考
+ // RtcHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct RtcHost {
+ struct IDeviceIoService service;
+ struct HdfDeviceObject *device;
+ struct RtcMethod *method;
+ void *data;
+ };
+ ```
+
+ - RtcHost成员钩子函数结构体RtcMethod的实例化。
+
+ ```c
+ // rtc_hi35xx.c中的示例:钩子函数的填充
+ static struct RtcMethod g_method = {
+ .ReadTime = HiRtcReadTime,
+ .WriteTime = HiRtcWriteTime,
+ .ReadAlarm = HiReadAlarm,
+ .WriteAlarm = HiWriteAlarm,
+ .RegisterAlarmCallback = HiRegisterAlarmCallback,
+ .AlarmInterruptEnable = HiAlarmInterruptEnable,
+ .GetFreq = HiGetFreq,
+ .SetFreq = HiSetFreq,
+ .Reset = HiReset,
+ .ReadReg = HiReadReg,
+ .WriteReg = HiWriteReg,
+ };
+ ```
+
+ - Bind函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ 关联HdfDeviceObject对象和RtcHost。
+
+ ```c
+ static int32_t HiRtcBind(struct HdfDeviceObject *device)
+ {
+ struct RtcHost *host = NULL;
+ host = RtcHostCreate(device); // 实际是申请内存并挂接device: host->device = device
+ // 使HdfDeviceObject与RtcHost可以相互转化的前提
+ ......
+ device->service = &host->service; // 使HdfDeviceObject与RtcHost可以相互转化的前提
+ // 方便后续通过调用RtcHostFromDevice实现全局性质的host
+ return HDF_SUCCESS;
+ }
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态。
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化RtcHost成员。
+
+ ```c
+ static int32_t HiRtcInit(struct HdfDeviceObject *device)
+ {
+ struct RtcHost *host = NULL;
+ struct RtcConfigInfo *rtcInfo = NULL;
+ ......
+ host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换
+ rtcInfo = OsalMemCalloc(sizeof(*rtcInfo));
+ ......
+ /* HiRtcConfigData会从设备配置树中读取属性填充rtcInfo的supportAnaCtrl、supportLock、spiBaseAddr、regAddrLength、irq,
+ * 为HiRtcSwInit和HiRtcSwInit提供参数,当函数HiRtcSwInit和HiRtcSwInit内部执行失败后进行内存释放等操作。
+ */
+ if (HiRtcConfigData(rtcInfo, device->property) != 0) {
+ ......
+ }
+ if (HiRtcSwInit(rtcInfo) != 0) { // 地址映射以及中断注册相关
+ ......
+ }
+ if (HiRtcHwInit(rtcInfo) != 0) { // 初始化anaCtrl和lockAddr相关内容
+ ......
+ }
+
+ host->method = &g_method; // RtcMethod的实例化对象的挂载
+ host->data = rtcInfo; // 使RtcConfigInfo与RtcHost可以相互转化的前提
+ HDF_LOGI("Hdf dev service:%s init success!", HdfDeviceGetServiceName(device));
+ return HDF_SUCCESS;
+ }
+ ```
+ - Release函数开发参考
- 入参:
+ 入参:
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
- 返回值:
+ 返回值:
- 无。
+ 无。
- 函数说明:
+ 函数说明:
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init或Bind函数中具备对应赋值的操作。
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init或Bind函数中具备对应赋值的操作。
- ```c
- static void HiRtcRelease(struct HdfDeviceObject *device)
- {
- struct RtcHost *host = NULL;
- struct RtcConfigInfo *rtcInfo = NULL;
- ...
- host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换
- rtcInfo = (struct RtcConfigInfo *)host->data; // 这里是RtcHost到RtcConfigInfo的强制转换
- if (rtcInfo != NULL) {
- HiRtcSwExit(rtcInfo);
- OsalMemFree(rtcInfo); // 释放RtcConfigInfo
- host->data = NULL;
- }
- RtcHostDestroy(host); // 释放RtcHost
- }
- ```
+ ```c
+ static void HiRtcRelease(struct HdfDeviceObject *device)
+ {
+ struct RtcHost *host = NULL;
+ struct RtcConfigInfo *rtcInfo = NULL;
+ ...
+ host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换
+ rtcInfo = (struct RtcConfigInfo *)host->data; // 这里是RtcHost到RtcConfigInfo的强制转换
+ if (rtcInfo != NULL) {
+ HiRtcSwExit(rtcInfo);
+ OsalMemFree(rtcInfo); // 释放RtcConfigInfo
+ host->data = NULL;
+ }
+ RtcHostDestroy(host); // 释放RtcHost
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,读取RTC时间、设置RTC时间等。
diff --git a/zh-cn/device-dev/driver/driver-platform-sdio-des.md b/zh-cn/device-dev/driver/driver-platform-sdio-des.md
index c43d88eecd9d918c5298ad7ef9317e42d81a8ce2..21747ff03bfaa77fcac49de37bdb254bd8b613a9 100644
--- a/zh-cn/device-dev/driver/driver-platform-sdio-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-sdio-des.md
@@ -7,26 +7,54 @@
SDIO是安全数字输入输出接口(Secure Digital Input and Output)的缩写,是从SD内存卡接口的基础上演化出来的一种外设接口。SDIO接口兼容以前的SD卡,并且可以连接支持SDIO接口的其他设备。
SDIO接口定义了操作SDIO的通用方法集合,包括:
+
- 打开/关闭SDIO控制器
+
- 独占/释放HOST
+
- 使能/去使能设备
+
- 申请/释放中断
+
- 读写、获取/设置公共信息
### 运作机制
在HDF框架中,SDIO的接口适配模式采用独立服务模式。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。
-SDIO总线有两端,其中一端是主机端(HOST),另一端是设备端(DEVICE)。所有的通信都是由HOST端发出命令开始的,在DEVICE端只要能解析HOST的命令,就可以同HOST进行通信了。SDIO的HOST可以连接多个DEVICE,如下图所示:
+独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
+
+- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
+- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
+
+SDIO模块各分层作用:
+
+- 接口层提供打开SDIO设备、设置块的大小、读取数据、写数据、设置公共信息、获取公共信息、刷新数据、独占HOST、释放Host、使能SDIO功能设备、去使能SDIO功能设备、申请中断、释放中断关闭SDIO设备的接口。
+
+- 核心层主要提供SDIO控制器的添加、移除及管理的能力,通过钩子函数与适配层交互。
+
+- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
+
+**图 1** SDIO独立服务模式结构图
+
+![SDIO独立服务模式结构图](figures/独立服务模式结构图.png)
+
+SDIO总线有两端,其中一端是主机端(HOST),另一端是设备端(DEVICE)。所有的通信都是由HOST端发出命令开始的,在DEVICE端只要能解析HOST的命令,就可以同HOST进行通信了。SDIO的HOST可以连接多个DEVICE,如图2所示:
+
- CLK信号:HOST给DEVICE的时钟信号。
+
- VDD信号:电源信号。
+
- VSS信号:Ground信号。
+
- D0-3信号:4条数据线,其中,DAT1信号线复用为中断线,在1BIT模式下DAT0用来传输数据,在4BIT模式下DAT0-DAT3用来传输数据。
+
- CMD信号:用于HOST发送命令和DEVICE回复响应。
-**图1** SDIO的HOST-DEVICE连接示意图
+**图 2** SDIO的HOST-DEVICE连接示意图
-![image](figures/SDIO的HOST-DEVICE连接示意图.png "SDIO的HOST-DEVICE连接示意图")
+![SDIO的HOST-DEVICE连接示意图](figures/SDIO的HOST-DEVICE连接示意图.png)
### 约束与限制
@@ -42,9 +70,9 @@ SDIO的应用比较广泛,目前,有许多手机都支持SDIO功能,并且
SDIO模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/sdio_if.h。
-**表1** SDIO驱动API接口功能介绍
+**表 1** SDIO驱动API接口功能介绍
-| 接口名 | 接口描述 |
+| 接口名 | 接口描述 |
| -------- | -------- |
| DevHandle SdioOpen(int16_t mmcBusNum, struct SdioFunctionConfig \*config) | 打开指定总线号的SDIO控制器 |
| void SdioClose(DevHandle handle) | 关闭SDIO控制器 |
@@ -67,11 +95,11 @@ SDIO模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core
### 使用流程
-使用SDIO的一般流程如下图所示。
+使用SDIO的一般流程如图3所示。
- **图2** SDIO使用流程图
+**图 3** SDIO使用流程图
- ![image](figures/SDIO使用流程图.png "SDIO使用流程图")
+![SDIO使用流程图](figures/SDIO使用流程图.png)
#### 打开SDIO控制器
@@ -81,17 +109,17 @@ SDIO模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core
DevHandle SdioOpen(int16_t mmcBusNum, struct SdioFunctionConfig *config);
```
- **表2** SdioOpen函数的参数和返回值描述
+**表 2** SdioOpen函数的参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| mmcBusNum | 总线号 |
-| config | SDIO功能配置信息 |
+| mmcBusNum | int16_t类型,总线号 |
+| config | 结构体指针,SDIO功能配置信息 |
| **返回值** | **返回值描述** |
| NULL | 获取SDIO控制器的设备句柄失败 |
| 设备句柄 | SDIO控制器的设备句柄 |
- 打开SDIO控制器的示例如下:
+打开SDIO控制器的示例如下:
```c
DevHandle handle = NULL;
@@ -99,10 +127,11 @@ struct SdioFunctionConfig config;
config.funcNr = 1;
config.vendorId = 0x123;
config.deviceId = 0x456;
-/* 打开总线号为1的SDIO控制器 */
+// 打开总线号为1的SDIO控制器
handle = SdioOpen(1, &config);
if (handle == NULL) {
- HDF_LOGE("SdioOpen: failed!\n");
+ HDF_LOGE("SdioOpen: open sdio fail!\n");
+ return NULL;
}
```
@@ -114,16 +143,16 @@ if (handle == NULL) {
void SdioClaimHost(DevHandle handle);
```
- **表3** SdioClaimHost函数的参数描述
+**表 3** SdioClaimHost函数的参数描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | SDIO控制器的设备句柄 |
+| handle | DevHandle类型,SDIO控制器的设备句柄 |
独占HOST示例如下:
```c
-SdioClaimHost(handle); /* 独占HOST */
+SdioClaimHost(handle); // 独占HOST
```
#### 使能SDIO设备
@@ -134,62 +163,64 @@ SdioClaimHost(handle); /* 独占HOST */
int32_t SdioEnableFunc(DevHandle handle);
```
- **表4** SdioEnableFunc函数的参数和返回值描述
+**表 4** SdioEnableFunc函数的参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | SDIO控制器的设备句柄 |
+| handle | DevHandle类型,SDIO控制器的设备句柄 |
| **返回值** | **返回值描述** |
-| 0 | SDIO使能成功 |
+| HDF_SUCCESS | SDIO使能成功 |
| 负数 | SDIO使能失败 |
使能SDIO设备的示例如下:
```c
int32_t ret;
-/* 使能SDIO设备 */
+// 使能SDIO设备
ret = SdioEnableFunc(handle);
-if (ret != 0) {
- HDF_LOGE("SdioEnableFunc: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioEnableFunc: sdio enable func fail, ret:%d\n", ret);
+ return ret;
}
```
#### 注册SDIO中断
-在通信之前,还需要注册SDIO中断,注册SDIO中断函数如下图所示:
+在通信之前,还需要注册SDIO中断,注册SDIO中断函数如下所示:
```c
int32_t SdioClaimIrq(DevHandle handle, SdioIrqHandler *handler);
```
- **表5** SdioClaimIrq函数的参数和返回值描述
+**表 5** SdioClaimIrq函数的参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | SDIO控制器的设备句柄 |
-| handler | 中断服务函数指针 |
+| handle | DevHandle类型,SDIO控制器的设备句柄 |
+| handler | 函数指针,中断服务函数 |
| **返回值** | **返回值描述** |
-| 0 | 注册中断成功 |
-| 负数 | 注册中断失败 |
+| HDF_SUCCESS | 注册SDIO中断成功 |
+| 负数 | 注册SDIO中断失败 |
注册SDIO中的示例如下:
```c
-/* 中断服务函数需要根据各自平台的情况去实现 */
+// 中断服务函数需要根据各自平台的情况去实现
static void SdioIrqFunc(void *data)
{
if (data == NULL) {
HDF_LOGE("SdioIrqFunc: data is NULL.\n");
return;
}
- /* 需要开发者自行添加具体实现 */
+ // 需要开发者自行添加具体实现
}
int32_t ret;
-/* 注册SDIO中断 */
+// 注册SDIO中断
ret = SdioClaimIrq(handle, SdioIrqFunc);
-if (ret != 0) {
- HDF_LOGE("SdioClaimIrq: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioClaimIrq: sdio claim irq fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -197,201 +228,207 @@ if (ret != 0) {
- 向SDIO设备增量写入指定长度的数据
- 对应的接口函数如下所示:
-
- ```c
- int32_t SdioWriteBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size);
- ```
-
- **表6** SdioWriteBytes函数的参数和返回值描述
-
- | 参数 | 参数描述 |
- | -------- | -------- |
- | handle | SDIO控制器的设备句柄 |
- | data | 待写入数据的指针 |
- | addr | 待写入数据的起始地址 |
- | size | 待写入数据的长度 |
- | **返回值** | **返回值描述** |
- | 0 | SDIO写数据成功 |
- | 负数 | SDIO写数据失败 |
-
- 向SDIO设备增量写入指定长度的数据的示例如下:
-
- ```c
- int32_t ret;
- uint8_t wbuff[] = {1,2,3,4,5};
- uint32_t addr = 0x100 + 0x09;
- /* 向SDIO设备起始地址0x109,增量写入5个字节的数据 */
- ret = SdioWriteBytes(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0]));
- if (ret != 0) {
- HDF_LOGE("SdioWriteBytes: failed, ret %d\n", ret);
- }
- ```
+ 对应的接口函数如下所示:
+
+ ```c
+ int32_t SdioWriteBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size);
+ ```
+
+ **表 6** SdioWriteBytes函数的参数和返回值描述
+
+ | 参数 | 参数描述 |
+ | -------- | -------- |
+ | handle | DevHandle类型,SDIO控制器的设备句柄 |
+ | data | uint8_t类型指针,待写入数据 |
+ | addr | uint32_t类型,待写入数据的起始地址 |
+ | size | uint32_t类型,待写入数据的长度 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | SDIO写数据成功 |
+ | 负数 | SDIO写数据失败 |
+
+ 向SDIO设备增量写入指定长度的数据的示例如下:
+
+ ```c
+ int32_t ret;
+ uint8_t wbuff[] = {1,2,3,4,5};
+ uint32_t addr = 0x100 + 0x09;
+ // 向SDIO设备起始地址0x109,增量写入5个字节的数据
+ ret = SdioWriteBytes(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0]));
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioWriteBytes: sdio write bytes fail, ret:%d\n", ret);
+ return ret;
+ }
+ ```
- 从SDIO设备增量读取指定长度的数据
- 对应的接口函数如下所示:
-
- ```c
- int32_t SdioReadBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size);
- ```
-
- **表7** SdioReadBytes函数的参数和返回值描述
-
- | 参数 | 参数描述 |
- | -------- | -------- |
- | handle | SDIO控制器的设备句柄 |
- | data | 接收读取数据的指针 |
- | addr | 待读取数据的起始地址 |
- | size | 待读取数据的长度 |
- | **返回值** | **返回值描述** |
- | 0 | SDIO读数据成功 |
- | 负数 | SDIO读数据失败 |
-
- 从SDIO设备增量读取指定长度的数据的示例如下:
-
- ```c
- int32_t ret;
- uint8_t rbuff[5] = {0};
- uint32_t addr = 0x100 + 0x09;
- /* 从SDIO设备起始地址0x109,增量读取5个字节的数据 */
- ret = SdioReadBytes(handle, rbuff, addr, 5);
- if (ret != 0) {
- HDF_LOGE("SdioReadBytes: failed, ret %d\n", ret);
- }
- ```
+ 对应的接口函数如下所示:
+
+ ```c
+ int32_t SdioReadBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size);
+ ```
+
+ **表 7** SdioReadBytes函数的参数和返回值描述
+
+ | 参数 | 参数描述 |
+ | -------- | -------- |
+ | handle | DevHandle类型,SDIO控制器的设备句柄 |
+ | data | uint8_t类型指针,接收读取数据 |
+ | addr | uint32_t类型,待读取数据的起始地址 |
+ | size | uint32_t类型,待读取数据的长度 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | SDIO读数据成功 |
+ | 负数 | SDIO读数据失败 |
+
+ 从SDIO设备增量读取指定长度的数据的示例如下:
+
+ ```c
+ int32_t ret;
+ uint8_t rbuff[5] = {0};
+ uint32_t addr = 0x100 + 0x09;
+ // 从SDIO设备起始地址0x109,增量读取5个字节的数据
+ ret = SdioReadBytes(handle, rbuff, addr, 5);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioReadBytes: sdio read bytes fail, ret:%d\n", ret);
+ return ret;
+ }
+ ```
- 向SDIO设备的固定地址写入指定长度的数据
- 对应的接口函数如下所示:
+ 对应的接口函数如下所示:
- ```c
- int32_t SdioWriteBytesToFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen);
- ```
+ ```c
+ int32_t SdioWriteBytesToFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen);
+ ```
- **表8** SdioWriteBytesToFixedAddr函数的参数和返回值描述
+ **表 8** SdioWriteBytesToFixedAddr函数的参数和返回值描述
- | 参数 | 参数描述 |
- | -------- | -------- |
- | handle | SDIO控制器的设备句柄 |
- | data | 待写入数据的指针 |
- | addr | 待写入数据的固定地址 |
- | size | 待写入数据的长度 |
- | scatterLen | 集散表的长度。如果该字段不为0,则data为集散表类型。 |
- | **返回值** | **返回值描述** |
- | 0 | SDIO写数据成功 |
- | 负数 | SDIO写数据失败 |
-
- 向SDIO设备的固定地址写入指定长度的数据的示例如下:
-
- ```c
- int32_t ret;
- uint8_t wbuff[] = {1,2,3,4,5};
- uint32_t addr = 0x100 + 0x09;
- /* 向SDIO设备固定地址0x109写入5个字节的数据 */
- ret = SdioWriteBytesToFixedAddr(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0]), 0);
- if (ret != 0) {
- HDF_LOGE("SdioWriteBytesToFixedAddr: failed, ret %d\n", ret);
- }
- ```
+ | 参数 | 参数描述 |
+ | -------- | -------- |
+ | handle | DevHandle类型,SDIO控制器的设备句柄 |
+ | data | uint8_t类型指针,待写入数据 |
+ | addr | uint32_t类型,待写入数据的固定地址 |
+ | size | uint32_t类型,待写入数据的长度 |
+ | scatterLen | uint32_t类型,集散表的长度。如果该字段不为0,则data为集散表类型。 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | SDIO写数据成功 |
+ | 负数 | SDIO写数据失败 |
+
+ 向SDIO设备的固定地址写入指定长度的数据的示例如下:
+
+ ```c
+ int32_t ret;
+ uint8_t wbuff[] = {1, 2, 3, 4, 5};
+ uint32_t addr = 0x100 + 0x09;
+ // 向SDIO设备固定地址0x109写入5个字节的数据
+ ret = SdioWriteBytesToFixedAddr(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0]), 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioWriteBytesToFixedAddr: sdio write bytes to fixed addr fail, ret:%d\n", ret);
+ return ret;
+ }
+ ```
- 从SDIO设备的固定地址读取指定长度的数据
- 对应的接口函数如下所示:
+ 对应的接口函数如下所示:
- ```c
- int32_t SdioReadBytesFromFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen);
- ```
+ ```c
+ int32_t SdioReadBytesFromFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen);
+ ```
- **表9** SdioReadBytesFromFixedAddr函数的参数和返回值描述
+ **表 9** SdioReadBytesFromFixedAddr函数的参数和返回值描述
- | 参数 | 参数描述 |
- | -------- | -------- |
- | handle | SDIO控制器的设备句柄 |
- | data | 接收读取数据的指针 |
- | addr | 待读取数据的起始地址 |
- | size | 待读取数据的长度 |
- | scatterLen | 集散表的长度。如果该字段不为0,则data为集散表类型。 |
- | **返回值** | **返回值描述** |
- | 0 | SDIO读数据成功 |
- | 负数 | SDIO读数据失败 |
-
- 从SDIO设备的固定地址读取指定长度的数据的示例如下:
-
- ```c
- int32_t ret;
- uint8_t rbuff[5] = {0};
- uint32_t addr = 0x100 + 0x09;
- /* 从SDIO设备固定地址0x109中读取5个字节的数据 */
- ret = SdioReadBytesFromFixedAddr(handle, rbuff, addr, 5, 0);
- if (ret != 0) {
- HDF_LOGE("SdioReadBytesFromFixedAddr: failed, ret %d\n", ret);
- }
- ```
+ | 参数 | 参数描述 |
+ | -------- | -------- |
+ | handle | DevHandle类型,SDIO控制器的设备句柄 |
+ | data | uint8_t类型指针,接收读取数据 |
+ | addr | uint32_t类型,待读取数据的起始地址 |
+ | size | uint32_t类型,待读取数据的长度 |
+ | scatterLen | uint32_t类型,集散表的长度。如果该字段不为0,则data为集散表类型。 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | SDIO读数据成功 |
+ | 负数 | SDIO读数据失败 |
+
+ 从SDIO设备的固定地址读取指定长度的数据的示例如下:
+
+ ```c
+ int32_t ret;
+ uint8_t rbuff[5] = {0};
+ uint32_t addr = 0x100 + 0x09;
+ // 从SDIO设备固定地址0x109中读取5个字节的数据
+ ret = SdioReadBytesFromFixedAddr(handle, rbuff, addr, 5, 0);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioReadBytesFromFixedAddr: sdio read bytes from fixed addr fail, ret:%d\n", ret);
+ return ret;
+ }
+ ```
- 向SDIO function 0的指定地址空间写入指定长度的数据
- 当前只支持写入一个字节的数据,对应的接口函数如下所示:
-
- ```c
- int32_t SdioWriteBytesToFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size);
- ```
-
- **表10** SdioWriteBytesToFunc0函数的参数和返回值描述
-
- | 参数 | 参数描述 |
- | -------- | -------- |
- | handle | SDIO控制器的设备句柄 |
- | data | 待写入数据的指针 |
- | addr | 待写入数据的起始地址 |
- | size | 待写入数据的长度 |
- | **返回值** | **返回值描述** |
- | 0 | SDIO写数据成功 |
- | 负数 | SDIO写数据失败 |
-
- 向SDIO function 0的指定地址空间写入指定长度的数据的示例如下:
-
- ```c
- int32_t ret;
- uint8_t wbuff = 1;
- /* 向SDIO function 0地址0x2中写入1字节的数据 */
- ret = SdioWriteBytesToFunc0(handle, &wbuff, 0x2, 1);
- if (ret != 0) {
- HDF_LOGE("SdioWriteBytesToFunc0: failed, ret %d\n", ret);
- }
- ```
+ 当前只支持写入一个字节的数据,对应的接口函数如下所示:
+
+ ```c
+ int32_t SdioWriteBytesToFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size);
+ ```
+
+ **表 10** SdioWriteBytesToFunc0函数的参数和返回值描述
+
+ | 参数 | 参数描述 |
+ | -------- | -------- |
+ | handle | DevHandle类型,SDIO控制器的设备句柄 |
+ | data | uint8_t类型指针,待写入数据 |
+ | addr | uint32_t类型,待写入数据的起始地址 |
+ | size | uint32_t类型,待写入数据的长度 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | SDIO写数据成功 |
+ | 负数 | SDIO写数据失败 |
+
+ 向SDIO function 0的指定地址空间写入指定长度的数据的示例如下:
+
+ ```c
+ int32_t ret;
+ uint8_t wbuff = 1;
+ // 向SDIO function 0地址0x2中写入1字节的数据
+ ret = SdioWriteBytesToFunc0(handle, &wbuff, 0x2, 1);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioWriteBytesToFunc0: sdio write bytes to func0 fail, ret:%d\n", ret);
+ return ret;
+ }
+ ```
- 从SDIO function 0的指定地址空间读取指定长度的数据
- 当前只支持读取一个字节的数据,对应的接口函数如下所示:
+ 当前只支持读取一个字节的数据,对应的接口函数如下所示:
- ```c
- int32_t SdioReadBytesFromFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size);
- ```
+ ```c
+ int32_t SdioReadBytesFromFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size);
+ ```
- **表11** SdioReadBytesFromFunc0函数的参数和返回值描述
+ **表 11** SdioReadBytesFromFunc0函数的参数和返回值描述
- | 参数 | 参数描述 |
- | -------- | -------- |
- | handle | SDIO控制器的设备句柄 |
- | data | 接收读取数据的指针 |
- | addr | 待读取数据的起始地址 |
- | size | 待读取数据的长度 |
- | **返回值** | **返回值描述** |
- | 0 | SDIO读数据成功 |
- | 负数 | SDIO读数据失败 |
+ | 参数 | 参数描述 |
+ | -------- | -------- |
+ | handle | DevHandle类型,SDIO控制器的设备句柄 |
+ | data | uint8_t类型指针,接收读取数据 |
+ | addr | uint32_t类型,待读取数据的起始地址 |
+ | size | uint32_t类型,待读取数据的长度 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | SDIO读数据成功 |
+ | 负数 | SDIO读数据失败 |
- 从SDIO function 0的指定地址空间读取指定长度的数据的示例如下:
+ 从SDIO function 0的指定地址空间读取指定长度的数据的示例如下:
- ```c
- int32_t ret;
- uint8_t rbuff;
- /* 从SDIO function 0设备地址0x2中读取1字节的数据 */
- ret = SdioReadBytesFromFunc0(handle, &rbuff, 0x2, 1);
- if (ret != 0) {
- HDF_LOGE("SdioReadBytesFromFunc0: failed, ret %d\n", ret);
- }
- ```
+ ```c
+ int32_t ret;
+ uint8_t rbuff;
+ /* 从SDIO function 0设备地址0x2中读取1字节的数据 */
+ ret = SdioReadBytesFromFunc0(handle, &rbuff, 0x2, 1);
+ if (ret != 0) {
+ HDF_LOGE("SdioReadBytesFromFunc0: sdio read bytes from func0 fail, ret:%d\n", ret);
+ return ret;
+ }
+ ```
#### 释放SDIO中断
@@ -399,23 +436,24 @@ if (ret != 0) {
int32_t SdioReleaseIrq(DevHandle handle);
- **表12** SdioReleaseIrq函数的参数和返回值描述
+**表 12** SdioReleaseIrq函数的参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | SDIO控制器的设备句柄 |
+| handle | DevHandle类型,SDIO控制器的设备句柄 |
| **返回值** | **返回值描述** |
-| 0 | 释放SDIO中断成功 |
+| HDF_SUCCESS | 释放SDIO中断成功 |
| 负数 | 释放SDIO中断失败 |
释放SDIO中断的示例如下:
```c
int32_t ret;
-/* 释放SDIO中断 */
+// 释放SDIO中断
ret = SdioReleaseIrq(handle);
-if (ret != 0) {
- HDF_LOGE("SdioReleaseIrq: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioReleaseIrq: sdio release irq fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -425,23 +463,24 @@ if (ret != 0) {
int32_t SdioDisableFunc(DevHandle handle);
- **表13** SdioDisableFunc函数的参数和返回值描述
+**表 13** SdioDisableFunc函数的参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | SDIO控制器的设备句柄 |
+| handle | DevHandle类型,SDIO控制器的设备句柄 |
| **返回值** | **返回值描述** |
-| 0 | 去使能SDIO设备成功 |
+| HDF_SUCCESS | 去使能SDIO设备成功 |
| 负数 | 去使能SDIO设备失败 |
去使能SDIO设备的示例如下:
```c
int32_t ret;
-/* 去使能SDIO设备 */
+// 去使能SDIO设备
ret = SdioDisableFunc(handle);
-if (ret != 0) {
- HDF_LOGE("SdioDisableFunc: failed, ret %d\n", ret);
+if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SdioDisableFunc: sdio disable func fail, ret:%d\n", ret);
+ return ret;
}
```
@@ -453,16 +492,16 @@ if (ret != 0) {
void SdioReleaseHost(DevHandle handle);
```
- **表14** SdioReleaseHost函数的参数描述
+**表 14** SdioReleaseHost函数的参数描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | SDIO控制器的设备句柄 |
+| handle | DevHandle类型,SDIO控制器的设备句柄 |
释放HOST的示例如下:
```c
-SdioReleaseHost(handle); /* 释放HOST */
+SdioReleaseHost(handle); // 释放HOST
```
#### 关闭SDIO控制器
@@ -475,16 +514,16 @@ void SdioClose(DevHandle handle);
该函数会释放掉申请的资源。
- **表15** SdioClose函数的参数描述
+**表 15** SdioClose函数的参数描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | SDIO控制器的设备句柄 |
+| handle | DevHandle类型,SDIO控制器的设备句柄 |
关闭SDIO控制器的示例如下:
```c
-SdioClose(handle); /* 关闭SDIO控制器 */
+SdioClose(handle); // 关闭SDIO控制器
```
### 使用实例
diff --git a/zh-cn/device-dev/driver/driver-platform-sdio-develop.md b/zh-cn/device-dev/driver/driver-platform-sdio-develop.md
index b5d7e6205a959a27f5be0d71202e9435a4604043..bde6b9371d4e1ed14ba2878c45beec52d8db725f 100755
--- a/zh-cn/device-dev/driver/driver-platform-sdio-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-sdio-develop.md
@@ -13,11 +13,20 @@ SDIO(Secure Digital Input and Output)由SD卡发展而来,与SD卡统称
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
-**图1** SDIO独立服务模式结构图
+SDIO模块各分层作用:
+
+- 接口层提供打开SDIO设备、设置块的大小、读取数据、写数据、设置公共信息、获取公共信息、刷新数据、独占HOST、释放Host、使能SDIO功能设备、去使能SDIO功能设备、申请中断、释放中断关闭SDIO设备的接口。
+
+- 核心层主要提供SDIO控制器的添加、移除及管理的能力,通过钩子函数与适配层交互。
+
+- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
-![image](figures/独立服务模式结构图.png "SDIO独立服务模式结构图")
+**图 1** SDIO独立服务模式结构图
+
+![SDIO独立服务模式结构图](figures/独立服务模式结构图.png)
### 约束与限制
@@ -36,7 +45,6 @@ SDIO的应用比较广泛,目前,有许多手机都支持SDIO功能,并且
SdioDeviceOps定义:
```c
-/* 函数模板 */
struct SdioDeviceOps {
int32_t (*incrAddrReadBytes)(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size);
int32_t (*incrAddrWriteBytes)(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size);
@@ -58,19 +66,19 @@ struct SdioDeviceOps {
};
```
- **表1** SdioDeviceOps结构体成员的钩子函数功能说明
+**表 1** SdioDeviceOps结构体成员的钩子函数功能说明
| 函数 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
-| incrAddrReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t,地址值
size:uint32_t,大小 | data:uint8_t指针,传出值 | HDF_STATUS相关状态 | 从指定的SDIO地址增量读取给定长度的数据 |
-| incrAddrWriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t指针,传入值
addr:uint32_t,地址值
size:uint32_t,大小 | 无 | HDF_STATUS相关状态 | 将给定长度的数据增量写入指定的SDIO地址 |
-| fixedAddrReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t,地址值
size:uint32_t,大小
scatterLen:uint32_t,数据长度 | data:uint8_t指针,传出值 | HDF_STATUS相关状态 | 从固定SDIO地址读取给定长度的数据。 |
-| fixedAddrWriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t指针,传入值
addr:uint32_t,地址值
size:uint32_t,大小
scatterLen:uint32_t,数据长度 | 无 | HDF_STATUS相关状态 | 将给定长度的数据写入固定SDIO地址 |
-| func0ReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t,地址值
size:uint32_t,大小 | data:uint8_t指针,传出值 | HDF_STATUS相关状态 | 从SDIO函数0的地址空间读取给定长度的数据。 |
-| func0WriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t指针,传入值
addr:uint32_t,地址值
size:uint32_t,大小 | 无 | HDF_STATUS相关状态 | 将给定长度的数据写入SDIO函数0的地址空间。 |
-| setBlockSize | dev:结构体指针,SDIO设备控制器
blockSize:uint32_t,Block大小 | 无 | HDF_STATUS相关状态 | 设置block大小 |
-| getCommonInfo | dev:联合体指针,SDIO设备控制器
infoType:uint32_t,info类型 | info:结构体指针,传出SdioFuncInfo信息 | HDF_STATUS相关状态 | 获取CommonInfo,说明见下 |
-| setCommonInfo | dev:结构体指针,SDIO设备控制器
info:联合体指针,SdioFuncInfo信息传入
infoType:uint32_t,info类型 | 无 | HDF_STATUS相关状态 | 设置CommonInfo,说明见下 |
+| incrAddrReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t类型,地址值
size:uint32_t类型,大小 | data:uint8_t类型指针,传出值 | HDF_STATUS相关状态 | 从指定的SDIO地址增量读取给定长度的数据 |
+| incrAddrWriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t类型指针,传入值
addr:uint32_t类型,地址值
size:uint32_t类型,大小 | 无 | HDF_STATUS相关状态 | 将给定长度的数据增量写入指定的SDIO地址 |
+| fixedAddrReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t类型,地址值
size:uint32_t类型,大小
scatterLen:uint32_t类型,数据长度 | data:uint8_t类型指针,传出值 | HDF_STATUS相关状态 | 从固定SDIO地址读取给定长度的数据。 |
+| fixedAddrWriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t类型指针,传入值
addr:uint32_t类型,地址值
size:uint32_t类型,大小
scatterLen:uint32_t类型,数据长度 | 无 | HDF_STATUS相关状态 | 将给定长度的数据写入固定SDIO地址 |
+| func0ReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t类型,地址值
size:uint32_t类型,大小 | data:uint8_t类型指针,传出值 | HDF_STATUS相关状态 | 从SDIO函数0的地址空间读取给定长度的数据。 |
+| func0WriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t类型指针,传入值
addr:uint32_t类型,地址值
size:uint32_t类型,大小 | 无 | HDF_STATUS相关状态 | 将给定长度的数据写入SDIO函数0的地址空间。 |
+| setBlockSize | dev:结构体指针,SDIO设备控制器
blockSize:uint32_t类型,Block大小 | 无 | HDF_STATUS相关状态 | 设置block大小 |
+| getCommonInfo | dev:联合体指针,SDIO设备控制器
infoType:uint32_t类型,info类型 | info:结构体指针,传出SdioFuncInfo信息 | HDF_STATUS相关状态 | 获取CommonInfo,说明见下 |
+| setCommonInfo | dev:结构体指针,SDIO设备控制器
info:联合体指针,SdioFuncInfo信息传入
infoType:uint32_t类型,info类型 | 无 | HDF_STATUS相关状态 | 设置CommonInfo,说明见下 |
| flushData | dev:结构体指针,SDIO设备控制器 | 无 | HDF_STATUS相关状态 | 当SDIO需要重新初始化或发生意外错误时调用的函数 |
| enableFunc | dev:结构体指针,SDIO设备控制器 | 无 | HDF_STATUS相关状态 | 使能SDIO设备 |
| disableFunc | dev:结构体指针,SDIO设备控制器 | 无 | HDF_STATUS相关状态 | 去使能SDIO设备 |
@@ -87,267 +95,287 @@ struct SdioDeviceOps {
### 开发步骤
-SDIO模块适配HDF框架的三个必选环节是实例化驱动入口,配置属性文件,以及实例化SDIO控制器对象。
+SDIO模块适配包含以下四个步骤:
1. 实例化驱动入口
- - 实例化HdfDriverEntry结构体成员。
- - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
+
+ - 实例化HdfDriverEntry结构体成员。
+
+ - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2. 配置属性文件
- - 在device_info.hcs文件中添加deviceNode描述。
- - 【可选】添加sdio_config.hcs器件属性文件。
+
+ - 在device_info.hcs文件中添加deviceNode描述。
+
+ - 【可选】添加sdio_config.hcs器件属性文件。
3. 实例化SDIO控制器对象
- - 初始化SdioDevice成员。
- - 实例化SdioDevice成员SdioDeviceOps。
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化SdioDevice成员SdioDeviceOps,其定义和成员说明见[接口说明](#接口说明)。
+
+ - 初始化SdioDevice成员。
+
+ - 实例化SdioDevice成员SdioDeviceOps。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化SdioDevice成员SdioDeviceOps,其定义和成员说明见[接口说明](#接口说明)。
4. 驱动调试
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如SDIO控制状态,中断响应情况等。
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如SDIO控制状态,中断响应情况,读写数据是否成功等。
+
### 开发实例
-下方将以sdio_adapter.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-
-1. 驱动开发首先需要实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。
-
- HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
-
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
- SDIO 驱动入口参考:
-
- ```c
- struct HdfDriverEntry g_sdioDriverEntry = {
- .moduleVersion = 1,
- .Bind = Hi35xxLinuxSdioBind, // 见Bind开发参考
- .Init = Hi35xxLinuxSdioInit, // 见Init开发参考
- .Release = Hi35xxLinuxSdioRelease, // 见Release开发参考
- .moduleName = "HDF_PLATFORM_SDIO", // 【必要且与HCS文件中里面的moduleName匹配】
- };
- /* 调用HDF_INIT将驱动入口注册到HDF框架中 */
- HDF_INIT(g_sdioDriverEntry);
- ```
-
-2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在sdio_config.hcs中配置器件属性。
-
- deviceNode信息与驱动入口注册相关,器件属性值与核心层SdioDevice成员的默认值或限制范围有密切关系。
-
- 本例只有一个SDIO控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在sdio_config文件中增加对应的器件属性。
-
- - device_info.hcs 配置参考:
-
- ```c
- root {
- device_info {
- match_attr = "hdf_manager";
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_sdio :: device {
- device0 :: deviceNode {
- policy = 1;
- priority = 70;
- permission = 0644;
- moduleName = "HDF_PLATFORM_SDIO"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。
- serviceName = "HDF_PLATFORM_MMC_2"; // 【必要】驱动对外发布服务的名称,必须唯一。
- deviceMatchAttr = "hisilicon_hi35xx_sdio_0"; // 【必要】用于配置控制器私有数据,要与sdio_config.hcs中对应控制器保持一致。
- }
- }
- }
- }
- }
- ```
-
- - sdio_config.hcs 配置参考:
-
-
- ```c
- root {
- platform {
- sdio_config {
- template sdio_controller {
- match_attr = "";
- hostId = 2; // 【必要】模式固定为2,在mmc_config.hcs有介绍。
- devType = 2; // 【必要】模式固定为2,在mmc_config.hcs有介绍。
- }
- controller_0x2dd1 :: sdio_controller {
- match_attr = "hisilicon_hi35xx_sdio_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致。
- }
- }
- }
- ```
-
- 需要注意的是,新增sdio_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
-
- 例如:本例中sdio_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/sdio/sdio_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/sdio/sdio_config.hcs" // 配置文件相对路径
- ```
-
-3. 完成属性文件配置之后,下一步就是以核心层SdioDevice对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化SdioDevice成员SdioDeviceOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
-
- - 自定义结构体参考:
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,而且sdio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。
-
- ```c
- typedef struct {
- uint32_t maxBlockNum; // 单个request最大的block个数
- uint32_t maxBlockSize; // 单个block最大的字节数1~2048
- uint32_t maxRequestSize; // 单个request最大的字节数1~2048
- uint32_t enTimeout; // 最大超时时间,单位毫秒,且不能超过一秒。
- uint32_t funcNum; // 函数编号1~7
- uint32_t irqCap; // 中断能力
- void *data; // 私有数据
- } SdioFuncInfo;
-
- /* SdioDevice是核心层控制器结构体,其中的成员在Bind函数中会被赋值。 */
- struct SdioDevice {
- struct SdDevice sd;
- struct SdioDeviceOps *sdioOps;
- struct SdioRegister sdioReg;
- uint32_t functions;
- struct SdioFunction *sdioFunc[SDIO_MAX_FUNCTION_NUMBER];
- struct SdioFunction *curFunction;
- struct OsalThread thread; // 中断线程
- struct OsalSem sem;
- bool irqPending;
- bool threadRunning;
- };
- ```
-
- - SdioDevice成员钩子函数结构体SdioDeviceOps的实例化,其他成员在Init函数中初始化。
-
- ```c
- static struct SdioDeviceOps g_sdioDeviceOps = {
- .incrAddrReadBytes = Hi35xxLinuxSdioIncrAddrReadBytes,
- .incrAddrWriteBytes = Hi35xxLinuxSdioIncrAddrWriteBytes,
- .fixedAddrReadBytes = Hi35xxLinuxSdioFixedAddrReadBytes,
- .fixedAddrWriteBytes = Hi35xxLinuxSdioFixedAddrWriteBytes,
- .func0ReadBytes = Hi35xxLinuxSdioFunc0ReadBytes,
- .func0WriteBytes = Hi35xxLinuxSdioFunc0WriteBytes,
- .setBlockSize = Hi35xxLinuxSdioSetBlockSize,
- .getCommonInfo = Hi35xxLinuxSdioGetCommonInfo,
- .setCommonInfo = Hi35xxLinuxSdioSetCommonInfo,
- .flushData = Hi35xxLinuxSdioFlushData,
- .enableFunc = Hi35xxLinuxSdioEnableFunc,
- .disableFunc = Hi35xxLinuxSdioDisableFunc,
- .claimIrq = Hi35xxLinuxSdioClaimIrq,
- .releaseIrq = Hi35xxLinuxSdioReleaseIrq,
- .findFunc = Hi35xxLinuxSdioFindFunc,
- .claimHost = Hi35xxLinuxSdioClaimHost,
- .releaseHost = Hi35xxLinuxSdioReleaseHost,
- };
- ```
-
- - Bind函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS 定义)。
-
- **表2** Bind函数入参及返回值
-
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化SdioCntlr成员,调用核心层SdioCntlrAdd函数,以及其他驱动适配者自定义初始化操作。
+下方将以//drivers/hdf_core/adapter/khdf/linux/model/storage/sdio_adapter.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-
- ```c
- static int32_t Hi35xxLinuxSdioBind(struct HdfDeviceObject *obj)
- {
- struct MmcCntlr *cntlr = NULL;
- int32_t ret;
- ...
- cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr));// 分配内存
- ...
- cntlr->ops = &g_sdioCntlrOps; //【必要】struct MmcCntlrOps g_sdioCntlrOps={
- // .rescanSdioDev = Hi35xxLinuxSdioRescan,};
- cntlr->hdfDevObj = obj; //【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
- obj->service = &cntlr->service; //【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
- ret = Hi35xxLinuxSdioCntlrParse(cntlr, obj); //【必要】初始化cntlr的index、devType,失败则goto _ERR。
- ...
- ret = MmcCntlrAdd(cntlr); //【必要】调用核心层mmc_core.c的函数,失败则goto _ERR。
- ...
- ret = MmcCntlrAllocDev(cntlr, (enum MmcDevType)cntlr->devType); //【必要】调用核心层mmc_core.c的函数,失败则goto _ERR。
- ...
-
- MmcDeviceAddOps(cntlr->curDev, &g_sdioDeviceOps); //【必要】调用核心层mmc_core.c的函数,钩子函数挂载。
- HDF_LOGD("Hi35xxLinuxSdioBind: Success!");
- return HDF_SUCCESS;
-
- _ERR:
- Hi35xxLinuxSdioDeleteCntlr(cntlr);
- HDF_LOGE("Hi35xxLinuxSdioBind: Fail!");
- return HDF_FAILURE;
- }
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- HDF_STATUS相关状态。
-
- 函数说明:
-
- 无操作,可根据驱动适配者需要添加。
+1. 实例化驱动入口
+
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+
+ SDIO 驱动入口参考:
+
+ ```c
+ struct HdfDriverEntry g_sdioDriverEntry = {
+ .moduleVersion = 1,
+ .Bind = Hi35xxLinuxSdioBind, // 见Bind开发参考
+ .Init = Hi35xxLinuxSdioInit, // 见Init开发参考
+ .Release = Hi35xxLinuxSdioRelease, // 见Release开发参考
+ .moduleName = "HDF_PLATFORM_SDIO", // 【必要且与HCS文件中里面的moduleName匹配】
+ };
+ HDF_INIT(g_sdioDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
+
+2. 配置属性文件
+
+ 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在sdio_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层SdioDevice成员的默认值或限制范围有密切关系。本例只有一个SDIO控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在sdio_config文件中增加对应的器件属性。
+
+ 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,SDIO设备控制器具体配置为1,表示驱动对内核态发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。SDIO设备控制器具体配置为30 |
+ | permission | 驱动创建设备节点权限,SDIO设备控制器具体配置为0664 |
+ | moduleName | 驱动名称,SDIO设备控制器固定为hi3516_mmc_driver |
+ | serviceName | 驱动对外发布服务的名称,SDIO设备控制器服务名设置为HDF_PLATFORM_MMC_2|
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,SDIO设备控制器设置为hi3516_mmc_sdio|
+
+ - device_info.hcs 配置参考:
+
+ ```c
+ root {
+ device_info {
+ match_attr = "hdf_manager";
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_sdio :: device {
+ device0 :: deviceNode {
+ policy = 1;
+ priority = 70;
+ permission = 0644;
+ moduleName = "HDF_PLATFORM_SDIO"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。
+ serviceName = "HDF_PLATFORM_MMC_2"; // 【必要】驱动对外发布服务的名称,必须唯一。
+ deviceMatchAttr = "hisilicon_hi35xx_sdio_0"; // 【必要】用于配置控制器私有数据,要与sdio_config.hcs中对应控制器保持一致。
+ }
+ }
+ }
+ }
+ }
+ ```
+
+ - sdio_config.hcs 配置参考:
+
+ ```c
+ root {
+ platform {
+ sdio_config {
+ template sdio_controller {
+ match_attr = "";
+ hostId = 2; // 【必要】模式固定为2,在mmc_config.hcs有介绍。
+ devType = 2; // 【必要】模式固定为2,在mmc_config.hcs有介绍。
+ }
+ controller_0x2dd1 :: sdio_controller {
+ match_attr = "hisilicon_hi35xx_sdio_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致。
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增sdio_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
+
+ 例如:本例中sdio_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/sdio/sdio_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/sdio/sdio_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化SDIO设备控制器对象
+
+ 完成属性文件配置之后,下一步就是以核心层SdioDevice对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化SdioDevice成员SdioDeviceOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+
+ - 自定义结构体参考:
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,而且sdio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。
+
+ ```c
+ typedef struct {
+ uint32_t maxBlockNum; // 单个request最大的block个数
+ uint32_t maxBlockSize; // 单个block最大的字节数1~2048
+ uint32_t maxRequestSize; // 单个request最大的字节数1~2048
+ uint32_t enTimeout; // 最大超时时间,单位毫秒,且不能超过一秒。
+ uint32_t funcNum; // 函数编号1~7
+ uint32_t irqCap; // 中断能力
+ void *data; // 私有数据
+ } SdioFuncInfo;
+
+ // SdioDevice是核心层控制器结构体,其中的成员在Bind函数中会被赋值。
+ struct SdioDevice {
+ struct SdDevice sd;
+ struct SdioDeviceOps *sdioOps;
+ struct SdioRegister sdioReg;
+ uint32_t functions;
+ struct SdioFunction *sdioFunc[SDIO_MAX_FUNCTION_NUMBER];
+ struct SdioFunction *curFunction;
+ struct OsalThread thread; // 中断线程
+ struct OsalSem sem;
+ bool irqPending;
+ bool threadRunning;
+ };
+ ```
+
+ - SdioDevice成员钩子函数结构体SdioDeviceOps的实例化。
+
+ ```c
+ static struct SdioDeviceOps g_sdioDeviceOps = {
+ .incrAddrReadBytes = Hi35xxLinuxSdioIncrAddrReadBytes,
+ .incrAddrWriteBytes = Hi35xxLinuxSdioIncrAddrWriteBytes,
+ .fixedAddrReadBytes = Hi35xxLinuxSdioFixedAddrReadBytes,
+ .fixedAddrWriteBytes = Hi35xxLinuxSdioFixedAddrWriteBytes,
+ .func0ReadBytes = Hi35xxLinuxSdioFunc0ReadBytes,
+ .func0WriteBytes = Hi35xxLinuxSdioFunc0WriteBytes,
+ .setBlockSize = Hi35xxLinuxSdioSetBlockSize,
+ .getCommonInfo = Hi35xxLinuxSdioGetCommonInfo,
+ .setCommonInfo = Hi35xxLinuxSdioSetCommonInfo,
+ .flushData = Hi35xxLinuxSdioFlushData,
+ .enableFunc = Hi35xxLinuxSdioEnableFunc,
+ .disableFunc = Hi35xxLinuxSdioDisableFunc,
+ .claimIrq = Hi35xxLinuxSdioClaimIrq,
+ .releaseIrq = Hi35xxLinuxSdioReleaseIrq,
+ .findFunc = Hi35xxLinuxSdioFindFunc,
+ .claimHost = Hi35xxLinuxSdioClaimHost,
+ .releaseHost = Hi35xxLinuxSdioReleaseHost,
+ };
+ ```
+
+ - Bind函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态 (表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化SdioCntlr成员,调用核心层SdioCntlrAdd函数,以及其他驱动适配者自定义初始化操作。
- ```c
- static int32_t Hi35xxLinuxSdioInit(struct HdfDeviceObject *obj)
- {
- (void)obj; // 无操作,可根据驱动适配者的需要进行添加
- HDF_LOGD("Hi35xxLinuxSdioInit: Success!");
- return HDF_SUCCESS;
- }
- ```
- - Release函数开发参考
+ ```c
+ static int32_t Hi35xxLinuxSdioBind(struct HdfDeviceObject *obj)
+ {
+ struct MmcCntlr *cntlr = NULL;
+ int32_t ret;
+ ......
+ cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr));// 分配内存
+ ......
+ cntlr->ops = &g_sdioCntlrOps; // 【必要】struct MmcCntlrOps g_sdioCntlrOps={
+ // .rescanSdioDev = Hi35xxLinuxSdioRescan,};
+ cntlr->hdfDevObj = obj; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
+ obj->service = &cntlr->service; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
+ ret = Hi35xxLinuxSdioCntlrParse(cntlr, obj); // 【必要】初始化cntlr的index、devType,失败则goto _ERR。
+ ......
+ ret = MmcCntlrAdd(cntlr); // 【必要】调用核心层mmc_core.c的函数,失败则goto _ERR。
+ ......
+ ret = MmcCntlrAllocDev(cntlr, (enum MmcDevType)cntlr->devType); // 【必要】调用核心层mmc_core.c的函数,失败则goto _ERR。
+ ......
+
+ MmcDeviceAddOps(cntlr->curDev, &g_sdioDeviceOps); // 【必要】调用核心层mmc_core.c的函数,钩子函数挂载。
+ HDF_LOGD("Hi35xxLinuxSdioBind: Success!");
+ return HDF_SUCCESS;
+
+ _ERR:
+ Hi35xxLinuxSdioDeleteCntlr(cntlr);
+ HDF_LOGE("Hi35xxLinuxSdioBind: Fail!");
+ return HDF_FAILURE;
+ }
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态。
- 入参:
+ 函数说明:
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+ 无操作,可根据驱动适配者需要添加。
- 返回值:
+ ```c
+ static int32_t Hi35xxLinuxSdioInit(struct HdfDeviceObject *obj)
+ {
+ (void)obj; // 无操作,可根据驱动适配者的需要进行添加
+ HDF_LOGD("Hi35xxLinuxSdioInit: Success!");
+ return HDF_SUCCESS;
+ }
+ ```
- 无。
+ - Release函数开发参考
- 函数说明:
+ 入参:
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Bind函数中具备对应赋值的操作。
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Bind函数中具备对应赋值的操作。
- ```c
- static void Hi35xxLinuxSdioRelease(struct HdfDeviceObject *obj)
- {
- if (obj == NULL) {
- return;
- }
- Hi35xxLinuxSdioDeleteCntlr((struct MmcCntlr *)obj->service); // 【必要】自定义的内存释放函数,这里有HdfDeviceObject到MmcCntlr的强制转换
- }
- ```
\ No newline at end of file
+ ```c
+ static void Hi35xxLinuxSdioRelease(struct HdfDeviceObject *obj)
+ {
+ if (obj == NULL) {
+ return;
+ }
+ Hi35xxLinuxSdioDeleteCntlr((struct MmcCntlr *)obj->service); // 【必要】自定义的内存释放函数,这里有HdfDeviceObject到MmcCntlr的强制转换
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如SDIO控制状态,中断响应情况,读写数据是否成功等。
diff --git a/zh-cn/device-dev/driver/driver-platform-spi-des.md b/zh-cn/device-dev/driver/driver-platform-spi-des.md
index 0f5f1f7432a2b3e7d4ad4ab6963ee4bc1a845ee2..9c1c3946213b2334c4095e1a099b627c4ee7d667 100644
--- a/zh-cn/device-dev/driver/driver-platform-spi-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-spi-des.md
@@ -7,37 +7,70 @@
SPI指串行外设接口(Serial Peripheral Interface),是一种高速的,全双工,同步的通信总线。SPI是由Motorola公司开发,用于在主设备和从设备之间进行通信。
SPI接口定义了操作SPI设备的通用方法集合,包括:
- - SPI设备句柄获取和释放。
- - SPI读写:从SPI设备读取或写入指定长度数据。
- - SPI自定义传输:通过消息传输结构体执行任意读写组合过程。
- - SPI设备配置:获取和设置SPI设备属性。
+
+- SPI设备句柄获取和释放。
+
+- SPI读写:从SPI设备读取或写入指定长度数据。
+
+- SPI自定义传输:通过消息传输结构体执行任意读写组合过程。
+
+- SPI设备配置:获取和设置SPI设备属性。
### 运作机制
在HDF框架中,SPI的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。
+独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
+
+- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
+- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
+
+**图 1** SPI独立服务模式结构图
+
+![SPI独立服务模式结构图](figures/独立服务模式结构图.png)
+
+SPI模块各分层作用:
+
+- 接口层提供打开SPI设备、SPI写数据、SPI读数据、SPI传输、配置SPI设备属性、获取SPI设备属性、关闭SPI设备的接口。
+
+- 核心层主要提供SPI控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
+
+- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
+
SPI以主从方式工作,通常有一个主设备和一个或者多个从设备。主设备和从设备之间一般用4根线相连,它们分别是:
- - SCLK:时钟信号,由主设备产生;
- - MOSI:主设备数据输出,从设备数据输入;
- - MISO:主设备数据输入,从设备数据输出;
- - CS:片选,从设备使能信号,由主设备控制。
-一个主设备和两个从设备的连接示意图如下所示,Device A和Device B共享主设备的SCLK、MISO和MOSI三根引脚,Device A的片选CS0连接主设备的CS0,Device B的片选CS1连接主设备的CS1。
+- SCLK:时钟信号,由主设备产生;
+
+- MOSI:主设备数据输出,从设备数据输入;
+
+- MISO:主设备数据输入,从设备数据输出;
-**图1** SPI主从设备连接示意图
+- CS:片选,从设备使能信号,由主设备控制。
-![image](figures/SPI主从设备连接示意图.png "SPI主从设备连接示意图")
+一个主设备和两个从设备的连接示意图如图2所示,Device A和Device B共享主设备的SCLK、MISO和MOSI三根引脚,Device A的片选CS0连接主设备的CS0,Device B的片选CS1连接主设备的CS1。
+
+**图 2** SPI主从设备连接示意图
+
+![SPI主从设备连接示意图](figures/SPI主从设备连接示意图.png)
- SPI通信通常由主设备发起,通过以下步骤完成一次通信:
- 1. 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。
- 2. 通过SCLK给选中的从设备提供时钟信号。
- 3. 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。
+
+ 1. 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。
+
+ 2. 通过SCLK给选中的从设备提供时钟信号。
+
+ 3. 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。
- 根据SCLK时钟信号的CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)的不同组合,SPI有以下四种工作模式:
- - CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。
- - CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。
- - CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。
- - CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。
+
+ - CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。
+
+ - CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。
+
+ - CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。
+
+ - CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。
### 约束与限制
@@ -53,7 +86,7 @@ SPI通常用于与闪存、实时时钟、传感器以及模数/数模转换器
SPI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/spi_if.h。
-**表1** SPI驱动API接口功能介绍
+**表 1** SPI驱动API接口功能介绍
| 接口名 | 接口描述 |
| -------- | -------- |
@@ -69,9 +102,9 @@ SPI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
使用SPI的一般流程如下图所示。
- **图2** SPI使用流程图
+**图 3** SPI使用流程图
- ![image](figures/SPI使用流程图.png "SPI使用流程图")
+![SPI使用流程图](figures/SPI使用流程图.png)
#### 获取SPI设备句柄
@@ -81,28 +114,28 @@ SPI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
DevHandle SpiOpen(const struct SpiDevInfo *info);
```
- **表2** SpiOpen参数和返回值描述
+**表 2** SpiOpen参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| info | SPI设备描述符 |
+| info | 结构体类型,SPI设备描述符 |
| **返回值** | **返回值描述** |
| NULL | 获取SPI设备句柄失败 |
-| 设备句柄 | 对应的SPI设备句柄 |
+| 设备句柄 | 获取对应的SPI设备句柄成功 |
假设系统中的SPI设备总线号为0,片选号为0,获取该SPI设备句柄的示例如下:
```c
-struct SpiDevInfo spiDevinfo; /* SPI设备描述符 */
-DevHandle spiHandle = NULL; /* SPI设备句柄 */
-spiDevinfo.busNum = 0; /* SPI设备总线号 */
-spiDevinfo.csNum = 0; /* SPI设备片选号 */
+struct SpiDevInfo spiDevinfo; // SPI设备描述符
+DevHandle spiHandle = NULL; // SPI设备句柄
+spiDevinfo.busNum = 0; // SPI设备总线号
+spiDevinfo.csNum = 0; // SPI设备片选号
-/* 获取SPI设备句柄 */
+// 获取SPI设备句柄
spiHandle = SpiOpen(&spiDevinfo);
if (spiHandle == NULL) {
- HDF_LOGE("SpiOpen: failed\n");
- return;
+ HDF_LOGE("SpiOpen: spi open fail!\n");
+ return HDF_FAILURE;
}
```
@@ -114,22 +147,23 @@ if (spiHandle == NULL) {
int32_t SpiGetCfg(DevHandle handle, struct SpiCfg *cfg);
```
- **表3** SpiGetCfg参数和返回值描述
+**表 3** SpiGetCfg参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | SPI设备句柄 |
-| cfg | SPI设备配置参数 |
+| handle | DevHandle类型,SPI设备句柄 |
+| cfg | 结构体指针类型,SPI设备配置参数 |
| **返回值** | **返回值描述** |
-| 0 | 获取配置成功 |
-| 负数 | 获取配置失败 |
+| HDF_SUCCESS | 获取设备属性成功 |
+| 负数 | 获取设备属性失败 |
```c
int32_t ret;
-struct SpiCfg cfg = {0}; /* SPI配置信息*/
-ret = SpiGetCfg(spiHandle, &cfg); /* 获取SPI设备属性 */
-if (ret != 0) {
+struct SpiCfg cfg = {0}; // SPI配置信息
+ret = SpiGetCfg(spiHandle, &cfg); // 获取SPI设备属性
+if (ret != HDF_SUCCESS) {
HDF_LOGE("SpiGetCfg: failed, ret %d\n", ret);
+ return ret;
}
```
@@ -141,26 +175,27 @@ if (ret != 0) {
int32_t SpiSetCfg(DevHandle handle, struct SpiCfg *cfg);
```
- **表4** SpiSetCfg参数和返回值描述
+**表 4** SpiSetCfg参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | SPI设备句柄 |
-| cfg | SPI设备配置参数 |
+| handle | DevHandle类型,SPI设备句柄 |
+| cfg | 结构体指针类型,SPI设备配置参数 |
| **返回值** | **返回值描述** |
-| 0 | 配置成功 |
-| 负数 | 配置失败 |
+| HDF_SUCCESS | 配置设备属性成功 |
+| 负数 | 配置设备属性失败 |
```c
int32_t ret;
-struct SpiCfg cfg = {0}; /* SPI配置信息*/
-cfg.mode = SPI_MODE_LOOP; /* 以回环模式进行通信 */
-cfg.transferMode = PAL_SPI_POLLING_TRANSFER; /* 以轮询的方式进行通信 */
-cfg.maxSpeedHz = 115200; /* 最大传输频率 */
-cfg.bitsPerWord = 8; /* 读写位宽为8比特 */
-ret = SpiSetCfg(spiHandle, &cfg); /* 配置SPI设备属性 */
-if (ret != 0) {
+struct SpiCfg cfg = {0}; // SPI配置信息
+cfg.mode = SPI_MODE_LOOP; // 以回环模式进行通信
+cfg.transferMode = PAL_SPI_POLLING_TRANSFER; // 以轮询的方式进行通信
+cfg.maxSpeedHz = 115200; // 最大传输频率
+cfg.bitsPerWord = 8; // 读写位宽为8比特
+ret = SpiSetCfg(spiHandle, &cfg); // 配置SPI设备属性
+if (ret != HDF_SUCCESS) {
HDF_LOGE("SpiSetCfg: failed, ret %d\n", ret);
+ return ret;
}
```
@@ -168,98 +203,101 @@ if (ret != 0) {
- 向SPI设备写入指定长度的数据
- 如果只向SPI设备写一次数据,则可以通过以下函数完成:
-
- ```c
- int32_t SpiWrite(DevHandle handle, uint8_t *buf, uint32_t len);
- ```
-
- **表5** SpiWrite参数和返回值描述
-
- | **参数** | **参数描述** |
- | -------- | -------- |
- | handle | SPI设备句柄 |
- | buf | 待写入数据的指针 |
- | len | 待写入的数据长度 |
- | **返回值** | **返回值描述** |
- | 0 | 写入成功 |
- | 负数 | 写入失败 |
-
- ```c
- int32_t ret;
- uint8_t wbuff[4] = {0x12, 0x34, 0x56, 0x78};
- /* 向SPI设备写入指定长度的数据 */
- ret = SpiWrite(spiHandle, wbuff, 4);
- if (ret != 0) {
- HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
- }
- ```
+ 如果只向SPI设备写一次数据,则可以通过以下函数完成:
+
+ ```c
+ int32_t SpiWrite(DevHandle handle, uint8_t *buf, uint32_t len);
+ ```
+
+ **表 5** SpiWrite参数和返回值描述
+
+ | **参数** | **参数描述** |
+ | -------- | -------- |
+ | handle | DevHandle类型,SPI设备句柄 |
+ | buf | uint8_t类型指针,待写入数据 |
+ | len | uint32_t类型,待写入的数据长度 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 写入成功 |
+ | 负数 | 写入失败 |
+
+ ```c
+ int32_t ret;
+ uint8_t wbuff[4] = {0x12, 0x34, 0x56, 0x78};
+ // 向SPI设备写入指定长度的数据
+ ret = SpiWrite(spiHandle, wbuff, 4);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
+ return ret;
+ }
+ ```
- 从SPI设备读取指定长度的数据
- 如果只读取一次数据,则可以通过以下函数完成:
-
- ```c
- int32_t SpiRead(DevHandle handle, uint8_t *buf, uint32_t len);
- ```
-
- **表6** SpiRead参数和返回值描述
-
- | **参数** | **参数描述** |
- | -------- | -------- |
- | handle | SPI设备句柄 |
- | buf | 待读取数据的指针 |
- | len | 待读取的数据长度 |
- | **返回值** | **返回值描述** |
- | 0 | 读取成功 |
- | 负数 | 读取失败 |
-
- ```c
- int32_t ret;
- uint8_t rbuff[4] = {0};
- /* 从SPI设备读取指定长度的数据 */
- ret = SpiRead(spiHandle, rbuff, 4);
- if (ret != 0) {
- HDF_LOGE("SpiRead: failed, ret %d\n", ret);
- }
- ```
+ 如果只读取一次数据,则可以通过以下函数完成:
+
+ ```c
+ int32_t SpiRead(DevHandle handle, uint8_t *buf, uint32_t len);
+ ```
+
+ **表 6** SpiRead参数和返回值描述
+
+ | **参数** | **参数描述** |
+ | -------- | -------- |
+ | handle | DevHandle类型,SPI设备句柄 |
+ | buf | uint8_t类型指针,待读取数据 |
+ | len | uint32_t类型,待读取的数据长度 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 读取成功 |
+ | 负数 | 读取失败 |
+
+ ```c
+ int32_t ret;
+ uint8_t rbuff[4] = {0};
+ // 从SPI设备读取指定长度的数据
+ ret = SpiRead(spiHandle, rbuff, 4);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SpiRead: failed, ret %d\n", ret);
+ return ret;
+ }
+ ```
- 自定义传输
- 如果需要发起一次自定义传输,则可以通过以下函数完成:
-
- ```c
- int32_t SpiTransfer(DevHandle handle, struct SpiMsg *msgs, uint32_t count);
- ```
-
- **表7** SpiTransfer参数和返回值描述
-
- | **参数** | **参数描述** |
- | -------- | -------- |
- | handle | SPI设备句柄 |
- | msgs | 待传输数据的数组 |
- | count | msgs数组长度 |
- | **返回值** | **返回值描述** |
- | 0 | 执行成功 |
- | 负数 | 执行失败 |
-
- ```c
- int32_t ret;
- uint8_t wbuff[1] = {0x12};
- uint8_t rbuff[1] = {0};
- struct SpiMsg msg; /* 自定义传输的消息 */
- msg.wbuf = wbuff; /* 写入的数据 */
- msg.rbuf = rbuff; /* 读取的数据 */
- msg.len = 1; /* 读取、写入数据的长度都是1 */
- msg.csChange = 1; /* 进行下一次传输前关闭片选 */
- msg.delayUs = 0; /* 进行下一次传输前不进行延时 */
- msg.speed = 115200; /* 本次传输的速度 */
- /* 进行一次自定义传输,传输的msg个数为1 */
- ret = SpiTransfer(spiHandle, &msg, 1);
- if (ret != 0) {
- HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
- }
- ```
+ 如果需要发起一次自定义传输,则可以通过以下函数完成:
+
+ ```c
+ int32_t SpiTransfer(DevHandle handle, struct SpiMsg *msgs, uint32_t count);
+ ```
+
+ **表 7** SpiTransfer参数和返回值描述
+
+ | **参数** | **参数描述** |
+ | -------- | -------- |
+ | handle | DevHandle类型,SPI设备句柄 |
+ | msgs | 结构体指针,待传输数据的数组 |
+ | count | uint32_t类型,msgs数组长度 |
+ | **返回值** | **返回值描述** |
+ | HDF_SUCCESS | 传输执行成功 |
+ | 负数 | 传输执行失败 |
+
+ ```c
+ int32_t ret;
+ uint8_t wbuff[1] = {0x12};
+ uint8_t rbuff[1] = {0};
+ struct SpiMsg msg; // 自定义传输的消息
+ msg.wbuf = wbuff; // 写入的数据
+ msg.rbuf = rbuff; // 读取的数据
+ msg.len = 1; // 读取、写入数据的长度都是1
+ msg.csChange = 1; // 进行下一次传输前关闭片选
+ msg.delayUs = 0; // 进行下一次传输前不进行延时
+ msg.speed = 115200; // 本次传输的速度
+ // 进行一次自定义传输,传输的msg个数为1
+ ret = SpiTransfer(spiHandle, &msg, 1);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
+ return ret;
+ }
+ ```
#### 销毁SPI设备句柄
@@ -271,14 +309,14 @@ void SpiClose(DevHandle handle);
该函数会释放掉申请的资源。
- **表8** SpiClose参数描述
+**表 8** SpiClose参数描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | SPI设备句柄 |
+| handle | DevHandle类型,SPI设备句柄 |
```c
-SpiClose(spiHandle); /* 销毁SPI设备句柄 */
+SpiClose(spiHandle); // 销毁SPI设备句柄
```
### 使用实例
@@ -294,61 +332,62 @@ SPI设备完整的使用示例如下所示,首先获取SPI设备句柄,然
void SpiTestSample(void)
{
int32_t ret;
- struct SpiCfg cfg; /* SPI配置信息 */
- struct SpiDevInfo spiDevinfo; /* SPI设备描述符 */
- DevHandle spiHandle = NULL; /* SPI设备句柄 */
- struct SpiMsg msg; /* 自定义传输的消息 */
+ struct SpiCfg cfg; // SPI配置信息
+ struct SpiDevInfo spiDevinfo; // SPI设备描述符
+ DevHandle spiHandle = NULL; // SPI设备句柄
+ struct SpiMsg msg; // 自定义传输的消息
uint8_t rbuff[4] = { 0 };
uint8_t wbuff[4] = { 0x12, 0x34, 0x56, 0x78 };
uint8_t wbuff2[4] = { 0xa1, 0xb2, 0xc3, 0xd4 };
- spiDevinfo.busNum = 0; /* SPI设备总线号 */
- spiDevinfo.csNum = 0; /* SPI设备片选号 */
- spiHandle = SpiOpen(&spiDevinfo); /* 根据spiDevinfo获取SPI设备句柄 */
+ spiDevinfo.busNum = 0; // SPI设备总线号
+ spiDevinfo.csNum = 0; // SPI设备片选号
+ spiHandle = SpiOpen(&spiDevinfo); // 根据spiDevinfo获取SPI设备句柄
if (spiHandle == NULL) {
- HDF_LOGE("SpiOpen: failed\n");
+ HDF_LOGE("SpiTestSample: spi open fail!\n");
return;
}
- /* 获取SPI设备属性 */
+ // 获取SPI设备属性
ret = SpiGetCfg(spiHandle, &cfg);
- if (ret != 0) {
- HDF_LOGE("SpiGetCfg: failed, ret %d\n", ret);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SpiTestSample: spi get cfg fail, ret:%d!\n", ret);
goto err;
}
- cfg.maxSpeedHz = 115200; /* 将最大时钟频率改为115200 */
- cfg.bitsPerWord = 8; /* 传输位宽改为8比特 */
- /* 配置SPI设备属性 */
+ cfg.maxSpeedHz = 115200; // 将最大时钟频率改为115200
+ cfg.bitsPerWord = 8; // 传输位宽改为8比特
+ // 配置SPI设备属性
ret = SpiSetCfg(spiHandle, &cfg);
- if (ret != 0) {
- HDF_LOGE("SpiSetCfg: failed, ret %d\n", ret);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SpiTestSample: spi set cfg fail, ret:%d!\n", ret);
goto err;
}
/* 向SPI设备写入指定长度的数据 */
ret = SpiWrite(spiHandle, wbuff, 4);
- if (ret != 0) {
- HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SpiTestSample: spi write fail, ret:%d!\n", ret);
goto err;
}
/* 从SPI设备读取指定长度的数据 */
ret = SpiRead(spiHandle, rbuff, 4);
- if (ret != 0) {
- HDF_LOGE("SpiRead: failed, ret %d\n", ret);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SpiTestSample: spi read fail, ret:%d!\n", ret);
goto err;
}
- msg.wbuf = wbuff2; /* 写入的数据 */
- msg.rbuf = rbuff; /* 读取的数据 */
- msg.len = 4; /* 读取写入数据的长度为4 */
- msg.csChange = 1; /* 进行下一次传输前关闭片选 */
- msg.delayUs = 0; /* 进行下一次传输前不进行延时 */
- msg.speed = 115200; /* 本次传输的速度 */
- /* 进行一次自定义传输,传输的msg个数为1 */
+ msg.wbuf = wbuff2; // 写入的数据
+ msg.rbuf = rbuff; // 读取的数据
+ msg.len = 4; // 读取写入数据的长度为4
+ msg.keepCs = 0; // 当前传输完成后是否保持CS活动,1表述保持,0表示关闭CS
+ msg.delayUs = 0; // 进行下一次传输前不进行延时
+ msg.speed = 115200; // 本次传输的速度
+ // 进行一次自定义传输,传输的msg个数为1
ret = SpiTransfer(spiHandle, &msg, 1);
- if (ret != 0) {
- HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
+ if (ret != HDF_SUCCESS) {
+ HDF_LOGE("SpiTestSample: spi transfer fail, ret:%d!\n", ret);
goto err;
}
+ HDF_LOGD("SpiTestSample: function tests end!");
err:
- /* 销毁SPI设备句柄 */
+ // 销毁SPI设备句柄
SpiClose(spiHandle);
}
```
diff --git a/zh-cn/device-dev/driver/driver-platform-spi-develop.md b/zh-cn/device-dev/driver/driver-platform-spi-develop.md
index 906e067e5db54c62fc2e59d7e5629d7c6b6cc1ee..d8f0496e9b199e9c8d9ed0b6f958623197a498e5 100755
--- a/zh-cn/device-dev/driver/driver-platform-spi-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-spi-develop.md
@@ -8,39 +8,59 @@ SPI即串行外设接口(Serial Peripheral Interface),是一种高速的
### 运作机制
-在HDF框架中,SPI的接口适配模式采用独立服务模式(如图1),在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。
+在HDF框架中,SPI的接口适配模式采用独立服务模式(如图1所示),在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
-**图1** SPI独立服务模式结构图
+**图 1** SPI独立服务模式结构图
+
+![SPI独立服务模式结构图](figures/独立服务模式结构图.png)
+
+SPI模块各分层作用:
+
+- 接口层提供打开SPI设备、SPI写数据、SPI读数据、SPI传输、配置SPI设备属性、获取SPI设备属性、关闭SPI设备的接口。
+
+- 核心层主要提供SPI控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
-![image](figures/独立服务模式结构图.png "RTC独立服务模式结构图")
+- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
SPI以主从方式工作,通常有一个主设备和一个或者多个从设备。主设备和从设备之间一般用4根线相连,它们分别是:
- - SCLK:时钟信号,由主设备产生;
- - MOSI:主设备数据输出,从设备数据输入;
- - MISO:主设备数据输入,从设备数据输出;
- - CS:片选,从设备使能信号,由主设备控制。
-一个主设备和两个从设备的连接示意图如下所示,Device A和Device B共享主设备的SCLK、MISO和MOSI三根引脚,Device A的片选CS0连接主设备的CS0,Device B的片选CS1连接主设备的CS1。
+- SCLK:时钟信号,由主设备产生;
- **图2** SPI主从设备连接示意图
+- MOSI:主设备数据输出,从设备数据输入;
+
+- MISO:主设备数据输入,从设备数据输出;
+
+- CS:片选,从设备使能信号,由主设备控制。
- ![image](figures/SPI主从设备连接示意图.png "SPI主从设备连接示意图")
+一个主设备和两个从设备的连接示意图如图2所示,Device A和Device B共享主设备的SCLK、MISO和MOSI三根引脚,Device A的片选CS0连接主设备的CS0,Device B的片选CS1连接主设备的CS1。
+
+**图 2** SPI主从设备连接示意图
+
+![SPI主从设备连接示意图](figures/SPI主从设备连接示意图.png)
- SPI通信通常由主设备发起,通过以下步骤完成一次通信:
- 1. 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。
- 2. 通过SCLK给选中的从设备提供时钟信号。
- 3. 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。
+
+ 1. 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。
+
+ 2. 通过SCLK给选中的从设备提供时钟信号。
+
+ 3. 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。
- 根据SCLK时钟信号的CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)的不同组合,SPI有以下四种工作模式:
- - CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。
- - CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。
- - CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。
- - CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。
+
+ - CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。
+
+ - CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。
+
+ - CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。
+
+ - CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。
## 开发指导
@@ -64,11 +84,11 @@ struct SpiCntlrMethod {
};
```
- **表1** SpiCntlrMethod结构体成员的钩子函数功能说明
+**表 1** SpiCntlrMethod结构体成员的钩子函数功能说明
| 成员函数 | 入参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- |
-| Transfer | cntlr:结构体指针,核心层SPI控制器。
msg:结构体指针,Spi消息。
count:uint32_t,消息个数。 | HDF_STATUS相关状态 | 传输消息 |
+| Transfer | cntlr:结构体指针,核心层SPI控制器。
msg:结构体指针,Spi消息。
count:uint32_t类型,消息个数。 | HDF_STATUS相关状态 | 传输消息 |
| SetCfg | cntlr:结构体指针,核心层SPI控制器。
cfg:结构体指针,Spi属性。 | HDF_STATUS相关状态 | 设置控制器属性 |
| GetCfg | cntlr:结构体指针,核心层SPI控制器。
cfg:结构体指针,Spi属性。 | HDF_STATUS相关状态 | 获取控制器属性 |
| Open | cntlr:结构体指针,核心层SPI控制器。 | HDF_STATUS相关状态 | 打开SPI |
@@ -77,21 +97,28 @@ struct SpiCntlrMethod {
### 开发步骤
-SPI模块适配HDF框架的三个必选环节是实例化驱动入口,配置属性文件,以及实例化核心层接口函数。
+SPI模块适配包含以下四个步骤:
1. 实例化驱动入口
- - 实例化HdfDriverEntry结构体成员。
- - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
+
+ - 实例化HdfDriverEntry结构体成员。
+
+ - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2. 配置属性文件
- - 在device_info.hcs文件中添加deviceNode描述。
- - 【可选】添加spi_config.hcs器件属性文件。
+
+ - 在device_info.hcs文件中添加deviceNode描述。
+
+ - 【可选】添加spi_config.hcs器件属性文件。
3. 实例化SPI控制器对象
- - 初始化SpiCntlr成员。
- - 实例化SpiCntlr成员SpiCntlrMethod。
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化SpiCntlr成员SpiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。
+
+ - 初始化SpiCntlr成员。
+
+ - 实例化SpiCntlr成员SpiCntlrMethod。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化SpiCntlr成员SpiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。
4. 驱动调试
@@ -101,322 +128,334 @@ SPI模块适配HDF框架的三个必选环节是实例化驱动入口,配置
下方将以//device/soc/hisilicon/common/platform/spi/spi_hi35xx.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 首先需要实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。
-
- HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
-
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
- SPI驱动入口参考:
-
- ```c
- struct HdfDriverEntry g_hdfSpiDevice = {
- .moduleVersion = 1,
- .moduleName = "HDF_PLATFORM_SPI", //【必要且与HCS文件中里面的moduleName匹配】
- .Bind = HdfSpiDeviceBind, //见Bind开发参考
- .Init = HdfSpiDeviceInit, //见Init开发参考
- .Release = HdfSpiDeviceRelease, //见Release开发参考
- };
- /* 调用HDF_INIT将驱动入口注册到HDF框架中 */
- HDF_INIT(g_hdfSpiDevice);
- ```
-
-2. 完成驱动入口注册之后,在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在spi_config.hcs中配置器件属性。
-
- deviceNode信息与驱动入口注册相关,器件属性值与核心层SpiCntlr成员的默认值或限制范围有密切关系。
-
- 本例只有一个SPI控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在spi_config文件中增加对应的器件属性。
-
- - device_info.hcs配置参考
-
- ```c
- root {
- device_info {
- match_attr = "hdf_manager";
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- device_spi :: device { //为每一个SPI控制器配置一个HDF设备节点
- device0 :: deviceNode {
- policy = 2;
- priority = 60;
- permission = 0644;
- moduleName = "HDF_PLATFORM_SPI";
- serviceName = "HDF_PLATFORM_SPI_0";
- deviceMatchAttr = "hisilicon_hi35xx_spi_0";
- }
- device1 :: deviceNode {
- policy = 2;
- priority = 60;
- permission = 0644;
- moduleName = "HDF_PLATFORM_SPI"; // 【必要】用于指定驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。
- serviceName = "HDF_PLATFORM_SPI_1"; // 【必要且唯一】驱动对外发布服务的名称。
- deviceMatchAttr = "hisilicon_hi35xx_spi_1"; // 需要与spi_config.hcs配置文件中的match_attr匹配。
- }
- ...
- }
- }
- }
- }
- ```
-
- - spi_config.hcs配置参考
-
-
- ```c
- root {
- platform {
- spi_config { // 每一个SPI控制器配置私有数据
- template spi_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。
- serviceName = "";
- match_attr = "";
- transferMode = 0; // 数据传输模式:中断传输(0)、流控传输(1)、DMA传输(2)
- busNum = 0; // 总线号
- clkRate = 100000000;
- bitsPerWord = 8; // 传输位宽
- mode = 19; // SPI 数据的输入输出模式
- maxSpeedHz = 0; // 最大时钟频率
- minSpeedHz = 0; // 最小时钟频率
- speed = 2000000; // 当前消息传输速度
- fifoSize = 256; // FIFO大小
- numCs = 1; // 片选号
- regBase = 0x120c0000; // 地址映射需要
- irqNum = 100; // 中断号
- REG_CRG_SPI = 0x120100e4; // CRG_REG_BASE(0x12010000) + 0x0e4
- CRG_SPI_CKEN = 0;
- CRG_SPI_RST = 0;
- REG_MISC_CTRL_SPI = 0x12030024; // MISC_REG_BASE(0x12030000) + 0x24
- MISC_CTRL_SPI_CS = 0;
- MISC_CTRL_SPI_CS_SHIFT = 0;
- }
- controller_0x120c0000 :: spi_controller {
- busNum = 0; // 【必要】总线号
- CRG_SPI_CKEN = 0x10000; // (0x1 << 16) 0:close clk, 1:open clk
- CRG_SPI_RST = 0x1; // (0x1 << 0) 0:cancel reset, 1:reset
- match_attr = "hisilicon_hi35xx_spi_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
- }
- controller_0x120c1000 :: spi_controller {
- busNum = 1;
- CRG_SPI_CKEN = 0x20000; // (0x1 << 17) 0:close clk, 1:open clk
- CRG_SPI_RST = 0x2; // (0x1 << 1) 0:cancel reset, 1:reset
- match_attr = "hisilicon_hi35xx_spi_1";
- regBase = 0x120c1000; // 【必要】地址映射需要
- irqNum = 101; // 【必要】中断号
- }
- ...
- /* 【可选】可新增,但需要在device_info.hcs添加对应的节点。 */
- }
- }
- }
- ```
-
- 需要注意的是,新增spi_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
-
- 例如:本例中spi_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/spi/spi_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/spi/spi_config.hcs" // 配置文件相对路径
- ```
-
-3. 完成属性文件配置之后,下一步就是以核心层SpiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化SpiCntlr成员SpiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
-
- - 自定义结构体参考
-
- 从驱动的角度看,自定义结构体是参数和数据的载体,而且spi_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如设备号、总线号等。
-
-
- ```c
- struct Pl022 { //对应于spi_config.hcs中的参数
- struct SpiCntlr *cntlr;
- struct DListHead deviceList;
- struct OsalSem sem;
- volatile unsigned char *phyBase;
- volatile unsigned char *regBase;
- uint32_t irqNum;
- uint32_t busNum;
- uint32_t numCs;
- uint32_t curCs;
- uint32_t speed;
- uint32_t fifoSize;
- uint32_t clkRate;
- uint32_t maxSpeedHz;
- uint32_t minSpeedHz;
- uint32_t regCrg;
- uint32_t clkEnBit;
- uint32_t clkRstBit;
- uint32_t regMiscCtrl;
- uint32_t miscCtrlCsShift;
- uint32_t miscCtrlCs;
- uint16_t mode;
- uint8_t bitsPerWord;
- uint8_t transferMode;
- };
-
- /* SpiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
- struct SpiCntlr {
- struct IDeviceIoService service;
- struct HdfDeviceObject *device;
- uint32_t busNum;
- uint32_t numCs;
- uint32_t curCs;
- struct OsalMutex lock;
- struct SpiCntlrMethod *method;
- struct DListHead list;
- void *priv;
- };
- ```
-
- - SpiCntlr成员钩子函数结构体SpiCntlrMethod的实例化,其他成员在Init函数中初始化。
-
-
- ```c
- /* spi_hi35xx.c中的示例:钩子函数的实例化 */
- struct SpiCntlrMethod g_method = {
- .Transfer = Pl022Transfer,
- .SetCfg = Pl022SetCfg,
- .GetCfg = Pl022GetCfg,
- .Open = Pl022Open,
- .Close = Pl022Close,
- };
- ```
-
- - Bind函数参考
+1. 实例化驱动入口
- 入参:
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
- 返回值:
+ SPI驱动入口参考:
- HDF_STATUS相关状态。
+ ```c
+ struct HdfDriverEntry g_hdfSpiDevice = {
+ .moduleVersion = 1,
+ .moduleName = "HDF_PLATFORM_SPI", //【必要且与HCS文件中里面的moduleName匹配】
+ .Bind = HdfSpiDeviceBind, // 挂接SPI模块Bind实例化
+ .Init = HdfSpiDeviceInit, // 挂接SPI模块Init实例化
+ .Release = HdfSpiDeviceRelease, // 挂接SPI模块Release实例化
+ };
+ HDF_INIT(g_hdfSpiDevice); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
- 函数说明:
+2. 配置属性文件
- 将SpiCntlr对象同HdfDeviceObject进行了关联。
+ 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode描述。deviceNode信息与驱动入口注册相关。 本例只有一个SPI控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在spi_config.hcs文件中增加对应的器件属性。器件属性值与核心层WatchdogCntlr成员的默认值或限制范围有密切关系,比如busNum设备号,需要在watchdog_config.hcs文件中增加对应的器件属性。
+
+ 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,SPI控制器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。SPI控制器具体配置为60 |
+ | permission | 驱动创建设备节点权限,SPI控制器具体配置为0664 |
+ | moduleName | 驱动名称,SPI控制器固定为HDF_PLATFORM_SPI |
+ | serviceName | 驱动对外发布服务的名称,SPI控制器服务名设置为HDF_PLATFORM_SPI_X,X代表SPI控制器编号|
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,SPI控制器设置为hisilicon_hi35xx_spi_X,X代表SPI控制器编号 |
+
+ - device_info.hcs配置参考
+
+ ```c
+ root {
+ device_info {
+ match_attr = "hdf_manager";
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ device_spi :: device { // 为每一个SPI控制器配置一个HDF设备节点
+ device0 :: deviceNode {
+ policy = 2;
+ priority = 60;
+ permission = 0644;
+ moduleName = "HDF_PLATFORM_SPI";
+ serviceName = "HDF_PLATFORM_SPI_0";
+ deviceMatchAttr = "hisilicon_hi35xx_spi_0";
+ }
+ device1 :: deviceNode {
+ policy = 2; // 驱动服务发布的策略,policy大于等于1(用户态可见为2,仅内核态可见为1)。
+ priority = 60; // 驱动启动优先级
+ permission = 0644; // 驱动创建设备节点权限
+ moduleName = "HDF_PLATFORM_SPI"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。
+ serviceName = "HDF_PLATFORM_SPI_1"; // 驱动对外发布服务的名称,必须唯一,必须要按照HDF_PLATFORM_SPI_1的格式,X为SPI控制器编号。
+ deviceMatchAttr = "hisilicon_hi35xx_spi_1"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致。
+ }
+ ...... // 如果存在多个SPI设备时【必须】添加节点,否则不用
+ }
+ }
+ }
+ }
+ ```
+
+ - spi_config.hcs配置参考
+
+
+ ```c
+ root {
+ platform {
+ spi_config { // 每一个SPI控制器配置私有数据
+ template spi_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。
+ serviceName = "";
+ match_attr = "";
+ transferMode = 0; // 数据传输模式:中断传输(0)、流控传输(1)、DMA传输(2)
+ busNum = 0; // 总线号
+ clkRate = 100000000;
+ bitsPerWord = 8; // 传输位宽
+ mode = 19; // SPI 数据的输入输出模式
+ maxSpeedHz = 0; // 最大时钟频率
+ minSpeedHz = 0; // 最小时钟频率
+ speed = 2000000; // 当前消息传输速度
+ fifoSize = 256; // FIFO大小
+ numCs = 1; // 片选号
+ regBase = 0x120c0000; // 地址映射需要
+ irqNum = 100; // 中断号
+ REG_CRG_SPI = 0x120100e4; // CRG_REG_BASE(0x12010000) + 0x0e4
+ CRG_SPI_CKEN = 0;
+ CRG_SPI_RST = 0;
+ REG_MISC_CTRL_SPI = 0x12030024; // MISC_REG_BASE(0x12030000) + 0x24
+ MISC_CTRL_SPI_CS = 0;
+ MISC_CTRL_SPI_CS_SHIFT = 0;
+ }
+ controller_0x120c0000 :: spi_controller {
+ busNum = 0; // 【必要】总线号
+ CRG_SPI_CKEN = 0x10000; // (0x1 << 16) 0:close clk, 1:open clk
+ CRG_SPI_RST = 0x1; // (0x1 << 0) 0:cancel reset, 1:reset
+ match_attr = "hisilicon_hi35xx_spi_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
+ }
+ controller_0x120c1000 :: spi_controller {
+ busNum = 1;
+ CRG_SPI_CKEN = 0x20000; // (0x1 << 17) 0:close clk, 1:open clk
+ CRG_SPI_RST = 0x2; // (0x1 << 1) 0:cancel reset, 1:reset
+ match_attr = "hisilicon_hi35xx_spi_1";
+ regBase = 0x120c1000; // 【必要】地址映射需要
+ irqNum = 101; // 【必要】中断号
+ }
+ ...... // 如果存在多个SPI设备时【必须】添加节点,否则不用
+ }
+ }
+ }
+ ```
+
+ 需要注意的是,新增spi_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
+
+ 例如:本例中spi_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/spi/spi_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/spi/spi_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化SPI控制器对象
+
+ 完成属性文件配置之后,下一步就是以核心层SpiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化SpiCntlr成员SpiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+
+ - 自定义结构体参考
+
+ 从驱动的角度看,自定义结构体是参数和数据的载体,而且spi_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如设备号、总线号等。
- ```c
- static int32_t HdfSpiDeviceBind(struct HdfDeviceObject *device)
- {
- ...
- return (SpiCntlrCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS;
- }
-
- struct SpiCntlr *SpiCntlrCreate(struct HdfDeviceObject *device)
- {
- struct SpiCntlr *cntlr = NULL; // 创建核心层SpiCntlr对象
- ...
- cntlr = (struct SpiCntlr *)OsalMemCalloc(sizeof(*cntlr)); // 分配内存
- ...
- cntlr->device = device; // 使HdfDeviceObject与SpiCntlr可以相互转化的前提
- device->service = &(cntlr->service); // 使HdfDeviceObject与SpiCntlr可以相互转化的前提
- (void)OsalMutexInit(&cntlr->lock); // 锁初始化
- DListHeadInit(&cntlr->list); // 添加对应的节点
- cntlr->priv = NULL;
- return cntlr;
- }
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
-
- **表2** HDF_STATUS返回值描述
-
- | 状态(值) | 描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化SpiCntlr成员。
+ ```c
+ // 对应于spi_config.hcs中的参数
+ struct Pl022 {
+ struct SpiCntlr *cntlr;
+ struct DListHead deviceList;
+ struct OsalSem sem;
+ volatile unsigned char *phyBase;
+ volatile unsigned char *regBase;
+ uint32_t irqNum;
+ uint32_t busNum;
+ uint32_t numCs;
+ uint32_t curCs;
+ uint32_t speed;
+ uint32_t fifoSize;
+ uint32_t clkRate;
+ uint32_t maxSpeedHz;
+ uint32_t minSpeedHz;
+ uint32_t regCrg;
+ uint32_t clkEnBit;
+ uint32_t clkRstBit;
+ uint32_t regMiscCtrl;
+ uint32_t miscCtrlCsShift;
+ uint32_t miscCtrlCs;
+ uint16_t mode;
+ uint8_t bitsPerWord;
+ uint8_t transferMode;
+ };
+
+ // SpiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct SpiCntlr {
+ struct IDeviceIoService service;
+ struct HdfDeviceObject *device;
+ uint32_t busNum;
+ uint32_t numCs;
+ uint32_t curCs;
+ struct OsalMutex lock;
+ struct SpiCntlrMethod *method;
+ struct DListHead list;
+ void *priv;
+ };
+ ```
+
+ - SpiCntlr成员钩子函数结构体SpiCntlrMethod的实例化。
+
+ ```c
+ // spi_hi35xx.c中的示例:钩子函数的实例化
+ struct SpiCntlrMethod g_method = {
+ .Transfer = Pl022Transfer,
+ .SetCfg = Pl022SetCfg,
+ .GetCfg = Pl022GetCfg,
+ .Open = Pl022Open,
+ .Close = Pl022Close,
+ };
+ ```
+
+ - Bind函数参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态。
+
+ 函数说明:
+
+ 将SpiCntlr对象同HdfDeviceObject进行了关联。
+
+ ```c
+ static int32_t HdfSpiDeviceBind(struct HdfDeviceObject *device)
+ {
+ ......
+ return (SpiCntlrCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS;
+ }
+
+ struct SpiCntlr *SpiCntlrCreate(struct HdfDeviceObject *device)
+ {
+ struct SpiCntlr *cntlr = NULL; // 创建核心层SpiCntlr对象
+ ......
+ cntlr = (struct SpiCntlr *)OsalMemCalloc(sizeof(*cntlr)); // 分配内存
+ ......
+ cntlr->device = device; // 使HdfDeviceObject与SpiCntlr可以相互转化的前提
+ device->service = &(cntlr->service); // 使HdfDeviceObject与SpiCntlr可以相互转化的前提
+ (void)OsalMutexInit(&cntlr->lock); // 锁初始化
+ DListHeadInit(&cntlr->list); // 添加对应的节点
+ cntlr->priv = NULL;
+ return cntlr;
+ }
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化SpiCntlr成员。
+
+ ```c
+ static int32_t HdfSpiDeviceInit(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ struct SpiCntlr *cntlr = NULL;
+ ......
+ cntlr = SpiCntlrFromDevice(device); // 这里有HdfDeviceObject到SpiCntlr的强制转换,通过service成员,赋值见Bind函数。
+ // return (device == NULL) ? NULL : (struct SpiCntlr *)device->service;
+ ......
+ ret = Pl022Init(cntlr, device); // 【必要】实例化驱动适配者自定义操作对象,示例见下。
+ ......
+ ret = Pl022Probe(cntlr->priv);
+ ......
+ return ret;
+ }
+
+ static int32_t Pl022Init(struct SpiCntlr *cntlr, const struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ struct Pl022 *pl022 = NULL;
+ ......
+ pl022 = (struct Pl022 *)OsalMemCalloc(sizeof(*pl022)); // 申请内存
+ ......
+ ret = SpiGetBaseCfgFromHcs(pl022, device->property); // 初始化busNum、numCs、speed、fifoSize、clkRate、mode、bitsPerWord、transferMode参数值。
+ ......
+ ret = SpiGetRegCfgFromHcs(pl022, device->property); // 初始化regBase、phyBase、irqNum、regCrg、clkEnBit、clkRstBit、regMiscCtrl、regMiscCtrl、 miscCtrlCs、miscCtrlCsShift参数值。
+ ......
+ // 计算最大、最小速度对应的频率。
+ pl022->maxSpeedHz = (pl022->clkRate) / ((SCR_MIN + 1) * CPSDVSR_MIN);
+ pl022->minSpeedHz = (pl022->clkRate) / ((SCR_MAX + 1) * CPSDVSR_MAX);
+ DListHeadInit(&pl022->deviceList); // 初始化DList链表
+ pl022->cntlr = cntlr; // 使Pl022与SpiCntlr可以相互转化的前提
+ cntlr->priv = pl022; // 使Pl022与SpiCntlr可以相互转化的前提
+ cntlr->busNum = pl022->busNum; // 给SpiCntlr的busNum赋值
+ cntlr->method = &g_method; // SpiCntlrMethod的实例化对象的挂载
+ ......
+ ret = Pl022CreatAndInitDevice(pl022);
+ if (ret != 0) {
+ Pl022Release(pl022); // 初始化失败则释放Pl022对象
+ return ret;
+ }
+ return 0;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
+
+ ```c
+ static void HdfSpiDeviceRelease(struct HdfDeviceObject *device)
+ {
+ struct SpiCntlr *cntlr = NULL;
+ ......
+ cntlr = SpiCntlrFromDevice(device); // 这里有HdfDeviceObject到SpiCntlr的强制转换,通过service成员,赋值见Bind函数
+ // return (device==NULL) ?NULL:(struct SpiCntlr *)device->service;
+ ......
+ if (cntlr->priv != NULL) {
+ Pl022Remove((struct Pl022 *)cntlr->priv); // 这里有SpiCntlr到Pl022的强制转换
+ }
+ SpiCntlrDestroy(cntlr); // 释放Pl022对象
+ }
+ ```
-
- ```c
- static int32_t HdfSpiDeviceInit(struct HdfDeviceObject *device)
- {
- int32_t ret;
- struct SpiCntlr *cntlr = NULL;
- ...
- cntlr = SpiCntlrFromDevice(device); // 这里有HdfDeviceObject到SpiCntlr的强制转换,通过service成员,赋值见Bind函数。
- // return (device == NULL) ? NULL : (struct SpiCntlr *)device->service;
- ...
- ret = Pl022Init(cntlr, device); // 【必要】实例化驱动适配者自定义操作对象,示例见下。
- ...
- ret = Pl022Probe(cntlr->priv);
- ...
- return ret;
- }
-
- static int32_t Pl022Init(struct SpiCntlr *cntlr, const struct HdfDeviceObject *device)
- {
- int32_t ret;
- struct Pl022 *pl022 = NULL;
- ...
- pl022 = (struct Pl022 *)OsalMemCalloc(sizeof(*pl022)); // 申请内存
- ...
- ret = SpiGetBaseCfgFromHcs(pl022, device->property); // 初始化busNum、numCs、speed、fifoSize、clkRate、mode、bitsPerWord、transferMode参数值。
- ...
- ret = SpiGetRegCfgFromHcs(pl022, device->property); // 初始化regBase、phyBase、irqNum、regCrg、clkEnBit、clkRstBit、regMiscCtrl、regMiscCtrl、 miscCtrlCs、miscCtrlCsShift参数值。
- ...
- // 计算最大、最小速度对应的频率。
- pl022->maxSpeedHz = (pl022->clkRate) / ((SCR_MIN + 1) * CPSDVSR_MIN);
- pl022->minSpeedHz = (pl022->clkRate) / ((SCR_MAX + 1) * CPSDVSR_MAX);
- DListHeadInit(&pl022->deviceList); // 初始化DList链表
- pl022->cntlr = cntlr; // 使Pl022与SpiCntlr可以相互转化的前提
- cntlr->priv = pl022; // 使Pl022与SpiCntlr可以相互转化的前提
- cntlr->busNum = pl022->busNum; // 给SpiCntlr的busNum赋值
- cntlr->method = &g_method; // SpiCntlrMethod的实例化对象的挂载
- ...
- ret = Pl022CreatAndInitDevice(pl022);
- if (ret != 0) {
- Pl022Release(pl022); // 初始化失败则释放Pl022对象
- return ret;
- }
- return 0;
- }
- ```
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。
-
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
-
- ```c
- static void HdfSpiDeviceRelease(struct HdfDeviceObject *device)
- {
- struct SpiCntlr *cntlr = NULL;
- ...
- cntlr = SpiCntlrFromDevice(device); // 这里有HdfDeviceObject到SpiCntlr的强制转换,通过service成员,赋值见Bind函数
- // return (device==NULL) ?NULL:(struct SpiCntlr *)device->service;
- ...
- if (cntlr->priv != NULL) {
- Pl022Remove((struct Pl022 *)cntlr->priv); // 这里有SpiCntlr到Pl022的强制转换
- }
- SpiCntlrDestroy(cntlr); // 释放Pl022对象
- }
- ```
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,SPI获取设备属性、SPI设置设备属性、SPI传输等。
diff --git a/zh-cn/device-dev/driver/driver-platform-uart-des.md b/zh-cn/device-dev/driver/driver-platform-uart-des.md
index 500ab657eac4f5a9392d64771cc3eb01f9bed8b3..b35f2eadd1f8cd9dc6456db5444969c9a6392611 100644
--- a/zh-cn/device-dev/driver/driver-platform-uart-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-uart-des.md
@@ -8,37 +8,43 @@ UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter),
两个UART设备的连接示意图如下,UART与其他模块一般用2线(图1)或4线(图2)相连,它们分别是:
- - TX:发送数据端,和对端的RX相连。
- - RX:接收数据端,和对端的TX相连。
- - RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。
- - CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。
+- TX:发送数据端,和对端的RX相连。
-**图1** 2线UART设备连接示意图
+- RX:接收数据端,和对端的TX相连。
-![image1](figures/2线UART设备连接示意图.png "2线UART设备连接示意图")
+- RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。
-**图2** 4线UART设备连接示意图
+- CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。
-![image2](figures/4线UART设备连接示意图.png "4线UART设备连接示意图")
+**图 1** 2线UART设备连接示意图
+
+![2线UART设备连接示意图](figures/2线UART设备连接示意图.png)
+
+**图 2** 4线UART设备连接示意图
+
+![4线UART设备连接示意图](figures/4线UART设备连接示意图.png)
UART通信之前,收发双方需要约定好一些参数:波特率、数据格式(起始位、数据位、校验位、停止位)等。通信过程中,UART通过TX发送给对端数据,通过RX接收对端发送的数据。当UART接收缓存达到预定的门限值时,RTS变为不可发送数据,对端的CTS检测到不可发送数据,则停止发送数据。
UART接口定义了操作UART端口的通用方法集合,包括:
- 打开/关闭UART设备
+
- 读写数据
+
- 设置/获取UART设备波特率
+
- 设置/获取UART设备属性
### 基本概念
- 异步通信
- 异步通信中,数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符中的两个相邻位代码间的时间间隔是固定的。
+ 异步通信中,数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符中的两个相邻位代码间的时间间隔是固定的。
- 全双工传输(Full Duplex)
- 此通信模式允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工可以同时进行信号的双向传输。
+ 此通信模式允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工可以同时进行信号的双向传输。
### 运作机制
@@ -47,17 +53,24 @@ UART接口定义了操作UART端口的通用方法集合,包括:
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
UART模块各分层作用:
- 接口层提供打开UART设备、UART设备读取指定长度数据、UART设备写入指定长度数据、设置UART设备波特率、获取设UART设备波特率、设置UART设备属性、获取UART设备波特率、设置UART设备传输模式、关闭UART设备的接口。
-- 核心层主要提供看UART控制器的创建、移除以及管理的能力,通过钩子函数与适配层交互。
+
+- 核心层主要提供UART控制器的创建、移除以及管理的能力,通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
-**图3** UART独立服务模式结构图
+**图 3** UART独立服务模式结构图
+
+![UART独立服务模式结构图](figures/独立服务模式结构图.png)
+
+### 约束与限制
-![image3](figures/独立服务模式结构图.png "UART独立服务模式结构图")
+UART模块UartSetTransMode接口设置传输模式在Linux中不支持,仅为空实现。
## 使用指导
@@ -67,18 +80,18 @@ UART模块应用比较广泛,主要用于实现设备之间的低速串行通
### 接口说明
-**表1** UART驱动API接口功能介绍
+**表 1** 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 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 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设置传输模式 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
@@ -88,9 +101,9 @@ UART模块应用比较广泛,主要用于实现设备之间的低速串行通
使用UART的一般流程如下图所示。
-**图3** UART使用流程图
+**图 4** UART使用流程图
-![image3](figures/UART使用流程图.png "UART使用流程图")
+![UART使用流程图](figures/UART使用流程图.png)
#### 获取UART设备句柄
@@ -101,11 +114,11 @@ UART模块应用比较广泛,主要用于实现设备之间的低速串行通
DevHandle UartOpen(uint32_t port);
```
-**表2** UartOpen参数和返回值描述
+**表 2** UartOpen参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| port | UART设备号 |
+| port | uint32_t类型,UART设备号 |
| **返回值** | **返回值描述** |
| NULL | 获取UART设备句柄失败 |
| 设备句柄 | UART设备句柄 |
@@ -131,12 +144,12 @@ if (handle == NULL) {
int32_t UartSetBaud(DevHandle handle, uint32_t baudRate);
```
-**表3** UartSetBaud参数和返回值描述
+**表 3** UartSetBaud参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | UART设备句柄 |
-| baudRate | 待设置的波特率值 |
+| handle | DevHandle类型,UART设备句柄 |
+| baudRate | uint32_t类型,待设置的波特率值 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | UART设置波特率成功 |
| 负数 | UART设置波特率失败 |
@@ -161,12 +174,12 @@ if (ret != HDF_SUCCESS) {
int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate);
```
-**表4** UartGetBaud参数和返回值描述
+**表 4** UartGetBaud参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | UART设备句柄 |
-| baudRate | 接收波特率值的指针 |
+| handle | DevHandle类型,UART设备句柄 |
+| baudRate | uint32_t类型指针,用于接收波特率的值 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | UART获取波特率成功 |
| 负数 | UART获取波特率失败 |
@@ -192,12 +205,12 @@ if (ret != HDF_SUCCESS) {
int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute);
```
-**表5** UartSetAttribute参数和返回值描述
+**表 5** UartSetAttribute参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | UART设备句柄 |
-| attribute | 待设置的设备属性 |
+| handle | DevHandle类型,UART设备句柄 |
+| attribute | 结构体指针,待设置的设备属性 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | UART设置设备属性成功 |
| 负数 | UART设置设备属性失败 |
@@ -231,12 +244,12 @@ turn ret;
int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute);
```
-**表6** UartGetAttribute参数和返回值描述
+**表 6** UartGetAttribute参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | UART设备句柄 |
-| attribute | 接收UART设备属性的指针 |
+| handle | DevHandle类型,UART设备句柄 |
+| attribute | 结构体指针,接收UART设备属性的指针 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | UART获取设备属性成功 |
| 负数 | UART获取设备属性失败 |
@@ -262,12 +275,12 @@ if (ret != HDF_SUCCESS) {
int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode);
```
-**表7** UartSetTransMode参数和返回值描述
+**表 7** UartSetTransMode参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | UART设备句柄 |
-| mode | 待设置的传输模式, |
+| handle | DevHandle类型,UART设备句柄 |
+| mode | 枚举类型,待设置的传输模式 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | UART设置传输模式成功 |
| 负数 | UART设置传输模式失败 |
@@ -292,13 +305,13 @@ if (ret != HDF_SUCCESS) {
int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size);
```
-**表8** UartWrite参数和返回值描述
+**表 8** UartWrite参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | UART设备句柄 |
-| data | 待写入数据的指针 |
-| size | 待写入数据的长度 |
+| handle | DevHandle类型,UART设备句柄 |
+| data | uint8_t类型指针,待写入数据的 |
+| size | uint32_t类型,待写入数据的长度 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | UART写数据成功 |
| 负数 | UART写数据失败 |
@@ -324,13 +337,13 @@ if (ret != HDF_SUCCESS) {
int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size);
```
-**表9** UartRead参数和返回值描述
+**表 9** UartRead参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
-| handle | UART设备句柄 |
-| data | 接收读取数据的指针 |
-| size | 待读取数据的长度 |
+| handle | DevHandle类型,UART设备句柄 |
+| data | uint8_t类型指针,接收读取数据 |
+| size | uint32_t类型,待读取数据的长度 |
| **返回值** | **返回值描述** |
| 非负数 | UART读取到的数据长度 |
| 负数 | UART读取数据失败 |
@@ -362,7 +375,7 @@ void UartClose(DevHandle handle);
该函数会释放申请的资源。
-**表10** UartClose参数和返回值描述
+**表 10** UartClose参数和返回值描述
| 参数 | 参数描述 |
| -------- | -------- |
@@ -379,20 +392,28 @@ UartClose(handle); // 销毁UART设备句柄
下面将基于Hi3516DV300开发板展示使用UART完整操作,步骤主要如下:
1. 传入UART端口号num,打开端口号对应的UART设备并获得UART设备句柄。
+
2. 通过UART设备句柄及设置的波特率,设置UART设备的波特率。
+
3. 通过UART设备句柄及待获取的波特率,获取UART设备的波特率。
+
4. 通过UART设备句柄及待设置的设备属性,设置UART设备的设备属性。
+
5. 通过UART设备句柄及待获取的设备属性,获取UART设备的设备属性。
+
6. 通过UART设备句柄及待设置的传输模式,设置UART设备的传输模式。
+
7. 通过UART设备句柄及待传输的数据及大小,传输指定长度的数据。
+
8. 通过UART设备句柄及待接收的数据及大小,接收指定长度的数据。
+
9. 通过UART设备句柄,关闭UART设备。
```c
#include "hdf_log.h"
#include "uart_if.h"
-void UartTestSample(void)
+static int32_t UartTestSample(void)
{
int32_t ret;
uint32_t port;
@@ -415,7 +436,7 @@ void UartTestSample(void)
handle = UartOpen(port); // 获取UART设备句柄
if (handle == NULL) {
HDF_LOGE("UartOpen: open uart_%u failed!\n", port);
- return;
+ return HDF_FAILURE;
}
ret = UartSetBaud(handle, 9600); // 设置UART波特率为9600
@@ -459,8 +480,9 @@ void UartTestSample(void)
HDF_LOGE("UartRead: read data failed, ret %d\n", ret);
goto ERR;
}
+ HDF_LOGI("%s: function tests end, %d", __func__, ret);
ERR:
UartClose(handle); // 销毁UART设备句柄
- return ret;
+ return ret;
}
```
diff --git a/zh-cn/device-dev/driver/driver-platform-uart-develop.md b/zh-cn/device-dev/driver/driver-platform-uart-develop.md
index cff8e0e2b1c72bd1b9ad3e2ed2750e8a83ed89d7..f9a94e1430ed3800572accc26e0fd78b1ab2107e 100755
--- a/zh-cn/device-dev/driver/driver-platform-uart-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-uart-develop.md
@@ -8,18 +8,21 @@ UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter),
两个UART设备的连接示意图如下,UART与其他模块一般用2线(图1)或4线(图2)相连,它们分别是:
- - TX:发送数据端,和对端的RX相连。
- - RX:接收数据端,和对端的TX相连。
- - RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。
- - CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。
+- TX:发送数据端,和对端的RX相连。
-**图1** 2线UART设备连接示意图
+- RX:接收数据端,和对端的TX相连。
-![image1](figures/2线UART设备连接示意图.png "2线UART设备连接示意图")
+- RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。
-**图2** 4线UART设备连接示意图
+- CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。
-![image2](figures/4线UART设备连接示意图.png "4线UART设备连接示意图")
+**图 1** 2线UART设备连接示意图
+
+![2线UART设备连接示意图](figures/2线UART设备连接示意图.png)
+
+**图 2** 4线UART设备连接示意图
+
+![4线UART设备连接示意图](figures/4线UART设备连接示意图.png)
UART通信之前,收发双方需要约定好一些参数:波特率、数据格式(起始位、数据位、校验位、停止位)等。通信过程中,UART通过TX发送给对端数据,通过RX接收对端发送的数据。当UART接收缓存达到预定的门限值时,RTS变为不可发送数据,对端的CTS检测到不可发送数据,则停止发送数据。
@@ -27,11 +30,11 @@ UART通信之前,收发双方需要约定好一些参数:波特率、数据
- 异步通信
- 异步通信中,数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符中的两个相邻位代码间的时间间隔是固定的。
+ 异步通信中,数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符中的两个相邻位代码间的时间间隔是固定的。
- 全双工传输(Full Duplex)
- 此通信模式允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工可以同时进行信号的双向传输。
+ 此通信模式允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工可以同时进行信号的双向传输。
### 运作机制
@@ -40,17 +43,20 @@ UART通信之前,收发双方需要约定好一些参数:波特率、数据
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
UART模块各分层作用:
- 接口层提供打开UART设备、UART设备读取指定长度数据、UART设备写入指定长度数据、设置UART设备波特率、获取设UART设备波特率、设置UART设备属性、获取UART设备波特率、设置UART设备传输模式、关闭UART设备的接口。
-- 核心层主要提供看UART控制器的创建、移除以及管理的能力,通过钩子函数与适配层交互。
+
+- 核心层主要提供UART控制器的创建、移除以及管理的能力,通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
-**图3** UART独立服务模式结构图
+**图 3** UART独立服务模式结构图
-![image3](figures/独立服务模式结构图.png "UART独立服务模式结构图")
+![UART独立服务模式结构图](figures/独立服务模式结构图.png)
## 开发指导
@@ -79,7 +85,7 @@ struct UartHostMethod {
};
```
-**表1** UartHostMethod结构体成员的回调函数功能说明
+**表 1** UartHostMethod结构体成员的回调函数功能说明
| 函数 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
@@ -92,358 +98,375 @@ struct UartHostMethod {
| 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轮询机制 |
+| PollEvent | host:结构体指针,核心层UART控制器
filep:void类型指针filep
table:void类型指针table | 无 | HDF_STATUS相关状态 | poll轮询机制 |
### 开发步骤
UART模块适配HDF框架包含以下四个步骤:
-- 实例化驱动入口。
-- 配置属性文件。
-- 实例化UART控制器对象。
-- 驱动调试。
+- 实例化驱动入口
+
+- 配置属性文件
+
+- 实例化UART控制器对象
+
+- 驱动调试
### 开发实例
下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/uart/uart_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 实例化驱动入口。
-
- 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
- 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
- UART驱动入口开发参考:
-
- ```c
- struct HdfDriverEntry g_hdfUartDevice = {
- .moduleVersion = 1,
- .moduleName = "HDF_PLATFORM_UART", // 【必要且与HCS文件中里面的moduleName匹配】
- .Bind = HdfUartDeviceBind, // 见Bind参考
- .Init = HdfUartDeviceInit, // 见Init参考
- .Release = HdfUartDeviceRelease, // 见Release参考
- };
- HDF_INIT(g_hdfUartDevice); //调用HDF_INIT将驱动入口注册到HDF框架中
- ```
-
-2. 配置属性文件。
-
- 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个UART控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层UartHost成员的默认值或限制范围有密切关系,比如UART设备端口号,需要在uart_config.hcs文件中增加对应的器件属性。
-
- - device_info.hcs 配置参考:
-
- 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
-
- ```c
- root {
- device_info {
- match_attr = "hdf_manager";
- platform :: host {
- hostName = "platform_host";
- priority = 50;
- 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 = "hisilicon_hi35xx_uart_0"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致。
- }
- device1 :: deviceNode {
- policy = 2;
- permission = 0644;
- priority = 40;
- moduleName = "HDF_PLATFORM_UART";
- serviceName = "HDF_PLATFORM_UART_1";
- deviceMatchAttr = "hisilicon_hi35xx_uart_1";
- }
- ...
- }
- }
- }
- }
- ```
-
- - uart_config.hcs 配置参考:
-
- 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs文件配置器件属性,其中配置参数如下:
-
- ```c
- root {
- platform {
- template uart_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值
- match_attr = "";
- num = 0; // 【必要】端口号
- baudrate = 115200; // 【必要】波特率,数值可按需填写
- fifoRxEn = 1; // 【必要】使能接收FIFO
- fifoTxEn = 1; // 【必要】使能发送FIFO
- flags = 4; // 【必要】标志信号
- regPbase = 0x120a0000; // 【必要】地址映射需要
- interrupt = 38; // 【必要】中断号
- iomemCount = 0x48; // 【必要】地址映射需要
- }
- controller_0x120a0000 :: uart_controller {
- match_attr = "hisilicon_hi35xx_uart_0"; // 【必要】必须和device_info.hcs中对应的设备的deviceMatchAttr值一致
- }
- controller_0x120a1000 :: uart_controller {
- num = 1;
- baudrate = 9600;
- regPbase = 0x120a1000;
- interrupt = 39;
- match_attr = "hisilicon_hi35xx_uart_1";
- }
- ... // 如果存在多个UART设备时【必须】添加节点,否则不用
- }
- }
- ```
-
- 需要注意的是,新增uart_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs" // 配置文件相对路径
- ```
-
-3. 实例化UART控制器对象。
-
- 完成属性文件配置之后,下一步就是以核心层UartHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化UartHost成员UartHostMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
-
- - 驱动适配者自定义结构体参考
-
- 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且uart_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如端口号。
-
- ```c
- struct UartPl011Port { // 驱动适配者自定义管脚描述结构体
- int32_t enable;
- unsigned long physBase; // 物理地址
- uint32_t irqNum; // 中断号
- uint32_t defaultBaudrate; // 默认波特率
- uint32_t flags; // 标志信号,下面三个宏与之相关
- #define PL011_FLG_IRQ_REQUESTED (1 << 0)
- #define PL011_FLG_DMA_RX_REQUESTED (1 << 1)
- #define PL011_FLG_DMA_TX_REQUESTED (1 << 2)
- struct UartDmaTransfer *rxUdt; // DMA传输相关
- struct UartDriverData *udd;
- };
- struct UartDriverData { // 数据传输相关的结构体
- uint32_t num; // 端口号
- uint32_t baudrate; // 波特率(可设置)
- struct UartAttribute attr; // 数据位、停止位等传输属性相关
- struct UartTransfer *rxTransfer; // 缓冲区相关,可理解为FIFO结构
- wait_queue_head_t wait; // 条件变量相关的排队等待信号
- int32_t count; // 数据数量
- int32_t state; // UART控制器状态
- #define UART_STATE_NOT_OPENED 0
- #define UART_STATE_OPENING 1
- #define UART_STATE_USEABLE 2
- #define UART_STATE_SUSPENDED 3
- uint32_t flags; // 状态标志
- #define UART_FLG_DMA_RX (1 << 0)
- #define UART_FLG_DMA_TX (1 << 1)
- #define UART_FLG_RD_BLOCK (1 << 2)
- RecvNotify recv; // 函数指针类型,指向串口数据接收函数
- struct UartOps *ops; // 自定义函数指针结构体
- void *private; // 私有数据
- };
-
- // UartHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。
- struct UartHost {
- struct IDeviceIoService service; // 驱动服务
- struct HdfDeviceObject *device; // 驱动设备对象
- uint32_t num; // 端口号
- OsalAtomic atom; // 原子量
- void *priv; // 私有数据
- struct UartHostMethod *method; // 回调函数
- };
- ```
-
- - UartHost成员回调函数结构体UartHostMethod的实例化,其中的成员在Init函数中初始化。
-
- ```c
- // uart_hi35xx.c 中的示例:钩子函数的实例化
- struct UartHostMethod g_uartHostMethod = {
- .Init = Hi35xxInit, // 初始化
- .Deinit = Hi35xxDeinit, // 去初始化
- .Read = Hi35xxRead, // 接收数据
- .Write = Hi35xxWrite, // 发送数据
- .SetBaud = Hi35xxSetBaud, // 设置波特率
- .GetBaud = Hi35xxGetBaud, // 获取波特率
- .SetAttribute = Hi35xxSetAttribute, // 设置设备属性
- .GetAttribute = Hi35xxGetAttribute, // 获取设备属性
- .SetTransMode = Hi35xxSetTransMode, // 设置传输模式
- .pollEvent = Hi35xxPollEvent, // 轮询
- };
- ```
-
- - Bind函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可参见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS中HDF_STATUS定义)。
-
- **表2** HDF_STATUS返回值说明
-
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_INVALID_PARAM | 参数非法 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化UartHost成员。
-
- ```c
- //uart_hi35xx.c
- static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device)
- {
- ...
- return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; // 【必须】调用核心层函数UartHostCreate
- }
-
- // uart_core.c核心层UartHostCreate函数说明
- struct UartHost *UartHostCreate(struct HdfDeviceObject *device)
- {
- struct UartHost *host = NULL; // 新建UartHost
- ...
- host = (struct UartHost *)OsalMemCalloc(sizeof(*host)); // 分配内存
- ...
- host->device = device; // 【必要】使HdfDeviceObject与UartHost可以相互转化的前提
- device->service = &(host->service); // 【必要】使HdfDeviceObject与UartHost可以相互转化的前提
- host->device->service->Dispatch = UartIoDispatch; // 为service成员的Dispatch方法赋值
- OsalAtomicSet(&host->atom, 0); // 原子量初始化或者原子量设置
- host->priv = NULL;
- host->method = NULL;
- return host;
- }
- ```
-
- - Init函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- HDF_STATUS相关状态。
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化UartHost成员,调用核心层UartAddDev函数,完成UART控制器的添加,接入VFS。
+1. 实例化驱动入口
+
+ 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+
+ 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
+
+ UART驱动入口开发参考:
+
+ ```c
+ struct HdfDriverEntry g_hdfUartDevice = {
+ .moduleVersion = 1,
+ .moduleName = "HDF_PLATFORM_UART", // 【必要且与HCS文件中里面的moduleName匹配】
+ .Bind = HdfUartDeviceBind, // 挂接UART模块Bind实例化
+ .Init = HdfUartDeviceInit, // 挂接UART模块Init实例化
+ .Release = HdfUartDeviceRelease, // 挂接UART模块Release实例化
+ };
+ HDF_INIT(g_hdfUartDevice); // 调用HDF_INIT将驱动入口注册到HDF框架中
+ ```
+
+2. 配置属性文件
+
+ 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个UART控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在uart_config.hcs文件中增加对应的器件属性。器件属性值与核心层UartHost成员的默认值或限制范围有密切关系,比如UART设备端口号,需要在uart_config.hcs文件中增加对应的器件属性。
+
+ 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,UART控制器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。UART控制器具体配置为40 |
+ | permission | 驱动创建设备节点权限,UART控制器具体配置为0664 |
+ | moduleName | 驱动名称,UART控制器固定为HDF_PLATFORM_UART |
+ | serviceName | 驱动对外发布服务的名称,UART控制器服务名设置为HDF_PLATFORM_UART_X,X代表UART控制器编号 |
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,UART控制器设置为hisilicon_hi35xx_uart_X ,X代表UART控制器编号 |
+
+ - device_info.hcs 配置参考:
+
+ 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
+
+ ```c
+ root {
+ device_info {
+ match_attr = "hdf_manager";
+ platform :: host {
+ hostName = "platform_host";
+ priority = 50;
+ 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 = "hisilicon_hi35xx_uart_0"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致。
+ }
+ device1 :: deviceNode {
+ policy = 2;
+ permission = 0644;
+ priority = 40;
+ moduleName = "HDF_PLATFORM_UART";
+ serviceName = "HDF_PLATFORM_UART_1";
+ deviceMatchAttr = "hisilicon_hi35xx_uart_1";
+ }
+ ...... // 如果存在多个UART设备时【必须】添加节点,否则不用
+ }
+ }
+ }
+ }
+ ```
+
+ - uart_config.hcs 配置参考:
+
+ 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs文件配置器件属性,其中配置参数如下:
+
+ ```c
+ root {
+ platform {
+ template uart_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值
+ match_attr = "";
+ num = 0; // 【必要】端口号
+ baudrate = 115200; // 【必要】波特率,数值可按需填写
+ fifoRxEn = 1; // 【必要】使能接收FIFO
+ fifoTxEn = 1; // 【必要】使能发送FIFO
+ flags = 4; // 【必要】标志信号
+ regPbase = 0x120a0000; // 【必要】地址映射需要
+ interrupt = 38; // 【必要】中断号
+ iomemCount = 0x48; // 【必要】地址映射需要
+ }
+ controller_0x120a0000 :: uart_controller {
+ match_attr = "hisilicon_hi35xx_uart_0"; // 【必要】必须和device_info.hcs中对应的设备的deviceMatchAttr值一致
+ }
+ controller_0x120a1000 :: uart_controller {
+ num = 1;
+ baudrate = 9600;
+ regPbase = 0x120a1000;
+ interrupt = 39;
+ match_attr = "hisilicon_hi35xx_uart_1";
+ }
+ ...... // 如果存在多个UART设备时【必须】添加节点,否则不用
+ }
+ }
+ ```
+
+ 需要注意的是,新增uart_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化UART控制器对象
+
+ 完成属性文件配置之后,下一步就是以核心层UartHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化UartHost成员UartHostMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
+
+ - 驱动适配者自定义结构体参考
+
+ 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且uart_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如端口号。
+
+ ```c
+ struct UartPl011Port { // 驱动适配者自定义管脚描述结构体
+ int32_t enable;
+ unsigned long physBase; // 物理地址
+ uint32_t irqNum; // 中断号
+ uint32_t defaultBaudrate; // 默认波特率
+ uint32_t flags; // 标志信号,下面三个宏与之相关
+ #define PL011_FLG_IRQ_REQUESTED (1 << 0)
+ #define PL011_FLG_DMA_RX_REQUESTED (1 << 1)
+ #define PL011_FLG_DMA_TX_REQUESTED (1 << 2)
+ struct UartDmaTransfer *rxUdt; // DMA传输相关
+ struct UartDriverData *udd;
+ };
+ struct UartDriverData { // 数据传输相关的结构体
+ uint32_t num; // 端口号
+ uint32_t baudrate; // 波特率(可设置)
+ struct UartAttribute attr; // 数据位、停止位等传输属性相关
+ struct UartTransfer *rxTransfer; // 缓冲区相关,可理解为FIFO结构
+ wait_queue_head_t wait; // 条件变量相关的排队等待信号
+ int32_t count; // 数据数量
+ int32_t state; // UART控制器状态
+ #define UART_STATE_NOT_OPENED 0
+ #define UART_STATE_OPENING 1
+ #define UART_STATE_USEABLE 2
+ #define UART_STATE_SUSPENDED 3
+ uint32_t flags; // 状态标志
+ #define UART_FLG_DMA_RX (1 << 0)
+ #define UART_FLG_DMA_TX (1 << 1)
+ #define UART_FLG_RD_BLOCK (1 << 2)
+ RecvNotify recv; // 函数指针类型,指向串口数据接收函数
+ struct UartOps *ops; // 自定义函数指针结构体
+ void *private; // 私有数据
+ };
+
+ // UartHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct UartHost {
+ struct IDeviceIoService service; // 驱动服务
+ struct HdfDeviceObject *device; // 驱动设备对象
+ uint32_t num; // 端口号
+ OsalAtomic atom; // 原子量
+ void *priv; // 私有数据
+ struct UartHostMethod *method; // 回调函数
+ };
+ ```
+
+ - UartHost成员回调函数结构体UartHostMethod的实例化。
+
+ ```c
+ // uart_hi35xx.c 中的示例:钩子函数的实例化
+ struct UartHostMethod g_uartHostMethod = {
+ .Init = Hi35xxInit, // 初始化
+ .Deinit = Hi35xxDeinit, // 去初始化
+ .Read = Hi35xxRead, // 接收数据
+ .Write = Hi35xxWrite, // 发送数据
+ .SetBaud = Hi35xxSetBaud, // 设置波特率
+ .GetBaud = Hi35xxGetBaud, // 获取波特率
+ .SetAttribute = Hi35xxSetAttribute, // 设置设备属性
+ .GetAttribute = Hi35xxGetAttribute, // 获取设备属性
+ .SetTransMode = Hi35xxSetTransMode, // 设置传输模式
+ .pollEvent = Hi35xxPollEvent, // 轮询
+ };
+ ```
+
+ - Bind函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS中HDF_STATUS定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_INVALID_PARAM | 参数非法 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化UartHost成员。
+
+ ```c
+ //uart_hi35xx.c
+ static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device)
+ {
+ ......
+ return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; // 【必须】调用核心层函数UartHostCreate
+ }
+
+ // uart_core.c核心层UartHostCreate函数说明
+ struct UartHost *UartHostCreate(struct HdfDeviceObject *device)
+ {
+ struct UartHost *host = NULL; // 新建UartHost
+ ......
+ host = (struct UartHost *)OsalMemCalloc(sizeof(*host)); // 分配内存
+ ......
+ host->device = device; // 【必要】使HdfDeviceObject与UartHost可以相互转化的前提
+ device->service = &(host->service); // 【必要】使HdfDeviceObject与UartHost可以相互转化的前提
+ host->device->service->Dispatch = UartIoDispatch; // 为service成员的Dispatch方法赋值
+ OsalAtomicSet(&host->atom, 0); // 原子量初始化或者原子量设置
+ host->priv = NULL;
+ host->method = NULL;
+ return host;
+ }
+ ```
+
+ - Init函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ HDF_STATUS相关状态。
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化UartHost成员,调用核心层UartAddDev函数,完成UART控制器的添加,接入VFS。
- ```c
- int32_t HdfUartDeviceInit(struct HdfDeviceObject *device)
- {
- int32_t ret;
- struct UartHost *host = NULL;
- HDF_LOGI("%s: entry", __func__);
- ...
- host = UartHostFromDevice(device); // 通过service成员后强制转为UartHost,赋值是在Bind函数中
- ...
- ret = Hi35xxAttach(host, device); // 完成UartHost对象的初始化,见下
- ...
- host->method = &g_uartHostMethod; // UartHostMethod的实例化对象的挂载
- return ret;
- }
- // 完成UartHost对象的初始化。
- static int32_t Hi35xxAttach(struct UartHost *host, struct HdfDeviceObject *device)
- {
- int32_t ret;
- struct UartDriverData *udd = NULL; // udd和port对象是驱动适配者自定义的结构体对象,可根据需要实现相关功能
- struct UartPl011Port *port = NULL;
- ...
- // 【必要】步骤【1】~【7】主要实现对udd对象的实例化赋值,然后赋值给核心层UartHost对象。
- udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd)); // 【1】
- ...
- port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port)); // 【2】
- ...
- udd->ops = Pl011GetOps(); // 【3】设备开启、关闭、属性设置、发送操作等函数挂载。
- udd->recv = PL011UartRecvNotify; // 【4】数据接收通知函数(条件锁机制)挂载
- udd->count = 0; // 【5】
- port->udd = udd; // 【6】使UartPl011Port与UartDriverData可以相互转化的前提
- ret = UartGetConfigFromHcs(port, device->property); // 将HdfDeviceObject的属性传递给驱动适配者自定义结构体,用于相关操作,示例代码见下
- ...
- udd->private = port; // 【7】
- host->priv = udd; // 【必要】使UartHost与UartDriverData可以相互转化的前提
- host->num = udd->num; // 【必要】UART设备号
- UartAddDev(host); // 【必要】核心层uart_dev.c中的函数,作用:注册一个字符设备节点到vfs,这样从用户态可以通过这个虚拟文件节点访问UART
- return HDF_SUCCESS;
- }
-
- static int32_t UartGetConfigFromHcs(struct UartPl011Port *port, const struct DeviceResourceNode *node)
- {
- uint32_t tmp, regPbase, iomemCount;
- struct UartDriverData *udd = port->udd;
- struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
- ...
- // 通过请求参数提取相应的值,并赋值给驱动适配者自定义的结构体。
- if (iface->GetUint32(node, "num", &udd->num, 0) != HDF_SUCCESS) {
- HDF_LOGE("%s: read busNum fail", __func__);
- return HDF_FAILURE;
- }
- ...
- return 0;
- }
- ```
-
- - Release函数开发参考
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。
-
- > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
-
- ```c
- void HdfUartDeviceRelease(struct HdfDeviceObject *device)
- {
- struct UartHost *host = NULL;
- ...
- host = UartHostFromDevice(device); // 这里有HdfDeviceObject到UartHost的强制转化,通过service成员,赋值见Bind函数。
- ...
- if (host->priv != NULL) {
- Hi35xxDetach(host); // 驱动适配自定义的内存释放函数,见下。
- }
- UartHostDestroy(host); // 调用核心层函数释放host
- }
-
- static void Hi35xxDetach(struct UartHost *host)
- {
- struct UartDriverData *udd = NULL;
- struct UartPl011Port *port = NULL;
- ...
- udd = host->priv; // 这里有UartHost到UartDriverData的转化
- ...
- UartRemoveDev(host); // VFS注销
- port = udd->private; // 这里有UartDriverData到UartPl011Port的转化
- if (port != NULL) {
- if (port->physBase != 0) {
- OsalIoUnmap((void *)port->physBase); // 地址反映射
- }
- OsalMemFree(port);
- udd->private = NULL;
- }
- OsalMemFree(udd); // 释放UartDriverData
- host->priv = NULL;
- }
- ```
-
-4. 驱动调试。
-
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
\ No newline at end of file
+ ```c
+ int32_t HdfUartDeviceInit(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ struct UartHost *host = NULL;
+ HDF_LOGI("%s: entry", __func__);
+ ......
+ host = UartHostFromDevice(device); // 通过service成员后强制转为UartHost,赋值是在Bind函数中
+ ......
+ ret = Hi35xxAttach(host, device); // 完成UartHost对象的初始化,见下
+ ......
+ host->method = &g_uartHostMethod; // UartHostMethod的实例化对象的挂载
+ return ret;
+ }
+ // 完成UartHost对象的初始化。
+ static int32_t Hi35xxAttach(struct UartHost *host, struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ struct UartDriverData *udd = NULL; // udd和port对象是驱动适配者自定义的结构体对象,可根据需要实现相关功能
+ struct UartPl011Port *port = NULL;
+ ......
+ // 【必要】步骤【1】~【7】主要实现对udd对象的实例化赋值,然后赋值给核心层UartHost对象。
+ udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd)); // 【1】
+ ......
+ port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port)); // 【2】
+ ......
+ udd->ops = Pl011GetOps(); // 【3】设备开启、关闭、属性设置、发送操作等函数挂载。
+ udd->recv = PL011UartRecvNotify; // 【4】数据接收通知函数(条件锁机制)挂载
+ udd->count = 0; // 【5】
+ port->udd = udd; // 【6】使UartPl011Port与UartDriverData可以相互转化的前提
+ ret = UartGetConfigFromHcs(port, device->property); // 将HdfDeviceObject的属性传递给驱动适配者自定义结构体,用于相关操作,示例代码见下
+ ......
+ udd->private = port; // 【7】
+ host->priv = udd; // 【必要】使UartHost与UartDriverData可以相互转化的前提
+ host->num = udd->num; // 【必要】UART设备号
+ UartAddDev(host); // 【必要】核心层uart_dev.c中的函数,作用:注册一个字符设备节点到vfs,这样从用户态可以通过这个虚拟文件节点访问UART
+ return HDF_SUCCESS;
+ }
+
+ static int32_t UartGetConfigFromHcs(struct UartPl011Port *port, const struct DeviceResourceNode *node)
+ {
+ uint32_t tmp, regPbase, iomemCount;
+ struct UartDriverData *udd = port->udd;
+ struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
+ ......
+ // 通过请求参数提取相应的值,并赋值给驱动适配者自定义的结构体。
+ if (iface->GetUint32(node, "num", &udd->num, 0) != HDF_SUCCESS) {
+ HDF_LOGE("%s: read busNum fail", __func__);
+ return HDF_FAILURE;
+ }
+ ......
+ return 0;
+ }
+ ```
+
+ - Release函数开发参考
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。
+
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
+
+ ```c
+ void HdfUartDeviceRelease(struct HdfDeviceObject *device)
+ {
+ struct UartHost *host = NULL;
+ ...
+ host = UartHostFromDevice(device); // 这里有HdfDeviceObject到UartHost的强制转化,通过service成员,赋值见Bind函数。
+ ...
+ if (host->priv != NULL) {
+ Hi35xxDetach(host); // 驱动适配自定义的内存释放函数,见下。
+ }
+ UartHostDestroy(host); // 调用核心层函数释放host
+ }
+
+ static void Hi35xxDetach(struct UartHost *host)
+ {
+ struct UartDriverData *udd = NULL;
+ struct UartPl011Port *port = NULL;
+ ...
+ udd = host->priv; // 这里有UartHost到UartDriverData的转化
+ ...
+ UartRemoveDev(host); // VFS注销
+ port = udd->private; // 这里有UartDriverData到UartPl011Port的转化
+ if (port != NULL) {
+ if (port->physBase != 0) {
+ OsalIoUnmap((void *)port->physBase); // 地址反映射
+ }
+ OsalMemFree(port);
+ udd->private = NULL;
+ }
+ OsalMemFree(udd); // 释放UartDriverData
+ host->priv = NULL;
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
diff --git a/zh-cn/device-dev/driver/driver-platform-watchdog-des.md b/zh-cn/device-dev/driver/driver-platform-watchdog-des.md
index 09a16a81fb094c6d65f7a4f1dbae1104f879480b..3a11cb12663bde5ccac8a5657be581c36cfa2b35 100644
--- a/zh-cn/device-dev/driver/driver-platform-watchdog-des.md
+++ b/zh-cn/device-dev/driver/driver-platform-watchdog-des.md
@@ -9,9 +9,13 @@
Watchdog接口定义了看门狗操作的通用方法集合,包括:
- 打开/关闭看门狗设备
+
- 启动/停止看门狗设备
+
- 设置/获取看门狗设备超时时间
+
- 获取看门狗设备状态
+
- 喂狗
### 基本概念
@@ -25,17 +29,20 @@ Watchdog接口定义了看门狗操作的通用方法集合,包括:
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
Watchdog模块各分层作用:
- 接口层提供打开看门狗设备、获取看门狗设备状态、启动看门狗设备、设置看门狗设备超时时间、获取看门狗设备超时时间、喂狗、停止看门狗设备超时时间、关闭看门狗设备的接口。
+
- 核心层主要提供看门狗控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图 1** Watchdog独立服务模式结构图
-![image1](figures/独立服务模式结构图.png "Watchdog独立服务模式结构图")
+![Watchdog独立服务模式结构图](figures/独立服务模式结构图.png)
## 使用指导
@@ -45,19 +52,19 @@ Watchdog模块各分层作用:
### 接口说明
-Watchdog模块提供的主要接口如表1所示。
+Watchdog模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/watchdog_if.h。
-**表1** 看门狗API接口功能介绍
+**表 1** 看门狗API接口功能介绍
| 接口名 | 描述 |
| -------- | -------- |
-| int32_t WatchdogOpen(int16_t wdtId, DevHandle *handle) | 打开看门狗 |
+| int32_t WatchdogOpen(int16_t wdtId, DevHandle \*handle) | 打开看门狗 |
| void WatchdogClose(DevHandle handle) | 关闭看门狗 |
| int32_t WatchdogStart(DevHandle handle) | 启动看门狗 |
| int32_t WatchdogStop(DevHandle handle) | 停止看门狗 |
| int32_t WatchdogSetTimeout(DevHandle handle, uint32_t seconds) | 设置看门狗超时时间 |
-| int32_t WatchdogGetTimeout(DevHandle handle, uint32_t *seconds) | 获取看门狗超时时间 |
-| int32_t WatchdogGetStatus(DevHandle handle, int32_t *status) | 获取看门狗状态 |
+| int32_t WatchdogGetTimeout(DevHandle handle, uint32_t \*seconds) | 获取看门狗超时时间 |
+| int32_t WatchdogGetStatus(DevHandle handle, int32_t \*status) | 获取看门狗状态 |
| int32_t WatchdogFeed(DevHandle handle) | 清除看门狗定时器(喂狗) |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
@@ -67,9 +74,9 @@ Watchdog模块提供的主要接口如表1所示。
使用看门狗的一般流程如下图所示。
-**图2** 看门狗使用流程图
+**图 2** 看门狗使用流程图
-![image2](figures/看门狗使用流程图.png "看门狗使用流程图")
+![看门狗使用流程图](figures/看门狗使用流程图.png)
#### 打开看门狗设备
@@ -79,12 +86,12 @@ Watchdog模块提供的主要接口如表1所示。
DevHandle WatchdogOpen(int16_t wdtId, DevHandle *handle);
```
-**表2** WatchdogOpen参数和返回值描述
+**表 2** WatchdogOpen参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| wdtId | 看门狗设备号 |
-| handle | 看门狗设备句柄 |
+| wdtId | int16_t类型,看门狗设备号 |
+| handle | DevHandle类型,看门狗设备句柄 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | 打开看门狗设备成功 |
| 负数 | 打开看门狗设备失败 |
@@ -107,12 +114,12 @@ if (ret != HDF_SUCCESS) {
int32_t WatchdogGetStatus(DevHandle handle, int32_t *status);
```
-**表3** WatchdogGetStatus参数和返回值描述
+**表 3** WatchdogGetStatus参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | 看门狗设备句柄 |
-| status | 获取到的看门狗状态 |
+| handle | DevHandle类型,看门狗设备句柄 |
+| status | int32_t类型指针,获取到的看门狗状态 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | 获取看门狗状态成功 |
| 负数 | 获取看门狗状态失败 |
@@ -130,17 +137,16 @@ if (ret != HDF_SUCCESS) {
#### 设置超时时间
-
```c
int32_t WatchdogSetTimeout(DevHandle *handle, uint32_t seconds);
```
-**表4** WatchdogSetTimeout参数和返回值描述
+**表 4** WatchdogSetTimeout参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | 看门狗设备句柄 |
-| seconds | 超时时间,单位为秒 |
+| handle | DevHandle类型,看门狗设备句柄 |
+| seconds | uint32_t类型,超时时间,单位为秒 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 |
@@ -161,12 +167,12 @@ if (ret != HDF_SUCCESS) {
int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds);
```
-**表5** WatchdogGetTimeout参数和返回值描述
+**表 5** WatchdogGetTimeout参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | 看门狗设备句柄 |
-| seconds | 获取的看门狗超时时间 |
+| handle | DevHandle类型,看门狗设备句柄 |
+| seconds | uint32_t类型指针,获取的看门狗超时时间 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | 获取看门狗超时时间成功 |
| 负数 | 获取看门狗超时时间失败 |
@@ -188,11 +194,11 @@ int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds);
int32_t WatchdogStart(DevHandle handle);
```
-**表6** WatchdogStart参数和返回值描述
+**表 6** WatchdogStart参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | 看门狗设备句柄 |
+| handle | DevHandle类型,看门狗设备句柄 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | 启动看门狗成功 |
| 负数 | 启动看门狗失败 |
@@ -213,11 +219,11 @@ if (ret != HDF_SUCCESS) {
int32_t WatchdogFeed(DevHandle handle);
```
-**表7** WatchdogFeed参数和返回值描述
+**表 7** WatchdogFeed参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | 看门狗设备句柄 |
+| handle | DevHandle类型,看门狗设备句柄 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | 喂狗成功 |
| 负数 | 喂狗失败 |
@@ -238,11 +244,11 @@ if (ret != HDF_SUCCESS) {
int32_t WatchdogStop(DevHandle handle);
```
-**表8** WatchdogStop参数和返回值描述
+**表 8** WatchdogStop参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | 看门狗设备句柄 |
+| handle | DevHandle类型,看门狗设备句柄 |
| **返回值** | **返回值描述** |
| HDF_SUCCESS | 停止看门狗成功 |
| 负数 | 停止看门狗失败 |
@@ -265,11 +271,11 @@ if (ret != HDF_SUCCESS) {
void WatchdogClose(DevHandle handle);
```
-**表9** WatchdogClose参数和返回值描述
+**表 9** WatchdogClose参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
-| handle | 看门狗设备句柄 |
+| handle | DevHandle类型,看门狗设备句柄 |
```c
WatchdogClose(handle); // 关闭看门狗
@@ -280,17 +286,23 @@ WatchdogClose(handle); // 关闭看门狗
下面将基于Hi3516DV300开发板展示使用Watchdog完整操作,步骤主要如下:
1. 传入看门狗ID号,及空的描述句柄,打开看门狗设备并获得看门狗设备句柄。
+
2. 通过看门狗设备句柄及超时时间,设置看门狗设备超时时间。
+
3. 通过看门狗设备句柄及待获取超时时间,获取看门狗设备超时时间。
+
4. 通过看门狗设备句柄启动看门狗设备。
+
5. 通过看门狗设备句柄喂狗。
+
6. 通过看门狗设备句柄停止看门狗设备。
+
7. 通过看门狗设备句柄关闭看门狗设备。
```c
-#include "watchdog_if.h" /* watchdog标准接口头文件 */
-#include "hdf_log.h" /* 标准日志打印头文件 */
-#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */
+#include "watchdog_if.h" // watchdog标准接口头文件
+#include "hdf_log.h" // 标准日志打印头文件
+#include "osal_time.h" // 标准延迟&睡眠接口头文件
#define WATCHDOG_TEST_TIMEOUT 2
#define WATCHDOG_TEST_FEED_TIME 6
@@ -304,91 +316,92 @@ static int32_t TestCaseWatchdog(void)
uint32_t timeout;
DevHandle *handle = NULL;
- /* 打开0号看门狗设备 */
+ // 打开0号看门狗设备
ret = WatchdogOpen(wdtId, handle);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret);
+ HDF_LOGE("TestCaseWatchdog: open watchdog_%hd failed, ret:%d\n", wdtId, ret);
return ret;
}
- /* 设置超时时间 */
+ // 设置超时时间
ret = WatchdogSetTimeout(handle, WATCHDOG_TEST_TIMEOUT);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: set timeout fail! ret:%d\n", __func__, ret);
+ HDF_LOGE("TestCaseWatchdog: set timeout fail! ret:%d\n", ret);
WatchdogClose(handle);
return ret;
}
- /* 获取超时时间 */
+ // 获取超时时间
ret = WatchdogGetTimeout(handle, &timeout);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: get timeout fail! ret:%d\n", __func__, ret);
+ HDF_LOGE("TestCaseWatchdog: get timeout fail! ret:%d\n", ret);
WatchdogClose(handle);
return ret;
}
- /* 比较设置与获取的超时时间是否一致*/
+ // 比较设置与获取的超时时间是否一致
if (timeout != WATCHDOG_TEST_TIMEOUT) {
- HDF_LOGE("%s: set:%u, but get:%u", __func__, WATCHDOG_TEST_TIMEOUT, timeout);
+ HDF_LOGE("TestCaseWatchdog: set:%u, but get:%u", WATCHDOG_TEST_TIMEOUT, timeout);
WatchdogClose(handle);
return HDF_FAILURE;
}
- HDF_LOGI("%s: read timeout back:%u\n", __func__, timeout);
+ HDF_LOGI("TestCaseWatchdog: read timeout back:%u\n", timeout);
- /* 启动看门狗,开始计时 */
+ // 启动看门狗,开始计时
ret = WatchdogStart(handle);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: start fail! ret:%d\n", __func__, ret);
+ HDF_LOGE("TestCaseWatchdog: start fail! ret:%d\n", ret);
WatchdogClose(handle);
return ret;
}
- /* 获取看门狗状态,是否启动*/
+ // 获取看门狗状态,是否启动
status = WATCHDOG_STOP;
ret = WatchdogGetStatus(handle, &status);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: get status fail! ret:%d", __func__, ret);
+ HDF_LOGE("TestCaseWatchdog: get status fail! ret:%d", ret);
WatchdogClose(handle);
return ret;
}
if (status != WATCHDOG_START) {
- HDF_LOGE("%s: status is:%d after start", __func__, status);
+ HDF_LOGE("TestCaseWatchdog: status is:%d after start", status);
WatchdogClose(handle);
return HDF_FAILURE;
}
- /* 每隔1S喂狗一次 */
+ // 每隔1S喂狗一次
for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) {
- HDF_LOGI("%s: feeding watchdog %d times... \n", __func__, i);
+ HDF_LOGI("TestCaseWatchdog: feeding watchdog %d times... \n", i);
ret = WatchdogFeed(handle);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: feed dog fail! ret:%d\n", __func__, ret);
+ HDF_LOGE("TestCaseWatchdog: feed dog fail! ret:%d\n", ret);
WatchdogClose(handle);
return ret;
}
OsalSleep(1);
}
- /* 由于喂狗间隔小于超时时间,系统不会发生复位,此日志可以正常打印 */
- HDF_LOGI("%s: no reset ... feeding test OK!!!\n", __func__);
+ // 由于喂狗间隔小于超时时间,系统不会发生复位,此日志可以正常打印
+ HDF_LOGI("TestCaseWatchdog: no reset ... feeding test OK!!!\n");
ret = WatchdogStop(handle);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: stop fail! ret:%d", __func__, ret);
+ HDF_LOGE("TestCaseWatchdog: stop fail! ret:%d", ret);
WatchdogClose(handle);
return ret;
}
- /* 获取看门狗状态,是否停止*/
+ // 获取看门狗状态,是否停止
status = WATCHDOG_START;
ret = WatchdogGetStatus(handle, &status);
if (ret != HDF_SUCCESS) {
- HDF_LOGE("%s: get status fail! ret:%d", __func__, ret);
+ HDF_LOGE("TestCaseWatchdog: get status fail! ret:%d", ret);
WatchdogClose(handle);
return ret;
}
if (status != WATCHDOG_STOP) {
- HDF_LOGE("%s: status is:%d after stop", __func__, status);
+ HDF_LOGE("TestCaseWatchdog: status is:%d after stop", status);
WatchdogClose(handle);
return HDF_FAILURE;
}
WatchdogClose(handle);
+ HDF_LOGD("TestCaseWatchdog: function tests end.");
return HDF_SUCCESS;
}
-```
\ No newline at end of file
+```
diff --git a/zh-cn/device-dev/driver/driver-platform-watchdog-develop.md b/zh-cn/device-dev/driver/driver-platform-watchdog-develop.md
index 95f0816254de45d7855ebd9f7e7fab10eaa80aad..68553fbe7a6f15af7f8638bb98749602cd757c85 100755
--- a/zh-cn/device-dev/driver/driver-platform-watchdog-develop.md
+++ b/zh-cn/device-dev/driver/driver-platform-watchdog-develop.md
@@ -17,17 +17,20 @@
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
+
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
Watchdog模块各分层作用:
- 接口层提供打开看门狗设备、获取看门狗设备状态、启动看门狗设备、设置看门狗设备超时时间、获取看门狗设备超时时间、喂狗、停止看门狗设备超时时间、关闭看门狗设备的接口。
+
- 核心层主要提供看门狗控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
+
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图 1** Watchdog独立服务模式结构图
-![image](figures/独立服务模式结构图.png "Watchdog独立服务模式结构图")
+![Watchdog独立服务模式结构图](figures/独立服务模式结构图.png)
## 开发指导
@@ -54,15 +57,15 @@ struct WatchdogMethod {
};
```
-**表1** WatchdogMethod成员的钩子函数功能说明
+**表 1** WatchdogMethod成员的钩子函数功能说明
| 成员函数 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
| getStatus | wdt:结构体指针,核心层Watchdog控制器 | status:int32_t类型指针,表示获取的看门狗的状态(打开或关闭) | HDF_STATUS相关状态 | 获取看门狗状态 |
-| setTimeout | wdt:结构体指针,核心层Watchdog控制器;seconds:设置的看门狗超时时间 | 无 | HDF_STATUS相关状态 | 设置看门狗超时时间,单位秒,需要保证看门狗实际运行的时间符合该值 |
-| getTimeout | wdt:结构体指针,核心层Watchdog控制器 | seconds:uint32_t类型指针,表示获取的超时时间 | HDF_STATUS相关状态 | 获取看门狗超时时间 |
-| start | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 启动看门狗 |
-| stop | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 停止看门狗 |
+| setTimeout | wdt:结构体指针,核心层Watchdog控制器
seconds:uint32_t类型,设置的看门狗超时时间 | 无 | HDF_STATUS相关状态 | 设置看门狗超时时间,单位秒,需要保证看门狗实际运行的时间符合该值 |
+| getTimeout | wdt:结构体指针,核心层Watchdog控制器 | seconds:uint32_t类型指针,表示获取的超时时间 | HDF_STATUS相关状态 | 获取看门狗超时时间 |
+| start | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 启动看门狗 |
+| stop | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 停止看门狗 |
| feed | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 喂狗 |
| getPriv | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 获取看门狗驱动的私有数据 |
| releasePriv | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 释放看门狗驱动的私有数据 |
@@ -71,18 +74,22 @@ struct WatchdogMethod {
Watchdog模块适配包含以下四个步骤:
-- 实例化驱动入口。
-- 配置属性文件。
-- 实例化Watchdog控制器对象。
-- 驱动调试。
+- 实例化驱动入口
+
+- 配置属性文件
+
+- 实例化Watchdog控制器对象
+
+- 驱动调试
### 开发实例
下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/watchdog/watchdog_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
-1. 实例化驱动入口。
+1. 实例化驱动入口
驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
+
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
Watchdog驱动入口开发参考:
@@ -90,199 +97,212 @@ Watchdog模块适配包含以下四个步骤:
```c
struct HdfDriverEntry g_watchdogDriverEntry = {
.moduleVersion = 1,
- .Bind = Hi35xxWatchdogBind, // 见Bind参考
- .Init = Hi35xxWatchdogInit, // 见Init参考
- .Release = Hi35xxWatchdogRelease, // 见Release参考
+ .Bind = Hi35xxWatchdogBind, // 挂接Watchdog模块Bind实例化
+ .Init = Hi35xxWatchdogInit, // 挂接Watchdog模块Init实例化,本例是一个空实现,驱动适配者可根据自身需要添加相关操作
+ .Release = Hi35xxWatchdogRelease, // 挂接Watchdog模块Release实例化
.moduleName = "HDF_PLATFORM_WATCHDOG", // 【必要且与HCS文件中里面的moduleName匹配】
};
HDF_INIT(g_watchdogDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
```
-2. 配置属性文件。
-
- 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode描述。deviceNode信息与驱动入口注册相关。本例以一个Watchdog控制器为例,如有多个器件信息,则需要在device_info文件增加对应的deviceNode描述。器件属性值与核心层WatchdogCntlr成员的默认值或限制范围有密切关系,比如Watchdog设备号,需要在watchdog_config.hcs文件中增加对应的器件属性。
-
- - device_info.hcs 配置参考:
-
- 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
-
- ```c
- root {
- device_info {
- match_attr = "hdf_manager";
- device_watchdog :: device { // 设备节点
- device0 :: deviceNode { // 驱动的DeviceNode节点
- policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2
- priority = 20; // 驱动启动优先级
- permission = 0644; // 驱动创建设备节点权限
- moduleName = "HDF_PLATFORM_WATCHDOG"; // 【必要】用于指定驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
- serviceName = "HDF_PLATFORM_WATCHDOG_0"; // 【必要】驱动对外发布服务的名称,必须唯一。
- deviceMatchAttr = "hisilicon_hi35xx_watchdog_0"; // 【必要】用于配置控制器私有数据,必须和驱动私有数据配置表watchdog_config.hcs中的match_attr值保持一致。
- }
- }
- }
- }
- ```
-
- - watchdog_config.hcs 配置参考:
-
- 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs文件配置器件属性,其中配置参数如下:
-
- ```c
- root {
- platform {
- template watchdog_controller { // 【必要】配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值
- id = 0; // watchdog ID号
- match_attr = "";
- regBase = 0x12050000; // 【必要】地址映射需要,物理基地址
- regStep = 0x1000; // 【必要】地址映射需要,寄存器偏移步进
- }
- controller_0x12050000 :: watchdog_controller { // 【必要】是作为设备驱动私有数据匹配的关键字
- match_attr = "hisilicon_hi35xx_watchdog_0"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致
- }
- // 如果存在多个watchdog设备时【必须】添加节点,否则不用
- ...
- }
- }
- ```
-
- 需要注意的是,新增watchdog_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
-
- ```c
- #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs" // 配置文件相对路径
- ```
-
-3. 实例化Watchdog控制器对象。
-
- 完成驱动入口注册之后,下一步就是以核心层WatchdogCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化WatchdogCntlr成员WatchdogMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
-
- - 驱动适配者自定义结构体参考。
-
- 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且watchdog_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层WatchdogCntlr对象,例如watchdog设备ID号。
-
- ```c
- struct Hi35xxWatchdog {
- struct WatchdogCntlr wdt; // 【必要】是核心层控制对象,具体描述见下面
- OsalSpinlock lock; // 【必要】驱动适配者需要基于此锁变量对watchdog设备实现对应的加锁解锁
- volatile unsigned char *regBase; // 【必要】地址映射需要,寄存器基地址
- uint32_t phyBase; // 【必要】地址映射需要,物理基址
- uint32_t regStep; // 【必要】地址映射需要,寄存器偏移步进
- };
-
- struct WatchdogCntlr { // WatchdogCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
- struct IDeviceIoService service; // 驱动服务
- struct HdfDeviceObject *device; // 驱动设备对象
- OsalSpinlock lock; // 自旋锁
- struct WatchdogMethod *ops; // 钩子函数
- int16_t wdtId; // watchdog设备ID号
- void *priv; // 私有数据
- };
- ```
-
- - WatchdogCntlr成员钩子函数结构体WatchdogMethod的实例化,其他成员在Init和Bind函数中初始化。
-
- ```c
- static struct WatchdogMethod g_method = { // 钩子函数实例化
- .getStatus = Hi35xxWatchdogGetStatus, // 获取看门狗状态
- .start = Hi35xxWatchdogStart, // 启动看门狗
- .stop = Hi35xxWatchdogStop, // 停止看门狗
- .setTimeout = Hi35xxWatchdogSetTimeout, // 设置看门狗超时时间
- .getTimeout = Hi35xxWatchdogGetTimeout, // 获取看门狗超时时间
- .feed = Hi35xxWatchdogFeed, // 喂狗
- };
- ```
-
- - Init函数和Bind函数开发参考:
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。
-
- **表2** Init函数和Bind函数返回值和描述
-
- | 状态(值) | 问题描述 |
- | -------- | -------- |
- | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
- | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
- | HDF_ERR_IO | I/O 错误 |
- | HDF_SUCCESS | 初始化成功 |
- | HDF_FAILURE | 初始化失败 |
-
- 函数说明:
-
- 初始化自定义结构体对象,初始化WatchdogCntlr成员,调用核心层WatchdogCntlrAdd函数,完成看门狗控制器的添加。
-
- ```c
- // 一般而言,Init函数需要根据入参(HdfDeviceObject对象)的属性值初始化Hi35xxWatchdog结构体的成员,
- // 但watchdog_hi35xx.c示例中是在bind函数中实现的
- static int32_t Hi35xxWatchdogInit(struct HdfDeviceObject *device)
- {
- (void)device;
- return HDF_SUCCESS;
- }
-
- static int32_t Hi35xxWatchdogBind(struct HdfDeviceObject *device)
- {
- int32_t ret;
- struct Hi35xxWatchdog *hwdt = NULL;
- ...
- hwdt = (struct Hi35xxWatchdog *)OsalMemCalloc(sizeof(*hwdt)); //Hi35xxWatchdog 结构体指针的内存申请
- ...
- hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep); //地址映射
- ...
- hwdt->wdt.priv = (void *)device->property; // 【必要】此处是将设备属性的内容赋值给priv成员,但后续没有调用 priv 成员,
- // 如果需要用到priv成员,需要额外实例化WatchdogMethod的getPriv和releasePriv成员函数
- hwdt->wdt.ops = &g_method; // 【必要】WatchdogMethod实例化对象的挂载
- hwdt->wdt.device = device; // 【必要】这是为了方便HdfDeviceObject与WatchdogcCntlr相互转化
- ret = WatchdogCntlrAdd(&hwdt->wdt); // 【必要】调用此函数初始化核心层结构体,返回成功信号后驱动才完全接入平台核心层
- if (ret != HDF_SUCCESS) { // 不成功的话,需要去除映射并释放Init函数申请的资源
- OsalIoUnmap((void *)hwdt->regBase);
- OsalMemFree(hwdt);
- return ret;
- }
- return HDF_SUCCESS;
- }
- ```
-
- - Release函数开发参考:
-
- 入参:
-
- HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
-
- 返回值:
-
- 无。
-
- 函数说明:
-
- 该函数需要在驱动入口结构体中赋值给Release,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。该函数中需包含释放内存和删除控制器等操作。
-
- ```c
- static void Hi35xxWatchdogRelease(struct HdfDeviceObject *device)
- {
- struct WatchdogCntlr *wdt = NULL;
- struct Hi35xxWatchdog *hwdt = NULL;
- ...
- wdt = WatchdogCntlrFromDevice(device); // 【必要】通过device获取WatchdogCntlr
- ...
- if (wdt == NULL) {
- return;
- }
- WatchdogCntlrRemove(wdt); // 【必要】调用WatchdogCntlrRemove函数来释放WatchdogCntlr对象的内容
- hwdt = (struct Hi35xxWatchdog *)wdt; // 这里将WatchdogCntlr转化为Hi35xxWatchdog
- if (hwdt->regBase != NULL) { // 【必要】解除地址映射
- OsalIoUnmap((void *)hwdt->regBase);
- hwdt->regBase = NULL;
- }
- OsalMemFree(hwdt); // 【必要】释放驱动适配者自定义对象占用的内存
- }
- ```
-
-4. 驱动调试。
-
- 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
\ No newline at end of file
+2. 配置属性文件
+
+ 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode描述。deviceNode信息与驱动入口注册相关。本例以一个Watchdog控制器为例,如有多个器件信息,则需要在device_info文件增加对应的deviceNode描述,以及在watchdog_config.hcs文件中增加对应的器件属性。器件属性值与核心层WatchdogCntlr成员的默认值或限制范围有密切关系,比如Watchdog设备号,需要在watchdog_config.hcs文件中增加对应的器件属性。
+
+ 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
+
+ **表 2** device_info.hcs节点参数说明
+
+ | 成员名 | 值 |
+ | -------- | -------- |
+ | policy | 驱动服务发布的策略,Watchdog控制器具体配置为2,表示驱动对内核态和用户态都发布服务 |
+ | priority | 驱动启动优先级(0-200),值越大优先级越低。Watchdog控制器具体配置为20 |
+ | permission | 驱动创建设备节点权限,Watchdog控制器具体配置为0664 |
+ | moduleName | 驱动名称,Watchdog控制器固定为HDF_PLATFORM_WATCHDOG |
+ | serviceName | 驱动对外发布服务的名称,Watchdog控制器服务名设置为HDF_PLATFORM_WATCHDOG_X,X代表Watchdog控制器编号|
+ | deviceMatchAttr | 驱动私有数据匹配的关键字,Watchdog控制器设置为hisilicon_hi35xx_watchdog_X,X代表Watchdog控制器编号 |
+
+ - device_info.hcs 配置参考:
+
+ 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
+
+ ```c
+ root {
+ device_info {
+ match_attr = "hdf_manager";
+ device_watchdog :: device {
+ device0 :: deviceNode { // 驱动的DeviceNode节点
+ policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2
+ priority = 20; // 驱动启动优先级
+ permission = 0644; // 驱动创建设备节点权限
+ moduleName = "HDF_PLATFORM_WATCHDOG"; // 【必要】用于指定驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
+ serviceName = "HDF_PLATFORM_WATCHDOG_0"; // 【必要】驱动对外发布服务的名称,必须唯一。
+ deviceMatchAttr = "hisilicon_hi35xx_watchdog_0"; // 【必要】用于配置控制器私有数据,必须和驱动私有数据配置表watchdog_config.hcs中的match_attr值保持一致。
+ }
+ ...... // 如果存在多个watchdog设备时【必须】添加节点,否则不用
+ }
+ }
+ }
+ ```
+
+ - watchdog_config.hcs 配置参考:
+
+ 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs文件配置器件属性,其中配置参数如下:
+
+ ```c
+ root {
+ platform {
+ template watchdog_controller { // 【必要】配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值
+ id = 0; // watchdog ID号
+ match_attr = "";
+ regBase = 0x12050000; // 【必要】地址映射需要,物理基地址
+ regStep = 0x1000; // 【必要】地址映射需要,寄存器偏移步进
+ }
+ controller_0x12050000 :: watchdog_controller { // 【必要】是作为设备驱动私有数据匹配的关键字
+ match_attr = "hisilicon_hi35xx_watchdog_0"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致
+ }
+ ...... // 如果存在多个watchdog设备时【必须】添加节点,否则不用
+ }
+ }
+ ```
+
+ 需要注意的是,新增watchdog_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。
+
+ ```c
+ #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs" // 配置文件相对路径
+ ```
+
+3. 实例化Watchdog控制器对象
+
+ 完成驱动入口注册之后,下一步就是以核心层WatchdogCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化WatchdogCntlr成员WatchdogMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
+
+ - 驱动适配者自定义结构体参考。
+
+ 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且watchdog_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层WatchdogCntlr对象,例如watchdog设备ID号。
+
+ ```c
+ struct Hi35xxWatchdog {
+ struct WatchdogCntlr wdt; // 【必要】是核心层控制对象,具体描述见下面
+ OsalSpinlock lock; // 【必要】驱动适配者需要基于此锁变量对watchdog设备实现对应的加锁解锁
+ volatile unsigned char *regBase; // 【必要】地址映射需要,寄存器基地址
+ uint32_t phyBase; // 【必要】地址映射需要,物理基址
+ uint32_t regStep; // 【必要】地址映射需要,寄存器偏移步进
+ };
+
+ struct WatchdogCntlr { // WatchdogCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
+ struct IDeviceIoService service; // 驱动服务
+ struct HdfDeviceObject *device; // 驱动设备对象
+ OsalSpinlock lock; // 自旋锁
+ struct WatchdogMethod *ops; // 钩子函数
+ int16_t wdtId; // watchdog设备ID号
+ void *priv; // 私有数据
+ };
+ ```
+
+ - WatchdogCntlr成员钩子函数结构体WatchdogMethod的实例化。
+
+ ```c
+ static struct WatchdogMethod g_method = { // 钩子函数实例化
+ .getStatus = Hi35xxWatchdogGetStatus, // 获取看门狗状态
+ .start = Hi35xxWatchdogStart, // 启动看门狗
+ .stop = Hi35xxWatchdogStop, // 停止看门狗
+ .setTimeout = Hi35xxWatchdogSetTimeout, // 设置看门狗超时时间
+ .getTimeout = Hi35xxWatchdogGetTimeout, // 获取看门狗超时时间
+ .feed = Hi35xxWatchdogFeed, // 喂狗
+ };
+ ```
+
+ - Init函数和Bind函数开发参考:
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ HDF_STATUS相关状态 (表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。
+
+ **表 3** HDF_STATUS相关状态说明
+
+ | 状态(值) | 问题描述 |
+ | -------- | -------- |
+ | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
+ | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
+ | HDF_ERR_IO | I/O 错误 |
+ | HDF_SUCCESS | 初始化成功 |
+ | HDF_FAILURE | 初始化失败 |
+
+ 函数说明:
+
+ 初始化自定义结构体对象,初始化WatchdogCntlr成员,调用核心层WatchdogCntlrAdd函数,完成看门狗控制器的添加。
+
+ ```c
+ // 一般而言,Init函数需要根据入参(HdfDeviceObject对象)的属性值初始化Hi35xxWatchdog结构体的成员,
+ // 但watchdog_hi35xx.c示例中是在bind函数中实现的
+ static int32_t Hi35xxWatchdogInit(struct HdfDeviceObject *device)
+ {
+ (void)device;
+ return HDF_SUCCESS;
+ }
+
+ static int32_t Hi35xxWatchdogBind(struct HdfDeviceObject *device)
+ {
+ int32_t ret;
+ struct Hi35xxWatchdog *hwdt = NULL;
+ ......
+ hwdt = (struct Hi35xxWatchdog *)OsalMemCalloc(sizeof(*hwdt)); // Hi35xxWatchdog 结构体指针的内存申请
+ ......
+ hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep); // 地址映射
+ ......
+ hwdt->wdt.priv = (void *)device->property; // 【必要】此处是将设备属性的内容赋值给priv成员,但后续没有调用 priv 成员,
+ // 如果需要用到priv成员,需要额外实例化WatchdogMethod的getPriv和releasePriv成员函数
+ hwdt->wdt.ops = &g_method; // 【必要】WatchdogMethod实例化对象的挂载
+ hwdt->wdt.device = device; // 【必要】这是为了方便HdfDeviceObject与WatchdogcCntlr相互转化
+ ret = WatchdogCntlrAdd(&hwdt->wdt); // 【必要】调用此函数初始化核心层结构体,返回成功信号后驱动才完全接入平台核心层
+ if (ret != HDF_SUCCESS) { // 不成功的话,需要去除映射并释放Init函数申请的资源
+ OsalIoUnmap((void *)hwdt->regBase);
+ OsalMemFree(hwdt);
+ return ret;
+ }
+ return HDF_SUCCESS;
+ }
+ ```
+
+ - Release函数开发参考:
+
+ 入参:
+
+ HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
+
+ 返回值:
+
+ 无。
+
+ 函数说明:
+
+ 该函数需要在驱动入口结构体中赋值给Release,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。该函数中需包含释放内存和删除控制器等操作。
+
+ ```c
+ static void Hi35xxWatchdogRelease(struct HdfDeviceObject *device)
+ {
+ struct WatchdogCntlr *wdt = NULL;
+ struct Hi35xxWatchdog *hwdt = NULL;
+ ......
+ wdt = WatchdogCntlrFromDevice(device); // 【必要】通过device获取WatchdogCntlr
+ ......
+ if (wdt == NULL) {
+ return;
+ }
+ WatchdogCntlrRemove(wdt); // 【必要】调用WatchdogCntlrRemove函数来释放WatchdogCntlr对象的内容
+ hwdt = (struct Hi35xxWatchdog *)wdt; // 这里将WatchdogCntlr转化为Hi35xxWatchdog
+ if (hwdt->regBase != NULL) { // 【必要】解除地址映射
+ OsalIoUnmap((void *)hwdt->regBase);
+ hwdt->regBase = NULL;
+ }
+ OsalMemFree(hwdt); // 【必要】释放驱动适配者自定义对象占用的内存
+ }
+ ```
+
+4. 驱动调试
+
+ 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,获取看门狗状态、喂狗等。