提交 952ecd1c 编写于 作者: J jiaziyangnewer

fix: platform documents refresh.

Signed-off-by: Njiaziyangnewer <jiaziyang1@huawei.com>
上级 8c176ecf
...@@ -4,14 +4,15 @@ ...@@ -4,14 +4,15 @@
### 功能简介<a name="section2"></a> ### 功能简介<a name="section2"></a>
ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1: ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1所示
**图 1** ADC物理连线示意图<a name="fig1"></a> **图 1** ADC物理连线示意图<a name="fig1"></a>
![](figures/ADC物理连线示意图.png "ADC物理连线示意图") ![ADC物理连线示意图](figures/ADC物理连线示意图.png)
ADC接口定义了完成AD转换的通用方法集合,包括: ADC接口定义了完成AD转换的通用方法集合,包括:
- ADC设备管理:打开或关闭ADC设备。 - ADC设备管理:打开或关闭ADC设备。
- ADC读取转换结果:读取AD转换结果。 - ADC读取转换结果:读取AD转换结果。
### 基本概念<a name="section3"></a> ### 基本概念<a name="section3"></a>
...@@ -60,8 +61,8 @@ ADC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ ...@@ -60,8 +61,8 @@ ADC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
使用ADC设备的一般流程如图2所示。 使用ADC设备的一般流程如图2所示。
**图 2** ADC使用流程图<a name="fig2"></a> **图 2** ADC使用流程图<a name="fig2"></a>
![](figures/ADC使用流程图.png "ADC使用流程图") ![ADC使用流程图](figures/ADC使用流程图.png)
#### 打开ADC设备 #### 打开ADC设备
...@@ -78,7 +79,7 @@ DevHandle AdcOpen(int16_t number); ...@@ -78,7 +79,7 @@ DevHandle AdcOpen(int16_t number);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | ----------------- | | ---------- | ----------------- |
| number | ADC设备号 | | number | int16_t类型,ADC设备号 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| NULL | 打开ADC设备失败 | | NULL | 打开ADC设备失败 |
| 设备句柄 | 打开的ADC设备句柄 | | 设备句柄 | 打开的ADC设备句柄 |
...@@ -86,13 +87,13 @@ DevHandle AdcOpen(int16_t number); ...@@ -86,13 +87,13 @@ DevHandle AdcOpen(int16_t number);
假设系统中存在2个ADC设备,编号从0到1,那么我们现在打开1号设备。 假设系统中存在2个ADC设备,编号从0到1,那么我们现在打开1号设备。
```c ```c
DevHandle adcHandle = NULL; /* ADC设备句柄 / DevHandle adcHandle = NULL; // ADC设备句柄
/* 打开ADC设备 */ // 打开ADC设备
adcHandle = AdcOpen(1); adcHandle = AdcOpen(1);
if (adcHandle == NULL) { if (adcHandle == NULL) {
HDF_LOGE("AdcOpen: fail\n"); HDF_LOGE("AdcOpen: fail\n");
return; return NULL;
} }
``` ```
...@@ -108,11 +109,11 @@ int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t *val); ...@@ -108,11 +109,11 @@ int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t *val);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | ADC设备句柄 | | handle | DevHandle类型,ADC设备句柄 |
| channel | ADC设备通道号 | | channel| uint32_t类型,ADC设备通道号 |
| val | AD转换结果 | | val | uint32_t类型指针,AD转换结果 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 读取成功 | | HDF_SUCCESS | 读取成功 |
| 负数 | 读取失败 | | 负数 | 读取失败 |
读取转换结果示例(以通道1为例): 读取转换结果示例(以通道1为例):
...@@ -122,9 +123,9 @@ uint32_t value; ...@@ -122,9 +123,9 @@ uint32_t value;
int32_t ret; int32_t ret;
ret = AdcRead(adcHandle, 1, &value); ret = AdcRead(adcHandle, 1, &value);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("ADC read fail!\n"); HDF_LOGE("ADC read fail!\n");
return; return ret;
} }
``` ```
...@@ -140,14 +141,14 @@ void AdcClose(DevHandle handle); ...@@ -140,14 +141,14 @@ void AdcClose(DevHandle handle);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ------ | ----------- | | ------ | ----------- |
| handle | ADC设备句柄 | | handle | DevHandle类型,ADC设备句柄 |
| 返回值 | 返回值描述 | | 返回值 | 返回值描述 |
| 无 | 无 | | 无 | 无 |
关闭ADC设备示例: 关闭ADC设备示例:
```c ```c
AdcClose(adcHandle); /* 关闭ADC设备 */ AdcClose(adcHandle); // 关闭ADC设备
``` ```
### 使用实例<a name="section10"></a> ### 使用实例<a name="section10"></a>
...@@ -163,15 +164,15 @@ AdcClose(adcHandle); /* 关闭ADC设备 */ ...@@ -163,15 +164,15 @@ AdcClose(adcHandle); /* 关闭ADC设备 */
示例如下: 示例如下:
```c ```c
#include "adc_if.h" /* ADC标准接口头文件 */ #include "adc_if.h" // ADC标准接口头文件
#include "hdf_log.h" /* 标准日志打印头文件 */ #include "hdf_log.h" // 标准日志打印头文件
/* 设备号0,通道号1 */ /// 设备号0,通道号1
#define ADC_DEVICE_NUM 0 #define ADC_DEVICE_NUM 0
#define ADC_CHANNEL_NUM 1 #define ADC_CHANNEL_NUM 1
#define ADC_TEST_NUM 30 #define ADC_TEST_NUM 30
/* ADC例程总入口 */ // ADC例程总入口
static int32_t TestCaseAdc(void) static int32_t TestCaseAdc(void)
{ {
int32_t i; int32_t i;
...@@ -179,14 +180,14 @@ static int32_t TestCaseAdc(void) ...@@ -179,14 +180,14 @@ static int32_t TestCaseAdc(void)
DevHandle adcHandle = NULL; DevHandle adcHandle = NULL;
uint32_t readBuf[ADC_TEST_NUM] = {0}; uint32_t readBuf[ADC_TEST_NUM] = {0};
/* 打开ADC设备 */ // 打开ADC设备
adcHandle = AdcOpen(ADC_DEVICE_NUM); adcHandle = AdcOpen(ADC_DEVICE_NUM);
if (adcHandle == NULL) { if (adcHandle == NULL) {
HDF_LOGE("%s: Open ADC%u fail!", __func__, ADC_DEVICE_NUM); HDF_LOGE("%s: Open ADC%u fail!", __func__, ADC_DEVICE_NUM);
return -1; return -1;
} }
/* 连续进行30次AD转换并读取转换结果 */ // 连续进行30次AD转换并读取转换结果
for (i = 0; i < ADC_TEST_NUM; i++) { for (i = 0; i < ADC_TEST_NUM; i++) {
ret = AdcRead(adcHandle, ADC_CHANNEL_NUM, &readBuf[i]); ret = AdcRead(adcHandle, ADC_CHANNEL_NUM, &readBuf[i]);
if (ret != HDF_SUCCESS) { if (ret != HDF_SUCCESS) {
...@@ -197,7 +198,7 @@ static int32_t TestCaseAdc(void) ...@@ -197,7 +198,7 @@ static int32_t TestCaseAdc(void)
} }
HDF_LOGI("%s: ADC read successful!", __func__); HDF_LOGI("%s: ADC read successful!", __func__);
/* 访问完毕关闭ADC设备 */ // 访问完毕关闭ADC设备
AdcClose(adcHandle); AdcClose(adcHandle);
return 0; return 0;
......
...@@ -7,10 +7,13 @@ ...@@ -7,10 +7,13 @@
DAC(Digital to Analog Converter)是一种通过电流、电压或电荷的形式将数字信号转换为模拟信号的设备,主要用于: DAC(Digital to Analog Converter)是一种通过电流、电压或电荷的形式将数字信号转换为模拟信号的设备,主要用于:
- 作为过程控制计算机系统的输出通道,与执行器相连,实现对生产过程的自动控制。 - 作为过程控制计算机系统的输出通道,与执行器相连,实现对生产过程的自动控制。
- 在利用反馈技术的模数转换器设计中,作为重要的功能模块呈现。 - 在利用反馈技术的模数转换器设计中,作为重要的功能模块呈现。
DAC接口定义了完成DAC传输的通用方法集合,包括: DAC接口定义了完成DAC传输的通用方法集合,包括:
- DAC设备管理:打开或关闭DAC设备。 - DAC设备管理:打开或关闭DAC设备。
- DAC设置目标值:设置DAC设备需要将数字信号转成模拟信号的目标值。 - DAC设置目标值:设置DAC设备需要将数字信号转成模拟信号的目标值。
### 基本概念 ### 基本概念
...@@ -61,8 +64,8 @@ DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/f ...@@ -61,8 +64,8 @@ DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/f
| 接口名 | 接口描述 | | 接口名 | 接口描述 |
| ------------------------------------------------------------------ | ------------ | | ------------------------------------------------------------------ | ------------ |
| DevHandle DacOpen(uint32_t number) | 打开DAC设备。 | | DevHandle DacOpen(uint32_t number) | 打开DAC设备。|
| void DacClose(DevHandle handle) | 关闭DAC设备。 | | void DacClose(DevHandle handle) | 关闭DAC设备。|
| int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val) | 设置DA目标值。 | | int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val) | 设置DA目标值。 |
### 开发步骤 ### 开发步骤
...@@ -76,7 +79,7 @@ DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/f ...@@ -76,7 +79,7 @@ DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/f
在进行DA转换之前,首先要调用DacOpen打开DAC设备,打开函数如下所示: 在进行DA转换之前,首先要调用DacOpen打开DAC设备,打开函数如下所示:
```c++ ```c
DevHandle DacOpen(uint32_t number); DevHandle DacOpen(uint32_t number);
``` ```
...@@ -84,27 +87,27 @@ DevHandle DacOpen(uint32_t number); ...@@ -84,27 +87,27 @@ DevHandle DacOpen(uint32_t number);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| --------- | ---------------- | | --------- | ---------------- |
| number | DAC设备号。 | | number | uint32_t类型,DAC设备号。 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| NULL | 打开DAC设备失败。 | | NULL | 打开DAC设备失败。 |
| 设备句柄 | 打开的DAC设备句柄。 | | 设备句柄 | 打开的DAC设备句柄。 |
假设系统中存在2个DAC设备,编号从0到1,现在打开1号设备。 假设系统中存在2个DAC设备,编号从0到1,现在打开1号设备。
```c++ ```c
DevHandle dacHandle = NULL; // DAC设备句柄 DevHandle dacHandle = NULL; // DAC设备句柄
/* 打开DAC设备 */ // 打开DAC设备
dacHandle = DacOpen(1); dacHandle = DacOpen(1);
if (dacHandle == NULL) { if (dacHandle == NULL) {
HDF_LOGE("DacOpen: failed\n"); HDF_LOGE("DacOpen: open dac fail.\n");
return; return NULL;
} }
``` ```
#### 设置DA目标值 #### 设置DA目标值
```c++ ```c
int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val); int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val);
``` ```
...@@ -112,27 +115,29 @@ int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val); ...@@ -112,27 +115,29 @@ int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| --------- | ------------ | | --------- | ------------ |
| handle | DAC设备句柄。 | | handle | DevHandle类型,DAC设备句柄。 |
| channel | DAC设备通道号。| | channel | uint32_t类型,DAC设备通道号。 |
| val | 设置DA的值。 | | val | uint32_t类型,设置DA的值。 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 写入成功。 | | HDF_SUCCESS | 写入DA目标值成功 |
| 负数 | 写入失败。 | | 负数 | 写入DA目标值失败 |
```c++ ```c
/* 通过DAC_CHANNEL_NUM设备通道写入目标val值 */ // 通过DAC_CHANNEL_NUM设备通道写入目标val值
int32_t ret;
ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val); ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val);
if (ret != HDF_SUCCESS) { 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); DacClose(dacHandle);
return -1; return ret;
} }
``` ```
#### 关闭DAC设备 #### 关闭DAC设备
DAC通信完成之后,需要关闭DAC设备,关闭函数如下所示: DAC通信完成之后,需要关闭DAC设备,关闭函数如下所示:
```c++
```c
void DacClose(DevHandle handle); void DacClose(DevHandle handle);
``` ```
...@@ -142,12 +147,12 @@ void DacClose(DevHandle handle); ...@@ -142,12 +147,12 @@ void DacClose(DevHandle handle);
| --------- | ------------ | | --------- | ------------ |
| handle | DAC设备句柄。 | | handle | DAC设备句柄。 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| void | 无 | | 无 | 无 |
关闭DAC设备示例: 关闭DAC设备示例:
```c++ ```c
DacClose(dacHandle); /* 关闭DAC设备 */ DacClose(dacHandle); // 关闭DAC设备
``` ```
## 使用实例 ## 使用实例
...@@ -155,20 +160,22 @@ DacClose(dacHandle); /* 关闭DAC设备 */ ...@@ -155,20 +160,22 @@ DacClose(dacHandle); /* 关闭DAC设备 */
DAC设备的具体使用方式可以参考如下示例代码,示例代码步骤主要如下: DAC设备的具体使用方式可以参考如下示例代码,示例代码步骤主要如下:
1. 根据设备号DAC_DEVICE_NUM打开DAC设备得到设备句柄。 1. 根据设备号DAC_DEVICE_NUM打开DAC设备得到设备句柄。
2. 通过DAC的设备号以及设备通道设置val的值,如果写入失败则关闭设备句柄。 2. 通过DAC的设备号以及设备通道设置val的值,如果写入失败则关闭设备句柄。
3. 访问完毕DAC设备后,则关闭该设备句柄。 3. 访问完毕DAC设备后,则关闭该设备句柄。
运行结果:根据输入的val通过打印日志得到输出的结果。 运行结果:根据输入的val通过打印日志得到输出的结果。
```c++ ```c
#include "dac_if.h" /* DAC标准接口头文件 */ #include "dac_if.h" // DAC标准接口头文件
#include "hdf_log.h" /* 标准日志打印头文件 */ #include "hdf_log.h" // 标准日志打印头文件
/* 设备号0,通道号1 */ // 设备号0,通道号1
#define DAC_DEVICE_NUM 0 #define DAC_DEVICE_NUM 0
#define DAC_CHANNEL_NUM 1 #define DAC_CHANNEL_NUM 1
/* DAC例程总入口 */ // DAC例程总入口
static int32_t TestCaseDac(void) static int32_t TestCaseDac(void)
{ {
// 设置要写入的val值 // 设置要写入的val值
...@@ -176,14 +183,14 @@ static int32_t TestCaseDac(void) ...@@ -176,14 +183,14 @@ static int32_t TestCaseDac(void)
int32_t ret; int32_t ret;
DevHandle dacHandle; DevHandle dacHandle;
/* 打开DAC设备 */ // 打开DAC设备
dacHandle = DacOpen(DAC_DEVICE_NUM); dacHandle = DacOpen(DAC_DEVICE_NUM);
if (dacHandle == NULL) { if (dacHandle == NULL) {
HDF_LOGE("%s: Open DAC%u fail!", __func__, DAC_DEVICE_NUM); HDF_LOGE("%s: Open DAC%u fail!", __func__, DAC_DEVICE_NUM);
return -1; return -1;
} }
/* 写入数据 */ // 写入数据
ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val); ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val);
if (ret != HDF_SUCCESS) { if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret); HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret);
...@@ -191,9 +198,9 @@ static int32_t TestCaseDac(void) ...@@ -191,9 +198,9 @@ static int32_t TestCaseDac(void)
return -1; return -1;
} }
/* 访问完毕关闭DAC设备 */ // 访问完毕关闭DAC设备
DacClose(dacHandle); DacClose(dacHandle);
HDF_LOGI("%s: function tests end.", __func__);
return 0; return 0;
} }
``` ```
...@@ -8,10 +8,13 @@ GPIO(General-purpose input/output)即通用型输入输出。通常,GPIO ...@@ -8,10 +8,13 @@ GPIO(General-purpose input/output)即通用型输入输出。通常,GPIO
GPIO接口定义了操作GPIO管脚的标准方法集合,包括: GPIO接口定义了操作GPIO管脚的标准方法集合,包括:
- 设置管脚方向:方向可以是输入或者输出(暂不支持高阻态)。 - 设置、获取管脚方向:方向可以是输入或者输出(暂不支持高阻态)。
- 读写管脚电平值:电平值可以是低电平或高电平。
- 设置管脚中断服务函数:设置一个管脚的中断响应函数,以及中断触发方式。 - 读、写管脚电平值:电平值可以是低电平或高电平。
- 使能和禁止管脚中断:禁止或使能管脚中断。
- 设置、取消管脚中断服务函数:设置一个管脚的中断响应函数,以及中断触发方式。取消一个管脚的中断服务函数。
- 使能、禁止管脚中断:禁止或使能管脚中断。
### 基本概念 ### 基本概念
...@@ -34,7 +37,9 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。 ...@@ -34,7 +37,9 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。
GPIO模块各分层作用: GPIO模块各分层作用:
- 接口层提供操作GPIO管脚的标准方法。 - 接口层提供操作GPIO管脚的标准方法。
- 核心层主要提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。 - 核心层主要提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 - 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图 1** GPIO统一服务模式结构图 **图 1** GPIO统一服务模式结构图
...@@ -45,23 +50,23 @@ GPIO模块各分层作用: ...@@ -45,23 +50,23 @@ 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 | | GpioGetByName(const char \*gpioName) | 获取GPIO管脚ID |
| int32_t GpioRead(uint16_t gpio, uint16_t *val) | 读GPIO管脚电平值 | | int32_t GpioRead(uint16_t gpio, uint16_t \*val) | 读GPIO管脚电平值 |
| int32_t GpioWrite(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 GpioGetDir(uint16_t gpio, uint16_t \*dir) | 获取GPIO管脚方向 |
| int32_t GpioSetDir(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 GpioUnsetIrq(uint16_t gpio, void \*arg) | 取消GPIO管脚对应的中断服务函数 |
| int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, 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 GpioEnableIrq(uint16_t gpio) | 使能GPIO管脚中断 |
| int32_t GpioDisableIrq(uint16_t gpio) | 禁止GPIO管脚中断 | | int32_t GpioDisableIrq(uint16_t gpio) | 禁止GPIO管脚中断 |
...@@ -70,11 +75,11 @@ GPIO模块提供的主要接口如表1所示。 ...@@ -70,11 +75,11 @@ GPIO模块提供的主要接口如表1所示。
### 开发步骤 ### 开发步骤
GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流程如下图所示。 GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流程如图2所示。
**图1** GPIO使用流程图 **图 2** GPIO使用流程图
![image](figures/GPIO使用流程图.png "GPIO使用流程图") ![GPIO使用流程图](figures/GPIO使用流程图.png)
#### 确定GPIO管脚号 #### 确定GPIO管脚号
...@@ -116,15 +121,15 @@ GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流 ...@@ -116,15 +121,15 @@ GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流
int32_t GpioSetDir(uint16_t gpio, uint16_t dir); int32_t GpioSetDir(uint16_t gpio, uint16_t dir);
``` ```
**表2** GpioSetDir参数和返回值描述 **表 2** GpioSetDir参数和返回值描述
| **参数** | **参数描述** | | **参数** | **参数描述** |
| ---------- | ------------------ | | ---------- | ------------------ |
| gpio | GPIO管脚号 | | gpio | uint16_t类型,GPIO管脚号 |
| dir | 待设置的方向值 | | dir | uint16_t类型,待设置的方向值 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 设置成功 | | HDF_SUCCESS | 设置GPIO管脚方向成功 |
| 负数 | 设置失败 | | 负数 | 设置GPIO管脚方向失败 |
假设需要将GPIO管脚3的方向配置为输出,其使用示例如下: 假设需要将GPIO管脚3的方向配置为输出,其使用示例如下:
...@@ -132,8 +137,8 @@ int32_t GpioSetDir(uint16_t gpio, uint16_t dir); ...@@ -132,8 +137,8 @@ int32_t GpioSetDir(uint16_t gpio, uint16_t dir);
int32_t ret; int32_t ret;
ret = GpioSetDir(3, GPIO_DIR_OUT); // 将3号GPIO管脚配置为输出 ret = GpioSetDir(3, GPIO_DIR_OUT); // 将3号GPIO管脚配置为输出
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioSetDir: failed, ret %d\n", ret); HDF_LOGE("GpioSetDir: gpio set dir fail, ret:%d\n", ret);
return ret; return ret;
} }
``` ```
...@@ -146,15 +151,15 @@ if (ret != 0) { ...@@ -146,15 +151,15 @@ if (ret != 0) {
int32_t GpioGetDir(uint16_t gpio, uint16_t *dir); int32_t GpioGetDir(uint16_t gpio, uint16_t *dir);
``` ```
**表3** GpioGetDir参数和返回值描述 **表 3** GpioGetDir参数和返回值描述
| **参数** | **参数描述** | | **参数** | **参数描述** |
| ---------- | ------------------ | | ---------- | ------------------ |
| gpio | GPIO管脚号 | | gpio | uint16_t类型,GPIO管脚号 |
| dir | 获取到的方向值指针 | | dir | uint16_t类型指针,获取到的方向值 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 设置成功 | | HDF_SUCCESS | 获取GPIO管脚方向成功 |
| 负数 | 设置失败 | | 负数 | 获取GPIO管脚方向失败 |
假设需要获取GPIO管脚3的方向,其使用示例如下: 假设需要获取GPIO管脚3的方向,其使用示例如下:
...@@ -163,8 +168,8 @@ int32_t ret; ...@@ -163,8 +168,8 @@ int32_t ret;
uin16_t dir; uin16_t dir;
ret = GpioGetDir(3, &dir); // 获取3号GPIO管脚方向 ret = GpioGetDir(3, &dir); // 获取3号GPIO管脚方向
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioGetDir: failed, ret %d\n", ret); HDF_LOGE("GpioGetDir: gpio get dir fail, ret:%d\n", ret);
return ret; return ret;
} }
``` ```
...@@ -177,15 +182,15 @@ if (ret != 0) { ...@@ -177,15 +182,15 @@ if (ret != 0) {
int32_t GpioRead(uint16_t gpio, uint16_t *val); int32_t GpioRead(uint16_t gpio, uint16_t *val);
``` ```
**表4** GpioRead参数和返回值描述 **表 4** GpioRead参数和返回值描述
| **参数** | **参数描述** | | **参数** | **参数描述** |
| ---------- | -------------------- | | ---------- | -------------------- |
| gpio | GPIO管脚号 | | gpio | uint16_t类型,GPIO管脚号 |
| val | 接收读取电平值的指针 | | val | uint16_t类型指针,接收读取电平值 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 读取成功 | | HDF_SUCCESS | 读取GPIO管脚电平值成功 |
| 负数 | 读取失败 | | 负数 | 读取GPIO管脚电平值失败 |
假设需要读取GPIO管脚3的电平值,其使用示例如下: 假设需要读取GPIO管脚3的电平值,其使用示例如下:
...@@ -194,8 +199,8 @@ int32_t ret; ...@@ -194,8 +199,8 @@ int32_t ret;
uint16_t val; uint16_t val;
ret = GpioRead(3, &val); // 读取3号GPIO管脚电平值 ret = GpioRead(3, &val); // 读取3号GPIO管脚电平值
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioRead: failed, ret %d\n", ret); HDF_LOGE("GpioRead: gpio read fail, ret:%d\n", ret);
return ret; return ret;
} }
``` ```
...@@ -208,15 +213,15 @@ if (ret != 0) { ...@@ -208,15 +213,15 @@ if (ret != 0) {
int32_t GpioWrite(uint16_t gpio, uint16_t val); int32_t GpioWrite(uint16_t gpio, uint16_t val);
``` ```
**表5** GpioWrite参数和返回值描述 **表 5** GpioWrite参数和返回值描述
| **参数** | **参数描述** | | **参数** | **参数描述** |
| ---------- | ------------------ | | ---------- | ------------------ |
| gpio | GPIO管脚号 | | gpio | uint16_t类型,GPIO管脚号 |
| val | 待写入的电平值 | | val | uint16_t类型,待写入的电平值 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 写入成功 | | HDF_SUCCESS | 写入GPIO管脚电平值成功 |
| 负数 | 写入失败 | | 负数 | 写入GPIO管脚电平值失败 |
假设需要给GPIO管脚3写入低电平值,其使用示例如下: 假设需要给GPIO管脚3写入低电平值,其使用示例如下:
...@@ -224,8 +229,8 @@ int32_t GpioWrite(uint16_t gpio, uint16_t val); ...@@ -224,8 +229,8 @@ int32_t GpioWrite(uint16_t gpio, uint16_t val);
int32_t ret; int32_t ret;
ret = GpioWrite(3, GPIO_VAL_LOW); // 给3号GPIO管脚写入低电平值 ret = GpioWrite(3, GPIO_VAL_LOW); // 给3号GPIO管脚写入低电平值
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioRead: failed, ret %d\n", ret); HDF_LOGE("GpioWrite: gpio write fail, ret:%d\n", ret);
return ret; return ret;
} }
``` ```
...@@ -238,17 +243,17 @@ if (ret != 0) { ...@@ -238,17 +243,17 @@ if (ret != 0) {
int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg); int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg);
``` ```
**表6** GpioSetIrq参数和返回值描述 **表 6** GpioSetIrq参数和返回值描述
| **参数** | **参数描述** | | **参数** | **参数描述** |
| ---------- | ------------------------ | | ---------- | ------------------------ |
| gpio | GPIO管脚号 | | gpio | uint16_t类型,GPIO管脚号 |
| mode | 中断触发模式 | | mode | uint16_t类型,中断触发模式 |
| func | 中断服务程序 | | func | 函数指针,中断服务程序 |
| arg | 传递给中断服务程序的入参 | | arg | 无类型指针,传递给中断服务程序的入参 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 设置成功 | | HDF_SUCCESS | 设置GPIO管脚中断成功 |
| 负数 | 设置失败 | | 负数 | 设置GPIO管脚中断失败 |
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**<br> > ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**<br>
> 同一时间,只能为某个GPIO管脚设置一个中断服务函数,如果重复调用GpioSetIrq函数,则之前设置的中断服务函数会被取代。 > 同一时间,只能为某个GPIO管脚设置一个中断服务函数,如果重复调用GpioSetIrq函数,则之前设置的中断服务函数会被取代。
...@@ -261,15 +266,15 @@ int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg); ...@@ -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); int32_t GpioUnsetIrq(uint16_t gpio, void *arg);
``` ```
**表7** GpioUnsetIrq参数和返回值描述 **表 7** GpioUnsetIrq参数和返回值描述
| **参数** | **参数描述** | | **参数** | **参数描述** |
| ---------- | -------------- | | ---------- | -------------- |
| gpio | GPIO管脚号 | | gpio | uint16_t类型,GPIO管脚号 |
| arg | GPIO中断数据 | | arg | 无类型指针,GPIO中断数据 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 取消成功 | | HDF_SUCCESS | 取消GPIO管脚中断成功 |
| 负数 | 取消失败 | | 负数 | 取消GPIO管脚中断失败 |
#### 使能GPIO管脚中断 #### 使能GPIO管脚中断
...@@ -279,14 +284,14 @@ int32_t GpioUnsetIrq(uint16_t gpio, void *arg); ...@@ -279,14 +284,14 @@ int32_t GpioUnsetIrq(uint16_t gpio, void *arg);
int32_t GpioEnableIrq(uint16_t gpio); 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) **注意:**<br> > ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**<br>
> 必须通过此函数使能管脚中断,之前设置的中断服务函数才能被正确响应。 > 必须通过此函数使能管脚中断,之前设置的中断服务函数才能被正确响应。
...@@ -298,51 +303,51 @@ int32_t GpioEnableIrq(uint16_t gpio); ...@@ -298,51 +303,51 @@ int32_t GpioEnableIrq(uint16_t gpio);
```c ```c
int32_t GpioDisableIrq(uint16_t gpio); int32_t GpioDisableIrq(uint16_t gpio);
``` ```
**表9** GpioDisableIrq参数和返回值描述 **表 9** GpioDisableIrq参数和返回值描述
| **参数** | **参数描述** | | **参数** | **参数描述**|
| ---------- | -------------- | | ---------- | -------------- |
| gpio | GPIO管脚号 | | gpio | uint16_t类型,GPIO管脚号 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 禁止成功 | | HDF_SUCCESS | 禁止GPIO管脚中断成功 |
| 负数 | 禁止失败 | | 负数 | 禁止GPIO管脚中断失败 |
中断相关操作示例: 中断相关操作示例:
```c ```c
/* 中断服务函数*/ // 中断服务函数
int32_t MyCallBackFunc(uint16_t gpio, void *data) int32_t MyCallBackFunc(uint16_t gpio, void *data)
{ {
HDF_LOGI("%s: gpio:%u interrupt service in data\n", __func__, gpio); HDF_LOGI("MyCallBackFunc: gpio:%u interrupt service in data.\n", gpio);
return 0; return HDF_SUCCESS;
} }
int32_t ret; int32_t ret;
/* 设置中断服务程序为MyCallBackFunc,入参为NULL,中断触发模式为上升沿触发 */ // 设置中断服务程序为MyCallBackFunc,入参为NULL,中断触发模式为上升沿触发
ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL); ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioSetIrq: failed, ret %d\n", ret); HDF_LOGE("GpioSetIrq: gpio set irq fail, ret:%d\n", ret);
return ret; return ret;
} }
/* 使能3号GPIO管脚中断 */ // 使能3号GPIO管脚中断
ret = GpioEnableIrq(3); ret = GpioEnableIrq(3);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioEnableIrq: failed, ret %d\n", ret); HDF_LOGE("GpioEnableIrq: gpio enable irq fail, ret:%d\n", ret);
return ret; return ret;
} }
/* 禁止3号GPIO管脚中断 */ // 禁止3号GPIO管脚中断
ret = GpioDisableIrq(3); ret = GpioDisableIrq(3);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioDisableIrq: failed, ret %d\n", ret); HDF_LOGE("GpioDisableIrq: gpio disable irqfail, ret:%d\n", ret);
return ret; return ret;
} }
/* 取消3号GPIO管脚中断服务程序 */ // 取消3号GPIO管脚中断服务程序
ret = GpioUnsetIrq(3, NULL); ret = GpioUnsetIrq(3, NULL);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioUnSetIrq: failed, ret %d\n", ret); HDF_LOGE("GpioUnSetIrq: gpio unset irq fail, ret:%d\n", ret);
return ret; return ret;
} }
``` ```
...@@ -363,65 +368,66 @@ if (ret != 0) { ...@@ -363,65 +368,66 @@ if (ret != 0) {
static uint32_t g_irqCnt; static uint32_t g_irqCnt;
/* 中断服务函数*/ // 中断服务函数
static int32_t TestCaseGpioIrqHandler(uint16_t gpio, void *data) static int32_t TestCaseGpioIrqHandler(uint16_t gpio, void *data)
{ {
HDF_LOGE("%s: irq triggered! on gpio:%u, in data", __func__, gpio); HDF_LOGE("TestCaseGpioIrqHandler: irq triggered! on gpio:%u, in data", gpio);
g_irqCnt++; /* 如果中断服务函数触发执行,则将全局中断计数加1 */ g_irqCnt++; // 如果中断服务函数触发执行,则将全局中断计数加1
return GpioDisableIrq(gpio); return GpioDisableIrq(gpio);
} }
/* 测试用例函数 */ // 测试用例函数
static int32_t TestCaseGpioIrqEdge(void) static int32_t TestCaseGpioIrqEdge(void)
{ {
int32_t ret; int32_t ret;
uint16_t valRead; uint16_t valRead;
uint16_t mode; uint16_t mode;
uint16_t gpio = 83; /* 待测试的GPIO管脚号 */ uint16_t gpio = 84; // 待测试的GPIO管脚号
uint32_t timeout; uint32_t timeout;
/* 将管脚方向设置为输出 */ // 将管脚方向设置为输出
ret = GpioSetDir(gpio, GPIO_DIR_OUT); ret = GpioSetDir(gpio, GPIO_DIR_OUT);
if (ret != HDF_SUCCESS) { 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; return ret;
} }
/* 先禁止该管脚中断 */ // 先禁止该管脚中断
ret = GpioDisableIrq(gpio); ret = GpioDisableIrq(gpio);
if (ret != HDF_SUCCESS) { 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; return ret;
} }
/* 为管脚设置中断服务函数,触发模式为上升沿和下降沿共同触发 */ // 为管脚设置中断服务函数,触发模式为上升沿和下降沿共同触发
mode = OSAL_IRQF_TRIGGER_RISING | OSAL_IRQF_TRIGGER_FALLING; 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); ret = GpioSetIrq(gpio, mode, TestCaseGpioIrqHandler, NULL);
if (ret != HDF_SUCCESS) { 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; return ret;
} }
/* 使能此管脚中断 */ // 使能此管脚中断
ret = GpioEnableIrq(gpio); ret = GpioEnableIrq(gpio);
if (ret != HDF_SUCCESS) { 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); (void)GpioUnsetIrq(gpio, NULL);
return ret; return ret;
} }
g_irqCnt = 0; /* 清除全局计数器 */ g_irqCnt = 0; // 清除全局计数器
timeout = 0; /* 等待时间清零 */ timeout = 0; // 等待时间清零
/* 等待此管脚中断服务函数触发,等待超时时间为1000毫秒 */ // 等待此管脚中断服务函数触发,等待超时时间为1000毫秒
while (g_irqCnt <= 0 && timeout < 1000) { while (g_irqCnt <= 0 && timeout < 1000) {
(void)GpioRead(gpio, &valRead); (void)GpioRead(gpio, &valRead);
(void)GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW); (void)GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW);
HDF_LOGE("%s: wait irq timeout:%u\n", __func__, timeout); HDF_LOGE("TestCaseGpioIrqEdge: wait irq timeout:%u\n", timeout);
OsalMDelay(200); /* wait for irq trigger */ OsalMDelay(200); // 等待中断触发
timeout += 200; timeout += 200;
} }
(void)GpioUnsetIrq(gpio, NULL); (void)GpioUnsetIrq(gpio, NULL);
HDF_LOGI("TestCaseGpioIrqEdge: function tests end, g_irqCnt:%u", g_irqCnt);
return (g_irqCnt > 0) ? HDF_SUCCESS : HDF_FAILURE; return (g_irqCnt > 0) ? HDF_SUCCESS : HDF_FAILURE;
} }
``` ```
...@@ -20,15 +20,17 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。 ...@@ -20,15 +20,17 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。
### 运作机制 ### 运作机制
在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。GPIO模块接口适配模式采用统一服务模式(如图1所示)。 在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。GPIO模块采用统一服务模式(如图1所示)。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。 在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
GPIO模块各分层作用: GPIO模块各分层作用:
- 接口层提供操作GPIO管脚的标准方法。 - 接口层:提供操作GPIO管脚的标准方法。
- 核心层主要提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 - 核心层:提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。
- 适配层:由驱动适配者将钩子函数的功能实例化,实现与硬件相关的具体功能。
**图 1** GPIO统一服务模式结构图 **图 1** GPIO统一服务模式结构图
...@@ -38,7 +40,7 @@ GPIO模块各分层作用: ...@@ -38,7 +40,7 @@ GPIO模块各分层作用:
### 场景介绍 ### 场景介绍
GPIO仅是一个软件层面的概念,主要工作是GPIO管脚资源管理。驱动开发者可以使用GPIO模块提供的操作接口,实现对管脚的控制。当驱动开发者需要将GPIO适配到OpenHarmony时,需要进行GPIO驱动适配。下文将介绍如何进行GPIO驱动适配。 GPIO主要是对GPIO管脚资源进行管理。驱动开发者可以使用GPIO模块提供的操作接口,实现对管脚的具体控制。当驱动开发者需要将GPIO适配到OpenHarmony时,需要进行GPIO驱动适配。下文将介绍如何进行GPIO驱动适配。
### 接口说明 ### 接口说明
...@@ -55,22 +57,22 @@ struct GpioMethod { ...@@ -55,22 +57,22 @@ struct GpioMethod {
int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir); 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 (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir);
int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq); // 【预留】 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 (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local);
int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local); int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local);
int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local); int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local);
} }
``` ```
**表1** GpioMethod结构体成员的钩子函数功能说明 **表 1** GpioMethod结构体成员的钩子函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 | | 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- | | -------- | -------- | -------- | -------- | -------- |
| write | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号<br/>val:uint16_t类型,电平传入值 | 无 | HDF_STATUS相关状态 | GPIO引脚写入电平值 | | write | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号<br/>val:uint16_t类型,电平传入值 | 无 | HDF_STATUS相关状态 | GPIO引脚写入电平值 |
| read | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识 | val:uint16_t类型指针,用于传出电平值。 | HDF_STATUS相关状态 | GPIO引脚读取电平值 | | read | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | val:uint16_t类型指针,用于传出电平值。 | HDF_STATUS相关状态 | GPIO引脚读取电平值 |
| setDir | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号<br/>dir:uint16_t类型,管脚方向传入值 | 无 | HDF_STATUS相关状态 | 设置GPIO引脚输入/输出方向 | | setDir | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号<br/>dir:uint16_t类型,管脚方向传入值 | 无 | HDF_STATUS相关状态 | 设置GPIO引脚输入/输出方向 |
| getDir | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | dir:uint16_t类型指针,用于传出管脚方向值 | HDF_STATUS相关状态 | 读GPIO引脚输入/输出方向 | | getDir | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | dir:uint16_t类型指针,用于传出管脚方向值 | HDF_STATUS相关状态 | 读GPIO引脚输入/输出方向 |
| setIrq | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号<br/>mode:uint16_t类型,表示触发模式(边沿或电平)<br/>func:函数指针,中断服务程序;<br/>arg:void指针,中断服务程序入参 | 无 | HDF_STATUS相关状态 | 将GPIO引脚设置为中断模式 | | setIrq | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号<br/>mode:uint16_t类型,表示触发模式(边沿或电平) | 无 | HDF_STATUS相关状态 | 将GPIO引脚设置为中断模式 |
| unsetIrq | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 取消GPIO中断设置 | | unsetIrq | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 取消GPIO中断设置 |
| enableIrq | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 使能GPIO管脚中断 | | enableIrq | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 使能GPIO管脚中断 |
| disableIrq | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 禁止GPIO管脚中断 | | disableIrq | cntlr:结构体指针,核心层GPIO控制器<br/>local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 禁止GPIO管脚中断 |
...@@ -79,18 +81,22 @@ struct GpioMethod { ...@@ -79,18 +81,22 @@ struct GpioMethod {
GPIO模块适配包含以下四个步骤: GPIO模块适配包含以下四个步骤:
- 实例化驱动入口。 - 实例化驱动入口
- 配置属性文件。
- 实例化GPIO控制器对象。 - 配置属性文件
- 驱动调试。
- 实例化GPIO控制器对象
- 驱动调试
### 开发实例 ### 开发实例
下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/gpio/gpio_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/gpio/gpio_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释放驱动资源并退出。
GPIO驱动入口开发参考: GPIO驱动入口开发参考:
...@@ -99,16 +105,29 @@ GPIO模块适配包含以下四个步骤: ...@@ -99,16 +105,29 @@ GPIO模块适配包含以下四个步骤:
struct HdfDriverEntry g_gpioDriverEntry = { struct HdfDriverEntry g_gpioDriverEntry = {
.moduleVersion = 1, .moduleVersion = 1,
.Bind = Pl061GpioBind, // GPIO不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作 .Bind = Pl061GpioBind, // GPIO不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作
.Init = Pl061GpioInit, // 见Init参考 .Init = Pl061GpioInit, // 挂接Gpio模块Init实例化
.Release = Pl061GpioRelease, // 见Release参考 .Release = Pl061GpioRelease, // 挂接Gpio模块Release实例化
.moduleName = "hisi_pl061_driver", // 【必要且需要与HCS文件中里面的moduleName匹配】 .moduleName = "hisi_pl061_driver", // 【必要且需要与HCS文件中里面的moduleName匹配】
}; };
HDF_INIT(g_gpioDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 HDF_INIT(g_gpioDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
``` ```
2. 配置属性文件。 2. 配置属性文件
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以一个GPIO控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在gpio_config.hcs文件中增加对应的器件属性。器件属性值与核心层GpioCntlr成员的默认值或限制范围有密切关系,需要在gpio_config.hcs中配置器件属性。
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以一个GPIO控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息。器件属性值与核心层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 配置参考: - device_info.hcs 配置参考:
...@@ -122,12 +141,18 @@ GPIO模块适配包含以下四个步骤: ...@@ -122,12 +141,18 @@ GPIO模块适配包含以下四个步骤:
priority = 50; priority = 50;
device_gpio :: device { device_gpio :: device {
device0 :: deviceNode { device0 :: deviceNode {
policy = 2;
priority = 10;
permission = 0644;
moduleName = "HDF_PLATFORM_GPIO_MANAGER";
serviceName = "HDF_PLATFORM_GPIO_MANAGER";
}
device1 :: deviceNode {
policy = 0; // 等于0,不需要发布服务 policy = 0; // 等于0,不需要发布服务
priority = 10; // 驱动启动优先级 priority = 10; // 驱动启动优先级
permission = 0644; // 驱动创建设备节点权限 permission = 0644; // 驱动创建设备节点权限
moduleName = "hisi_pl061_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致 moduleName = "hisi_pl061_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致
deviceMatchAttr = "hisilicon_hi35xx_pl061"; // 【必要】用于配置控制器私有数据,要与gpio_config.hcs中 deviceMatchAttr = "hisilicon_hi35xx_pl061"; // 【必要】用于配置控制器私有数据,要与gpio_config.hcs中对应控制器保持一致,其他控制器信息也在文件中
// 对应控制器保持一致,其他控制器信息也在文件中
} }
} }
} }
...@@ -137,7 +162,7 @@ GPIO模块适配包含以下四个步骤: ...@@ -137,7 +162,7 @@ GPIO模块适配包含以下四个步骤:
- gpio_config.hcs配置参考: - gpio_config.hcs配置参考:
在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs文件配置器件属性,其中配置参数如下: 此处以Hi3516DV300为例,给出HCS配置参考。其中部分字段为Hi3516DV300特有功能,驱动适配者可根据需要进行删除或添加字段。
```c ```c
root { root {
...@@ -152,7 +177,13 @@ GPIO模块适配包含以下四个步骤: ...@@ -152,7 +177,13 @@ GPIO模块适配包含以下四个步骤:
irqStart = 48; // 【必要】开启中断 irqStart = 48; // 【必要】开启中断
irqShare = 0; // 【必要】共享中断 irqShare = 0; // 【必要】共享中断
} }
... template gpio_info { // gpio_info模板
gpioCustomName = ""; // gpio管脚默认名称
}
GPIO0 :: gpio_info {
gpioCustomName = "GPIO0_0";
}
......
} }
} }
} }
...@@ -164,7 +195,9 @@ GPIO模块适配包含以下四个步骤: ...@@ -164,7 +195,9 @@ GPIO模块适配包含以下四个步骤:
#include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // 配置文件相对路径 #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // 配置文件相对路径
``` ```
3. 实例化GPIO控制器对象。 本例基于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)。 完成驱动入口注册之后,下一步就是以核心层GpioCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化GpioCntlr成员GpioMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
...@@ -219,7 +252,7 @@ GPIO模块适配包含以下四个步骤: ...@@ -219,7 +252,7 @@ GPIO模块适配包含以下四个步骤:
}; };
``` ```
- GpioCntlr成员钩子函数结构体GpioMethod的实例化,其他成员在Init函数中初始化。 - GpioCntlr成员钩子函数结构体GpioMethod的实例化。
```c ```c
//GpioMethod结构体成员都是钩子函数,驱动适配者需要根据表1完成相应的函数功能。 //GpioMethod结构体成员都是钩子函数,驱动适配者需要根据表1完成相应的函数功能。
...@@ -246,9 +279,9 @@ GPIO模块适配包含以下四个步骤: ...@@ -246,9 +279,9 @@ GPIO模块适配包含以下四个步骤:
返回值: 返回值:
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
**表2** Init函数说明 **表 3** HDF_STATUS相关状态说明
| 状态(值) | 问题描述 | | 状态(值) | 问题描述 |
| -------- | -------- | | -------- | -------- |
...@@ -308,6 +341,11 @@ GPIO模块适配包含以下四个步骤: ...@@ -308,6 +341,11 @@ GPIO模块适配包含以下四个步骤:
(void)OsalSpinDestroy(&groups[i].lock); (void)OsalSpinDestroy(&groups[i].lock);
goto ERR_EXIT; 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; return HDF_SUCCESS;
...@@ -345,17 +383,17 @@ GPIO模块适配包含以下四个步骤: ...@@ -345,17 +383,17 @@ GPIO模块适配包含以下四个步骤:
if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 || if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 ||
pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) { pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) {
HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl061->bitNum); HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl0 61->bitNum);
return HDF_ERR_INVALID_PARAM; return HDF_ERR_INVALID_PARAM;
} }
pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep); //地址映射 pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep); // 地址映射
if (pl061->regBase == NULL) { if (pl061->regBase == NULL) {
HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase); HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase);
return HDF_ERR_IO; return HDF_ERR_IO;
} }
ret = Pl061GpioInitGroups(pl061); //group信息初始化,并添加到HDF核心层 ret = Pl061GpioInitGroups(pl061); // group信息初始化,并添加到HDF核心层
if (ret != HDF_SUCCESS) { if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: err init groups:%d", __func__, ret); HDF_LOGE("%s: err init groups:%d", __func__, ret);
OsalIoUnmap((void *)pl061->regBase); OsalIoUnmap((void *)pl061->regBase);
...@@ -437,6 +475,6 @@ GPIO模块适配包含以下四个步骤: ...@@ -437,6 +475,6 @@ GPIO模块适配包含以下四个步骤:
} }
``` ```
4. 驱动调试 4. 驱动调试
【可选】针对新增驱动程序,建议验证驱动基本功能,例如GPIO控制状态,中断响应情况等。 【可选】针对新增驱动程序,建议验证驱动基本功能,例如GPIO控制状态,中断响应情况等。
# HDMI # HDMI
## 概述 ## 概述
### 功能简介 ### 功能简介
HDMI(High Definition Multimedia Interface),即高清多媒体接口,主要用于DVD、机顶盒等音视频Source到TV、显示器等Sink设备的传输。 HDMI(High Definition Multimedia Interface),即高清多媒体接口,主要用于DVD、机顶盒等音视频Source到TV、显示器等Sink设备的传输。
HDMI以主从方式工作,通常有一个Source端和一个Sink端。 HDMI以主从方式工作,通常有一个Source端和一个Sink端。
HDMI接口定义了完成HDMI传输的通用方法集合,包括: HDMI接口定义了完成HDMI传输的通用方法集合,包括:
- HDMI控制器管理:打开或关闭HDMI控制器 - HDMI控制器管理:打开或关闭HDMI控制器
- HDMI启动/停止传输:启动或停止HDMI传输 - HDMI启动/停止传输:启动或停止HDMI传输
- HDMI控制器设置:设置音频、视频及HDR属性,设置色彩深度、声音图像消隐等 - HDMI控制器设置:设置音频、视频及HDR属性,设置色彩深度、声音图像消隐等
- HDMI读取EDID:读取Sink端原始的EDID数据 - HDMI读取EDID:读取Sink端原始的EDID数据
- HDMI热插拔:注册/注销热插拔回调函数 - HDMI热插拔:注册/注销热插拔回调函数
### 基本概念 ### 基本概念
...@@ -20,25 +25,50 @@ HDMI接口定义了完成HDMI传输的通用方法集合,包括: ...@@ -20,25 +25,50 @@ HDMI接口定义了完成HDMI传输的通用方法集合,包括:
HDMI是Hitachi、Panasonic、Philips、Silicon Image、Sony、Thomson、Toshiba共同发布的一款音视频传输协议。传输过程遵循TMDS(Transition Minimized Differential Signaling)协议。 HDMI是Hitachi、Panasonic、Philips、Silicon Image、Sony、Thomson、Toshiba共同发布的一款音视频传输协议。传输过程遵循TMDS(Transition Minimized Differential Signaling)协议。
- TMDS(Transition Minimized Differential signal):过渡调制差分信号,也被称为最小化传输差分信号,用于发送音频、视频及各种辅助数据。 - TMDS(Transition Minimized Differential signal):过渡调制差分信号,也被称为最小化传输差分信号,用于发送音频、视频及各种辅助数据。
- DDC(Display Data Channel):显示数据通道,发送端与接收端可利用DDC通道得知彼此的发送与接收能力,但HDMI仅需单向获知接收端(显示器)的能力。 - DDC(Display Data Channel):显示数据通道,发送端与接收端可利用DDC通道得知彼此的发送与接收能力,但HDMI仅需单向获知接收端(显示器)的能力。
- CEC(Consumer Electronics Control):消费电子控制,该功能应该能够在连接HDMI的发送设备与接收设备之间实现交互操作。 - CEC(Consumer Electronics Control):消费电子控制,该功能应该能够在连接HDMI的发送设备与接收设备之间实现交互操作。
- FRL(Fixed Rate Link):TMDS 的架构进行讯号传输时,最高带宽可达 18Gbps,而FRL模式的带宽则提升到48 Gbps。 - FRL(Fixed Rate Link):TMDS 的架构进行讯号传输时,最高带宽可达 18Gbps,而FRL模式的带宽则提升到48 Gbps。
- HDCP(High-bandwidth Digital Content Protection):即高带宽数字内容保护技术,当用户对高清晰信号进行非法复制时,该技术会进行干扰,降低复制出来的影像的质量,从而对内容进行保护。 - HDCP(High-bandwidth Digital Content Protection):即高带宽数字内容保护技术,当用户对高清晰信号进行非法复制时,该技术会进行干扰,降低复制出来的影像的质量,从而对内容进行保护。
- EDID(Extended Display Identification Data):扩展显示标识数据,通常存储在显示器的固件中,标识供应商信息、EDID版本信息、最大图像大小、颜色设置、厂商预设置、频率范围的限制以及显示器名和序列号的字符串。 - 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的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,8 +78,9 @@ HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同 ...@@ -48,8 +78,9 @@ HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同
### 接口说明 ### 接口说明
**表 1** HDMI驱动API接口功能介绍 HDMI模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/framework/include/platform/hdmi_if.h。
**表 1** HDMI驱动API接口功能介绍
| 接口名 | 描述 | | 接口名 | 描述 |
| ----------------------------- | -------------------------- | | ----------------------------- | -------------------------- |
...@@ -69,11 +100,11 @@ HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同 ...@@ -69,11 +100,11 @@ HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同
### 开发步骤 ### 开发步骤
使用HDMI设备的一般流程如图2所示。 使用HDMI设备的一般流程如图3所示。
**图 2** HDMI设备使用流程图 **图 3** HDMI设备使用流程图
![](figures/HDMI使用流程图.png "HDMI使用流程图") ![HDMI设备使用流程图](figures/HDMI使用流程图.png)
#### 打开HDMI控制器 #### 打开HDMI控制器
...@@ -87,7 +118,7 @@ DevHandle HdmiOpen(int16_t number); ...@@ -87,7 +118,7 @@ DevHandle HdmiOpen(int16_t number);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------------- | | ---------- | -------------------- |
| number | HDMI控制器号 | | number | int16_t类型,HDMI控制器号 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| NULL | 打开HDMI控制器失败 | | NULL | 打开HDMI控制器失败 |
| 控制器句柄 | 打开的HDMI控制器句柄 | | 控制器句柄 | 打开的HDMI控制器句柄 |
...@@ -95,13 +126,13 @@ DevHandle HdmiOpen(int16_t number); ...@@ -95,13 +126,13 @@ DevHandle HdmiOpen(int16_t number);
假设系统中存在2个HDMI控制器,编号从0到1,以下代码示例为获取0号控制器: 假设系统中存在2个HDMI控制器,编号从0到1,以下代码示例为获取0号控制器:
```c ```c
DevHandle hdmiHandle = NULL; /* HDMI控制器句柄 / DevHandle hdmiHandle = NULL; // HDMI控制器句柄
/* 打开HDMI控制器 */ // 打开HDMI控制器
hdmiHandle = HdmiOpen(0); hdmiHandle = HdmiOpen(0);
if (hdmiHandle == NULL) { if (hdmiHandle == NULL) {
HDF_LOGE("HdmiOpen: failed\n"); HDF_LOGE("HdmiOpen: hdmi open fail!\n");
return; return NULL;
} }
``` ```
...@@ -115,16 +146,16 @@ int32_t HdmiRegisterHpdCallbackFunc(DevHandle handle, struct HdmiHpdCallbackInfo ...@@ -115,16 +146,16 @@ int32_t HdmiRegisterHpdCallbackFunc(DevHandle handle, struct HdmiHpdCallbackInfo
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | ------------------ | | ---------- | ------------------ |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| callback | 热插拔回调函数信息 | | callback | 结构体指针,热插拔回调函数信息 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 注册成功 | | HDF_SUCCESS | 注册成功 |
| 负数 | 注册失败 | | 负数 | 注册失败 |
注册热插拔检测回调函数示例: 注册热插拔检测回调函数示例:
```c ```c
/* 热插拔检测回调函数定义 */ // 热插拔检测回调函数定义
static void HdmiHpdHandle(void *data, bool hpd) static void HdmiHpdHandle(void *data, bool hpd)
{ {
if (data == NULL) { if (data == NULL) {
...@@ -133,23 +164,22 @@ static void HdmiHpdHandle(void *data, bool hpd) ...@@ -133,23 +164,22 @@ static void HdmiHpdHandle(void *data, bool hpd)
} }
if (hpd == true) { if (hpd == true) {
HDF_LOGD("HdmiHpdHandle: hot plug"); HDF_LOGD("HdmiHpdHandle: hot plug");
/* 调用者添加相关处理 */ // 调用者添加相关处理
} else { } else {
HDF_LOGD("HdmiHpdHandle: hot unplug"); HDF_LOGD("HdmiHpdHandle: hot unplug");
/* 调用者添加相关处理 */ // 调用者添加相关处理
} }
} }
/* 热插拔检测回调函数注册示例 */ // 热插拔检测回调函数注册示例
···
struct HdmiHpdCallbackInfo info = {0}; struct HdmiHpdCallbackInfo info = {0};
info.data = handle; info.data = handle;
info.callbackFunc = HdmiHpdHandle; info.callbackFunc = HdmiHpdHandle;
ret = HdmiRegisterHpdCallbackFunc(hdmiHandle, info); ret = HdmiRegisterHpdCallbackFunc(hdmiHandle, info);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register failed."); HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register hpd callback func fail, ret:%d", ret);
return ret;
} }
···
``` ```
#### 读取EDID #### 读取EDID
...@@ -162,9 +192,9 @@ int32_t HdmiReadSinkEdid(DevHandle handle, uint8_t *buffer, uint32_t len); ...@@ -162,9 +192,9 @@ int32_t HdmiReadSinkEdid(DevHandle handle, uint8_t *buffer, uint32_t len);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | ---------------------- | | ---------- | ---------------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| buffer | 数据缓冲区 | | buffer | uint8_t类型指针,数据缓冲区 |
| len | 数据长度 | | len | uint32_t类型,数据长度 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 正整数 | 成功读取的原始EDID数据 | | 正整数 | 成功读取的原始EDID数据 |
| 负数或0 | 读取失败 | | 负数或0 | 读取失败 |
...@@ -177,7 +207,8 @@ uint8_t edid[HDMI_EDID_MAX_LEN] = {0}; ...@@ -177,7 +207,8 @@ uint8_t edid[HDMI_EDID_MAX_LEN] = {0};
len = HdmiReadSinkEdid(hdmiHandle, edid, HDMI_EDID_MAX_LEN); len = HdmiReadSinkEdid(hdmiHandle, edid, HDMI_EDID_MAX_LEN);
if (len <= 0) { 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;
} }
``` ```
...@@ -189,13 +220,12 @@ int32_t HdmiSetAudioAttribute(DevHandle handle, struct HdmiAudioAttr *attr); ...@@ -189,13 +220,12 @@ int32_t HdmiSetAudioAttribute(DevHandle handle, struct HdmiAudioAttr *attr);
**表 5** HdmiSetAudioAttribute参数和返回值描述 **表 5** HdmiSetAudioAttribute参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ------ | -------------- | | ------ | -------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| attr | 音频属性 | | attr | 结构体指针,音频属性 |
| 返回值 | 返回值描述 | | 返回值 | 返回值描述 |
| 0 | 设置成功 | | HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 | | 负数 | 设置失败 |
设置音频属性示例: 设置音频属性示例:
...@@ -210,8 +240,9 @@ audioAttr.bitDepth = HDMI_ADIO_BIT_DEPTH_16; ...@@ -210,8 +240,9 @@ audioAttr.bitDepth = HDMI_ADIO_BIT_DEPTH_16;
audioAttr.sampleRate = HDMI_SAMPLE_RATE_8K; audioAttr.sampleRate = HDMI_SAMPLE_RATE_8K;
audioAttr.channels = HDMI_AUDIO_FORMAT_CHANNEL_3; audioAttr.channels = HDMI_AUDIO_FORMAT_CHANNEL_3;
ret = HdmiSetAudioAttribute(handle, &audioAttr); ret = HdmiSetAudioAttribute(handle, &audioAttr);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("HdmiSetAudioAttribute failed."); HDF_LOGE("HdmiSetAudioAttribute: hdmi set audio attribute fail!, ret:%d", ret);
return ret;
} }
``` ```
...@@ -223,13 +254,12 @@ int32_t HdmiSetVideoAttribute(DevHandle handle, struct HdmiVideoAttr *attr); ...@@ -223,13 +254,12 @@ int32_t HdmiSetVideoAttribute(DevHandle handle, struct HdmiVideoAttr *attr);
**表 6** HdmiSetVideoAttribute参数和返回值描述 **表 6** HdmiSetVideoAttribute参数和返回值描述
| 参数| 参数描述|
| 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| attr | 视频属性 | | attr | 结构体指针,视频属性 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 设置成功 | | HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 | | 负数 | 设置失败 |
设置视频属性示例: 设置视频属性示例:
...@@ -243,8 +273,9 @@ videoAttr.colorimetry = HDMI_COLORIMETRY_EXTENDED; ...@@ -243,8 +273,9 @@ videoAttr.colorimetry = HDMI_COLORIMETRY_EXTENDED;
videoAttr.extColorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM; videoAttr.extColorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM;
videoAttr.quantization = HDMI_QUANTIZATION_RANGE_FULL; videoAttr.quantization = HDMI_QUANTIZATION_RANGE_FULL;
ret = HdmiSetVideoAttribute(handle, &videoAttr); ret = HdmiSetVideoAttribute(handle, &videoAttr);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("HdmiSetVideoAttribute failed."); HDF_LOGE("HdmiSetVideoAttribute: hdmi set video attribute fail, ret:%d.", ret);
return ret;
} }
``` ```
...@@ -256,13 +287,12 @@ int32_t HdmiSetHdrAttribute(DevHandle handle, struct HdmiHdrAttr *attr); ...@@ -256,13 +287,12 @@ int32_t HdmiSetHdrAttribute(DevHandle handle, struct HdmiHdrAttr *attr);
**表 7** HdmiSetHdrAttribute参数和返回值描述 **表 7** HdmiSetHdrAttribute参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| attr | HDR属性 | | attr | 结构体指针,HDR属性 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 设置成功 | | HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 | | 负数 | 设置失败 |
设置HDR属性示例: 设置HDR属性示例:
...@@ -277,8 +307,9 @@ hdrAttr.eotfType = HDMI_EOTF_SMPTE_ST_2048; ...@@ -277,8 +307,9 @@ hdrAttr.eotfType = HDMI_EOTF_SMPTE_ST_2048;
hdrAttr.metadataType = HDMI_DRM_STATIC_METADATA_TYPE_1; hdrAttr.metadataType = HDMI_DRM_STATIC_METADATA_TYPE_1;
hdrAttr.colorimetry = HDMI_HDR_EXTENDED_COLORIMETRY_XV_YCC_709; hdrAttr.colorimetry = HDMI_HDR_EXTENDED_COLORIMETRY_XV_YCC_709;
ret = HdmiSetHdrAttribute(handle, &hdrAttr); ret = HdmiSetHdrAttribute(handle, &hdrAttr);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("HdmiSetHdrAttribute failed."); HDF_LOGE("HdmiSetHdrAttribute: hdmi set hdr attribute fail, ret:%d", ret);
return ret;
} }
``` ```
...@@ -290,13 +321,12 @@ int32_t HdmiAvmuteSet(DevHandle handle, bool enable); ...@@ -290,13 +321,12 @@ int32_t HdmiAvmuteSet(DevHandle handle, bool enable);
**表 8** HdmiAvmuteSet参数和返回值描述 **表 8** HdmiAvmuteSet参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | ----------------- | | ---------- | ----------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| enable | 使能/去使能avmute | | enable | 布尔值,使能/去使能avmute |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 设置成功 | | HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 | | 负数 | 设置失败 |
设置声音图像消隐示例: 设置声音图像消隐示例:
...@@ -305,8 +335,9 @@ int32_t HdmiAvmuteSet(DevHandle handle, bool enable); ...@@ -305,8 +335,9 @@ int32_t HdmiAvmuteSet(DevHandle handle, bool enable);
int32_t ret; int32_t ret;
ret = HdmiAvmuteSet(hdmiHandle, true); ret = HdmiAvmuteSet(hdmiHandle, true);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("HdmiAvmuteSet failed."); HDF_LOGE("HdmiAvmuteSet: hdmi avmute set fail, ret:%d", ret);
return ret;
} }
``` ```
...@@ -318,13 +349,12 @@ int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color); ...@@ -318,13 +349,12 @@ int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color);
**表 9** HdmiDeepColorSet参数和返回值描述 **表 9** HdmiDeepColorSet参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| color | 色彩深度 | | color | 枚举类型,色彩深度 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 设置成功 | | HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 | | 负数 | 设置失败 |
设置色彩深度示例: 设置色彩深度示例:
...@@ -333,8 +363,9 @@ int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color); ...@@ -333,8 +363,9 @@ int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color);
int32_t ret; int32_t ret;
ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS); ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("HdmiDeepColorSet failed."); HDF_LOGE("HdmiDeepColorSet: hdmi deep color set fail, ret:%d.", ret);
return ret;
} }
``` ```
...@@ -346,13 +377,12 @@ int32_t HdmiDeepColorGet(DevHandle handle, enum HdmiDeepColor *color); ...@@ -346,13 +377,12 @@ int32_t HdmiDeepColorGet(DevHandle handle, enum HdmiDeepColor *color);
**表 10** HdmiDeepColorGet参数和返回值描述 **表 10** HdmiDeepColorGet参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| color | 色彩深度 | | color | 枚举类型指针,色彩深度 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 获取成功 | | HDF_SUCCESS | 获取成功 |
| 负数 | 获取失败 | | 负数 | 获取失败 |
获取色彩深度示例: 获取色彩深度示例:
...@@ -362,8 +392,9 @@ enum HdmiDeepColor color; ...@@ -362,8 +392,9 @@ enum HdmiDeepColor color;
int32_t ret; int32_t ret;
ret = HdmiDeepColorGet(handle, &color); ret = HdmiDeepColorGet(handle, &color);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("HdmiDeepColorGet failed."); HDF_LOGE("HdmiDeepColorGet: hdmi deep color get fail, ret:%d", ret);
return ret;
} }
``` ```
...@@ -375,12 +406,11 @@ int32_t HdmiStart(DevHandle handle); ...@@ -375,12 +406,11 @@ int32_t HdmiStart(DevHandle handle);
**表 11** HdmiStart参数和返回值描述 **表 11** HdmiStart参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 启动成功 | | HDF_SUCCESS | 启动成功 |
| 负数 | 启动失败 | | 负数 | 启动失败 |
启动HDMI传输示例: 启动HDMI传输示例:
...@@ -389,8 +419,9 @@ int32_t HdmiStart(DevHandle handle); ...@@ -389,8 +419,9 @@ int32_t HdmiStart(DevHandle handle);
int32_t ret; int32_t ret;
ret = HdmiStart(hdmiHandle); ret = HdmiStart(hdmiHandle);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("start transmission failed."); HDF_LOGE("HdmiStart: start transmission fail, ret:%d", ret);
return ret;
} }
``` ```
...@@ -402,12 +433,11 @@ int32_t HdmiStop(DevHandle handle); ...@@ -402,12 +433,11 @@ int32_t HdmiStop(DevHandle handle);
**表 12** HdmiStop参数和返回值描述 **表 12** HdmiStop参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 停止成功 | | HDF_SUCCESS | 停止成功 |
| 负数 | 停止失败 | | 负数 | 停止失败 |
停止HDMI传输示例: 停止HDMI传输示例:
...@@ -416,8 +446,9 @@ int32_t HdmiStop(DevHandle handle); ...@@ -416,8 +446,9 @@ int32_t HdmiStop(DevHandle handle);
int32_t ret; int32_t ret;
ret = HdmiStop(hdmiHandle); ret = HdmiStop(hdmiHandle);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("stop transmission failed."); HDF_LOGE("HdmiStop: stop transmission fail, ret:%d.", ret);
return ret;
} }
``` ```
...@@ -429,12 +460,11 @@ int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle); ...@@ -429,12 +460,11 @@ 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); ...@@ -443,8 +473,9 @@ int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle);
int32_t ret; int32_t ret;
ret = HdmiUnregisterHpdCallbackFunc(hdmiHandle); ret = HdmiUnregisterHpdCallbackFunc(hdmiHandle);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("unregister failed."); HDF_LOGE("HdmiUnregisterHpdCallbackFunc:unregister fail, ret:%d.", ret);
return ret;
} }
``` ```
...@@ -456,10 +487,9 @@ void HdmiClose(DevHandle handle); ...@@ -456,10 +487,9 @@ void HdmiClose(DevHandle handle);
**表 14** HdmiClose参数和返回值描述 **表 14** HdmiClose参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | HDMI控制器句柄 | | handle | DevHandle类型,HDMI控制器句柄 |
关闭HDMI控制器示例: 关闭HDMI控制器示例:
......
# HDMI # HDMI
## 概述 ## 概述
### 功能简介 ### 功能简介
...@@ -19,18 +18,25 @@ HDMI(High Definition Multimedia Interface),即高清多媒体接口,是H ...@@ -19,18 +18,25 @@ HDMI(High Definition Multimedia Interface),即高清多媒体接口,是H
- HDCP(High-bandwidth Digital Content Protection):即高带宽数字内容保护技术,当用户对高清晰信号进行非法复制时,该技术会进行干扰,降低复制出来的影像的质量,从而对内容进行保护。 - 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具有体积小、传输速率高、传输带宽宽、兼容性好、能同 ...@@ -41,6 +47,7 @@ HDMI具有体积小、传输速率高、传输带宽宽、兼容性好、能同
### 接口说明 ### 接口说明
HdmiCntlrOps定义: HdmiCntlrOps定义:
```c ```c
struct HdmiCntlrOps { struct HdmiCntlrOps {
void (*hardWareInit)(struct HdmiCntlr *cntlr); void (*hardWareInit)(struct HdmiCntlr *cntlr);
...@@ -80,7 +87,7 @@ struct HdmiCntlrOps { ...@@ -80,7 +87,7 @@ struct HdmiCntlrOps {
}; };
``` ```
**表1** HdmiCntlrOps结构体成员的回调函数功能说明 **表 1** HdmiCntlrOps结构体成员的回调函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 | | 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| ------------------------ | ------------------------------------------------------------ | -------------------------------------- | ------------------ | -------------------------------------------------- | | ------------------------ | ------------------------------------------------------------ | -------------------------------------- | ------------------ | -------------------------------------------------- |
...@@ -120,20 +127,30 @@ struct HdmiCntlrOps { ...@@ -120,20 +127,30 @@ struct HdmiCntlrOps {
### 开发步骤 ### 开发步骤
HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以及实例化HDMI控制器对象。 HDMI模块适配包含以下四个步骤:
- 实例化驱动入口
- 实例化驱动入口:
- 实例化HdfDriverEntry结构体成员。 - 实例化HdfDriverEntry结构体成员。
- 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
- 配置属性文件: - 配置属性文件
- 在device_info.hcs文件中添加deviceNode描述。 - 在device_info.hcs文件中添加deviceNode描述。
- 【可选】添加hdmi_config.hcs器件属性文件。 - 【可选】添加hdmi_config.hcs器件属性文件。
- 实例化HDMI控制器对象: - 实例化HDMI控制器对象
- 初始化HdmiCntlr成员。 - 初始化HdmiCntlr成员。
- 实例化HdmiCntlr成员HdmiCntlrOps方法集合。 - 实例化HdmiCntlr成员HdmiCntlrOps方法集合。
- 驱动调试
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,HDMI传输等。
1. 实例化驱动入口 1. 实例化驱动入口
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
...@@ -148,16 +165,14 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 ...@@ -148,16 +165,14 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
.Bind = HdmiAdapterBind, .Bind = HdmiAdapterBind,
.Init = HdmiAdapterInit, .Init = HdmiAdapterInit,
.Release = HdmiAdapterRelease, .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. 配置属性文件 2. 配置属性文件
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在hdmi_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层HdmiCntlr相关成员的默认值或限制范围有密切关系。 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在hdmi_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层HdmiCntlr相关成员的默认值或限制范围有密切关系。从第一个节点开始配置具体HDMI控制器信息,此节点并不表示某一路HDMI控制器,而是代表一个资源性质设备,用于描述一类HDMI控制器的信息。本例只有一个HDMI控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在hdmi_config文件中增加对应的器件属性。
从第一个节点开始配置具体HDMI控制器信息,此节点并不表示某一路HDMI控制器,而是代表一个资源性质设备,用于描述一类HDMI控制器的信息。本例只有一个HDMI控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在hdmi_config文件中增加对应的器件属性。
- device_info.hcs配置参考 - device_info.hcs配置参考
...@@ -227,7 +242,7 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 ...@@ -227,7 +242,7 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
} }
``` ```
3. 实例化控制器对象 3. 实例化HDMI控制器对象
最后一步,完成驱动入口注册之后,要以核心层HdmiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化HdmiCntlr成员HdmiCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 最后一步,完成驱动入口注册之后,要以核心层HdmiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化HdmiCntlr成员HdmiCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
...@@ -245,7 +260,6 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 ...@@ -245,7 +260,6 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
uint32_t irqNum; //【必要】中断号 uint32_t irqNum; //【必要】中断号
}; };
/* HdmiCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。 */
struct HdmiCntlr { struct HdmiCntlr {
struct IDeviceIoService service; struct IDeviceIoService service;
struct HdfDeviceObject *hdfDevObj; struct HdfDeviceObject *hdfDevObj;
...@@ -320,16 +334,17 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 ...@@ -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_OBJECT | 控制器对象非法 |
|HDF_ERR_INVALID_PARAM |参数非法| | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
|HDF_ERR_MALLOC_FAIL |内存分配失败| | HDF_ERR_IO | I/O&nbsp;错误 |
|HDF_ERR_IO |I/O错误| | HDF_SUCCESS | 初始化成功 |
|HDF_SUCCESS |传输成功| | HDF_FAILURE | 初始化失败 |
|HDF_FAILURE |传输失败|
**函数说明:** **函数说明:**
...@@ -358,13 +373,13 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 ...@@ -358,13 +373,13 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
cntlr->hdfDevObj = obj; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提 cntlr->hdfDevObj = obj; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提
obj->service = &cntlr->service; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提 obj->service = &cntlr->service; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提
ret = HdmiAdapterCntlrParse(cntlr, obj); //【必要】初始化cntlr,失败则goto __ERR。 ret = HdmiAdapterCntlrParse(cntlr, obj); //【必要】初始化cntlr,失败则goto __ERR。
... ......
ret = HdmiAdapterHostParse(host, obj); //【必要】初始化host对象的相关属性,失败则goto __ERR。 ret = HdmiAdapterHostParse(host, obj); //【必要】初始化host对象的相关属性,失败则goto __ERR。
... ......
ret = HdmiAdapterHostInit(host, cntlr); // 厂商自定义的初始化,失败则goto __ERR。 ret = HdmiAdapterHostInit(host, cntlr); // 厂商自定义的初始化,失败则goto __ERR。
... ......
ret = HdmiCntlrAdd(cntlr); // 调用核心层函数,失败则goto __ERR。 ret = HdmiCntlrAdd(cntlr); // 调用核心层函数,失败则goto __ERR。
... ......
HDF_LOGD("HdmiAdapterBind: success."); HDF_LOGD("HdmiAdapterBind: success.");
return HDF_SUCCESS; return HDF_SUCCESS;
__ERR: __ERR:
...@@ -416,11 +431,13 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 ...@@ -416,11 +431,13 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以
static void HdmiAdapterRelease(struct HdfDeviceObject *obj) static void HdmiAdapterRelease(struct HdfDeviceObject *obj)
{ {
struct HdmiCntlr *cntlr = NULL; struct HdmiCntlr *cntlr = NULL;
... ......
cntlr = (struct HdmiCntlr *)obj->service; // 这里有HdfDeviceObject到HdmiCntlr的强制转化,通过service成员,赋值见Bind函数。 cntlr = (struct HdmiCntlr *)obj->service; // 这里有HdfDeviceObject到HdmiCntlr的强制转化,通过service成员,赋值见Bind函数。
... ......
HimciDeleteHost((struct HimciAdapterHost *)cntlr->priv);// 厂商自定义的内存释放函数,这里有HdmiCntlr到HimciAdapterHost的强制转化。 HimciDeleteHost((struct HimciAdapterHost *)cntlr->priv);// 厂商自定义的内存释放函数,这里有HdmiCntlr到HimciAdapterHost的强制转化。
} }
``` ```
4. 驱动调试
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,HDMI传输等。
# I2C # I2C
## 概述 ## 概述
### 功能简介 ### 功能简介
...@@ -16,12 +15,14 @@ I2C数据的传输必须以一个起始信号作为开始条件,以一个结 ...@@ -16,12 +15,14 @@ I2C数据的传输必须以一个起始信号作为开始条件,以一个结
I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址,当主设备需要和某一个从设备通信时,通过广播的方式,将从设备地址写到总线上,如果某个从设备符合此地址,将会发出应答信号,建立传输。 I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址,当主设备需要和某一个从设备通信时,通过广播的方式,将从设备地址写到总线上,如果某个从设备符合此地址,将会发出应答信号,建立传输。
I2C接口定义了完成I2C传输的通用方法集合,包括: I2C接口定义了完成I2C传输的通用方法集合,包括:
- I2C控制器管理:打开或关闭I2C控制器 - I2C控制器管理:打开或关闭I2C控制器
- I2C消息传输:通过消息传输结构体数组进行自定义传输 - I2C消息传输:通过消息传输结构体数组进行自定义传输
**图1** I2C物理连线示意图 **图 1** I2C物理连线示意图
![image](figures/I2C物理连线示意图.png "I2C物理连线示意图") ![I2C物理连线示意图](figures/I2C物理连线示意图.png)
## 使用指导 ## 使用指导
...@@ -33,7 +34,7 @@ I2C通常用于与各类支持I2C协议的传感器、执行器或输入输出 ...@@ -33,7 +34,7 @@ I2C通常用于与各类支持I2C协议的传感器、执行器或输入输出
I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i2c_if.h。 I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i2c_if.h。
**表1** I2C驱动API接口功能介绍 **表 1** I2C驱动API接口功能介绍
| 接口名 | 接口描述 | | 接口名 | 接口描述 |
| -------- | -------- | | -------- | -------- |
...@@ -45,9 +46,9 @@ I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ ...@@ -45,9 +46,9 @@ I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
使用I2C设备的一般流程如下图所示。 使用I2C设备的一般流程如下图所示。
**图2** I2C设备使用流程图 **图 2** I2C设备使用流程图
![image](figures/I2C设备使用流程图.png "I2C设备使用流程图") ![I2C设备使用流程图](figures/I2C设备使用流程图.png)
#### 打开I2C控制器 #### 打开I2C控制器
...@@ -58,11 +59,11 @@ I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ ...@@ -58,11 +59,11 @@ I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
DevHandle I2cOpen(int16_t number); DevHandle I2cOpen(int16_t number);
``` ```
**表2** I2cOpen参数和返回值描述 **表 2** I2cOpen参数和返回值描述
| **参数** | **参数描述** | | **参数** | **参数描述** |
| -------- | -------- | | -------- | -------- |
| number | I2C控制器号 | | number | int16_t类型,I2C控制器号 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| NULL | 打开I2C控制器失败 | | NULL | 打开I2C控制器失败 |
| 设备句柄 | 打开的I2C控制器设备句柄 | | 设备句柄 | 打开的I2C控制器设备句柄 |
...@@ -70,57 +71,55 @@ DevHandle I2cOpen(int16_t number); ...@@ -70,57 +71,55 @@ DevHandle I2cOpen(int16_t number);
假设系统中存在8个I2C控制器,编号从0到7,以下代码示例为获取3号控制器: 假设系统中存在8个I2C控制器,编号从0到7,以下代码示例为获取3号控制器:
```c ```c
DevHandle i2cHandle = NULL; /* I2C控制器句柄 / DevHandle i2cHandle = NULL; // I2C控制器句柄
/* 打开I2C控制器 */ // 打开I2C控制器
i2cHandle = I2cOpen(3); i2cHandle = I2cOpen(3);
if (i2cHandle == NULL) { if (i2cHandle == NULL) {
HDF_LOGE("I2cOpen: failed\n"); HDF_LOGE("I2cOpen: i2c open fail.\n");
return; return NULL;
} }
``` ```
#### 进行I2C通信 #### 进行I2C通信
消息传输 消息传输
```c ```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控制器设备句柄 | | handle | DevHandle类型,I2C控制器设备句柄 |
| msgs | 待传输数据的消息结构体数组 | | msgs | 结构体指针,待传输数据的消息结构体数组 |
| count | 消息数组长度 | | count | int16_t类型,消息数组长度 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 正整数 | 成功传输的消息结构体数目 | | 正整数 | 成功传输的消息结构体数目 |
| 负数 | 执行失败 | | 负数 | 执行失败 |
I2C传输消息类型为I2cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。组合读写示例: I2C传输消息类型为I2cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。组合读写示例:
```c ```c
int32_t ret; int32_t ret;
uint8_t wbuff[2] = { 0x12, 0x13 }; uint8_t wbuff[2] = { 0x12, 0x13 };
uint8_t rbuff[2] = { 0 }; uint8_t rbuff[2] = { 0 };
struct I2cMsg msgs[2]; /* 自定义传输的消息结构体数组 */ struct I2cMsg msgs[2]; // 自定义传输的消息结构体数组
msgs[0].buf = wbuff; /* 写入的数据 */ msgs[0].buf = wbuff; // 写入的数据
msgs[0].len = 2; /* 写入数据长度为2 */ msgs[0].len = 2; // 写入数据长度为2
msgs[0].addr = 0x5A; /* 写入设备地址为0x5A */ msgs[0].addr = 0x5A; // 写入设备地址为0x5A
msgs[0].flags = 0; /* 传输标记为0,默认为写 */ msgs[0].flags = 0; // 传输标记为0,默认为写
msgs[1].buf = rbuff; /* 要读取的数据 */ msgs[1].buf = rbuff; // 要读取的数据
msgs[1].len = 2; /* 读取数据长度为2 */ msgs[1].len = 2; // 读取数据长度为2
msgs[1].addr = 0x5A; /* 读取设备地址为0x5A */ msgs[1].addr = 0x5A; // 读取设备地址为0x5A
msgs[1].flags = I2C_FLAG_READ /* I2C_FLAG_READ置位 */ msgs[1].flags = I2C_FLAG_READ // I2C_FLAG_READ置位
/* 进行一次自定义传输,传输的消息个数为2 */ // 进行一次自定义传输,传输的消息个数为2
ret = I2cTransfer(i2cHandle, msgs, 2); ret = I2cTransfer(i2cHandle, msgs, 2);
if (ret != 2) { if (ret != 2) {
HDF_LOGE("I2cTransfer: failed, ret %d\n", ret); HDF_LOGE("I2cTransfer: i2c transfer fail, ret:%d\n", ret);
return; return HDF_FAILURE;
} }
``` ```
...@@ -142,19 +141,18 @@ I2C通信完成之后,需要关闭I2C控制器,关闭函数如下所述: ...@@ -142,19 +141,18 @@ I2C通信完成之后,需要关闭I2C控制器,关闭函数如下所述:
void I2cClose(DevHandle handle); void I2cClose(DevHandle handle);
``` ```
**表4** I2cClose参数和返回值描述 **表 4** I2cClose参数和返回值描述
| 参数 | 参数描述 | | 参数 | 参数描述 |
| -------- | -------- | | -------- | -------- |
| handle | I2C控制器设备句柄 | | handle | DevHandle类型,I2C控制器设备句柄 |
关闭I2C控制器示例: 关闭I2C控制器示例:
```c ```c
I2cClose(i2cHandle); /* 关闭I2C控制器 */ I2cClose(i2cHandle); // 关闭I2C控制器
``` ```
### 使用示例 ### 使用示例
本例程以操作开发板上的I2C设备为例,详细展示I2C接口的完整使用流程。 本例程以操作开发板上的I2C设备为例,详细展示I2C接口的完整使用流程。
......
...@@ -15,11 +15,14 @@ I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单 ...@@ -15,11 +15,14 @@ I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单
I2C模块各分层的作用为: I2C模块各分层的作用为:
- 接口层:提供打开设备,数据传输以及关闭设备的能力。 - 接口层:提供打开设备,数据传输以及关闭设备的能力。
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。 - 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。
- 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。 - 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。
**图1** I2C统一服务模式结构图<a name="fig1"></a> **图 1** I2C统一服务模式结构图<a name="fig1"></a>
![image](figures/统一服务模式结构图.png "I2C统一服务模式结构图")
![I2C统一服务模式结构图](figures/统一服务模式结构图.png)
## 使用指导 ## 使用指导
...@@ -71,13 +74,13 @@ static const struct I2cLockMethod g_i2cLockOpsDefault = { ...@@ -71,13 +74,13 @@ static const struct I2cLockMethod g_i2cLockOpsDefault = {
若实际情况不允许使用mutex(例如使用者可能在中断上下文调用I2C接口,mutex可能导致休眠,而中断上下文不允许休眠)时,驱动适配者可以考虑使用其他类型的锁来实现一个自定义的I2cLockMethod。一旦实现了自定义的I2cLockMethod,默认的I2cLockMethod将被覆盖。 若实际情况不允许使用mutex(例如使用者可能在中断上下文调用I2C接口,mutex可能导致休眠,而中断上下文不允许休眠)时,驱动适配者可以考虑使用其他类型的锁来实现一个自定义的I2cLockMethod。一旦实现了自定义的I2cLockMethod,默认的I2cLockMethod将被覆盖。
**表1** I2cMethod结构体成员函数功能说明 **表 1** I2cMethod结构体成员函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 | | 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- | | -------- | -------- | -------- | -------- | -------- |
| transfer | cntlr:结构体指针,核心层I2C控制器。<br>msgs:结构体指针,用户消息。<br>count:uint16_t,消息数量。 | 无 | HDF_STATUS相关状态 | 传递用户消息 | | transfer | cntlr:结构体指针,核心层I2C控制器。<br>msgs:结构体指针,用户消息。<br>count:uint16_t类型,消息数量。 | 无 | HDF_STATUS相关状态 | 传递用户消息 |
**表2** I2cLockMethod结构体成员函数功能说明 **表 2** I2cLockMethod结构体成员函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 | | 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- | | -------- | -------- | -------- | -------- | -------- |
...@@ -86,22 +89,26 @@ static const struct I2cLockMethod g_i2cLockOpsDefault = { ...@@ -86,22 +89,26 @@ static const struct I2cLockMethod g_i2cLockOpsDefault = {
### 开发步骤 ### 开发步骤
I2C模块适配的三个必选环节是实例化驱动入口,配置属性文件,以及实例化核心层接口函数。 I2C模块适配包含以下四个步骤:
1. 实例化驱动入口 1. 实例化驱动入口
- 实例化HdfDriverEntry结构体成员。 - 实例化HdfDriverEntry结构体成员。
- 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2. 配置属性文件 2. 配置属性文件
- 在device_info.hcs文件中添加deviceNode描述。 - 在device_info.hcs文件中添加deviceNode描述。
- 【可选】添加i2c_config.hcs器件属性文件。 - 【可选】添加i2c_config.hcs器件属性文件。
3. 实例化I2C控制器对象 3. 实例化I2C控制器对象
- 初始化I2cCntlr成员。 - 初始化I2cCntlr成员。
- 实例化I2cCntlr成员I2cMethod和I2cLockMethod。 - 实例化I2cCntlr成员I2cMethod和I2cLockMethod。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br> > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> 实例化I2cCntlr成员I2cMethod和I2cLockMethod,详见[接口说明](#接口说明)。 > 实例化I2cCntlr成员I2cMethod和I2cLockMethod,详见[接口说明](#接口说明)。
...@@ -113,7 +120,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -113,7 +120,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
下方将以Hi3516DV300的驱动//device/soc/hisilicon/common/platform/i2c/i2c_hi35xx.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 下方将以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对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
...@@ -134,7 +141,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -134,7 +141,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
}; };
HDF_INIT(g_i2cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 HDF_INIT(g_i2cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
/* 核心层i2c_core.c管理器服务的驱动入口 */ // 核心层i2c_core.c管理器服务的驱动入口
struct HdfDriverEntry g_i2cManagerEntry = { struct HdfDriverEntry g_i2cManagerEntry = {
.moduleVersion = 1, .moduleVersion = 1,
.Bind = I2cManagerBind, .Bind = I2cManagerBind,
...@@ -145,20 +152,24 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -145,20 +152,24 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
HDF_INIT(g_i2cManagerEntry); 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 | | policy | 驱动服务发布的策略,I2C管理器具体配置为2,表示驱动对内核态和用户态都发布服务 |
| serviceName | 固定为HDF_PLATFORM_I2C_MANAGER | | priority | 驱动启动优先级(0-200),值越大优先级越低。I2C管理器具体配置为50 |
| policy | 具体配置为1或2取决于是否对用户态可见 | | permission | 驱动创建设备节点权限,I2C管理器具体配置为0664 |
| deviceMatchAttr | 没有使用,可忽略 | | 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文件中有所体现。 从第二个节点开始配置具体I2C控制器信息,此节点并不表示某一路I2C控制器,而是代表一个资源性质设备,用于描述一类I2C控制器的信息。多个控制器之间相互区分的参数是busId和reg_pbase,这在i2c_config.hcs文件中有所体现。
...@@ -197,14 +208,14 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -197,14 +208,14 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
root { root {
platform { platform {
i2c_config { i2c_config {
match_attr = "hisilicon_hi35xx_i2c"; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致 match_attr = "hisilicon_hi35xx_i2c"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
template i2c_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 template i2c_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。
bus = 0; //【必要】i2c识别号 bus = 0; // 【必要】i2c识别号
reg_pbase = 0x120b0000; //【必要】物理基地址 reg_pbase = 0x120b0000; // 【必要】物理基地址
reg_size = 0xd1; //【必要】寄存器位宽 reg_size = 0xd1; // 【必要】寄存器位宽
irq = 0; //【可选】中断号,由控制器的中断特性决定是否需要 irq = 0; // 【可选】中断号,由控制器的中断特性决定是否需要
freq = 400000; //【可选】频率,初始化硬件控制器的可选参数 freq = 400000; // 【可选】频率,初始化硬件控制器的可选参数
clk = 50000000; //【可选】控制器时钟,由控制器时钟的初始化流程决定是否需要 clk = 50000000; // 【可选】控制器时钟,由控制器时钟的初始化流程决定是否需要
} }
controller_0x120b0000 :: i2c_controller { controller_0x120b0000 :: i2c_controller {
bus = 0; bus = 0;
...@@ -227,14 +238,16 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -227,14 +238,16 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
#include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs" // 配置文件相对路径 #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs" // 配置文件相对路径
``` ```
3. 完成驱动入口注册之后,下一步就是以核心层I2cCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化I2cCntlr成员I2cMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 3. 实例化I2C控制器对象
完成驱动入口注册之后,下一步就是以核心层I2cCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化I2cCntlr成员I2cMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
- 自定义结构体参考 - 自定义结构体参考
从驱动的角度看,自定义结构体是参数和数据的载体,而且i2c_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I2cCntlr对象,例如设备号、总线号等。 从驱动的角度看,自定义结构体是参数和数据的载体,而且i2c_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I2cCntlr对象,例如设备号、总线号等。
```c ```c
/* 驱动适配者自定义结构体 */ // 驱动适配者自定义结构体
struct Hi35xxI2cCntlr { struct Hi35xxI2cCntlr {
struct I2cCntlr cntlr; // 【必要】是核心层控制对象,具体描述见下面。 struct I2cCntlr cntlr; // 【必要】是核心层控制对象,具体描述见下面。
OsalSpinlock spin; // 【必要】驱动适配者需要基于此锁变量对各个i2c操作函数实现对应的加锁解锁。 OsalSpinlock spin; // 【必要】驱动适配者需要基于此锁变量对各个i2c操作函数实现对应的加锁解锁。
...@@ -247,7 +260,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -247,7 +260,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
uint32_t regBasePhy; // 【必要】寄存器物理基地址 uint32_t regBasePhy; // 【必要】寄存器物理基地址
}; };
/* I2cCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。*/ // I2cCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
struct I2cCntlr { struct I2cCntlr {
struct OsalMutex lock; struct OsalMutex lock;
void *owner; void *owner;
...@@ -261,7 +274,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -261,7 +274,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
- I2cCntlr成员钩子函数结构体I2cMethod的实例化,和锁机制钩子函数结构体I2cLockMethod实例化,其他成员在Init函数中初始化。 - I2cCntlr成员钩子函数结构体I2cMethod的实例化,和锁机制钩子函数结构体I2cLockMethod实例化,其他成员在Init函数中初始化。
```c ```c
/* i2c_hi35xx.c中的示例 */ // i2c_hi35xx.c中的示例
static const struct I2cMethod g_method = { static const struct I2cMethod g_method = {
.transfer = Hi35xxI2cTransfer, .transfer = Hi35xxI2cTransfer,
}; };
...@@ -280,16 +293,16 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -280,16 +293,16 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
返回值: 返回值:
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** Init函数入参及返回值参考 **表 4** HDF_STATUS相关状态说明
| 状态(值) | 问题描述 | | 状态(值) | 问题描述 |
| -------- | -------- | | -------- | -------- |
| HDF_ERR_INVALID_OBJECT | 控制器对象非法 | | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
| HDF_ERR_INVALID_PARAM | 参数非法 | | HDF_ERR_INVALID_PARAM | 参数非法 |
| HDF_ERR_MALLOC_FAIL | 内存分配失败 | | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
| HDF_ERR_IO | I/O&nbsp;错误 | | HDF_ERR_IO | I/O错误 |
| HDF_SUCCESS | 传输成功 | | HDF_SUCCESS | 传输成功 |
| HDF_FAILURE | 传输失败 | | HDF_FAILURE | 传输失败 |
...@@ -300,23 +313,23 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -300,23 +313,23 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
```c ```c
static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device) static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device)
{ {
... ......
/* 遍历、解析i2c_config.hcs中的所有配置节点,并分别进行初始化,需要调用Hi35xxI2cParseAndInit函数。*/ // 遍历、解析i2c_config.hcs中的所有配置节点,并分别进行初始化,需要调用Hi35xxI2cParseAndInit函数。
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = Hi35xxI2cParseAndInit(device, childNode);//函数定义见下 ret = Hi35xxI2cParseAndInit(device, childNode);//函数定义见下
... ......
} }
... ......
} }
static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
{ {
struct Hi35xxI2cCntlr *hi35xx = NULL; struct Hi35xxI2cCntlr *hi35xx = NULL;
... // 入参判空 ...... // 入参判空
hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx)); // 内存分配 hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx)); // 内存分配
... // 返回值校验 ...... // 返回值校验
hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // 地址映射 hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // 地址映射
... // 返回值校验 ...... // 返回值校验
Hi35xxI2cCntlrInit(hi35xx); // 【必要】i2c设备的初始化 Hi35xxI2cCntlrInit(hi35xx); // 【必要】i2c设备的初始化
hi35xx->cntlr.priv = (void *)node; // 【必要】存储设备属性 hi35xx->cntlr.priv = (void *)node; // 【必要】存储设备属性
...@@ -325,7 +338,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -325,7 +338,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
hi35xx->cntlr.lockOps = &g_lockOps; // 【必要】I2cLockMethod的实例化对象的挂载 hi35xx->cntlr.lockOps = &g_lockOps; // 【必要】I2cLockMethod的实例化对象的挂载
(void)OsalSpinInit(&hi35xx->spin); // 【必要】锁的初始化 (void)OsalSpinInit(&hi35xx->spin); // 【必要】锁的初始化
ret = I2cCntlrAdd(&hi35xx->cntlr); // 【必要】调用此函数将控制器对象添加至平台核心层,返回成功信号后驱动才完全接入平台核心层。 ret = I2cCntlrAdd(&hi35xx->cntlr); // 【必要】调用此函数将控制器对象添加至平台核心层,返回成功信号后驱动才完全接入平台核心层。
... ......
#ifdef USER_VFS_SUPPORT #ifdef USER_VFS_SUPPORT
(void)I2cAddVfsById(hi35xx->cntlr.busId); // 【可选】若支持用户级的虚拟文件系统,则接入。 (void)I2cAddVfsById(hi35xx->cntlr.busId); // 【可选】若支持用户级的虚拟文件系统,则接入。
#endif #endif
...@@ -360,8 +373,8 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -360,8 +373,8 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
```c ```c
static void Hi35xxI2cRelease(struct HdfDeviceObject *device) static void Hi35xxI2cRelease(struct HdfDeviceObject *device)
{ {
... ......
/* 与Hi35xxI2cInit一样,需要将每个节点分别进行释放。*/ // 与Hi35xxI2cInit一样,需要将每个节点分别进行释放。
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
Hi35xxI2cRemoveByNode(childNode); // 函数定义如下 Hi35xxI2cRemoveByNode(childNode); // 函数定义如下
} }
...@@ -369,13 +382,13 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -369,13 +382,13 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
static void Hi35xxI2cRemoveByNode(const struct DeviceResourceNode *node) static void Hi35xxI2cRemoveByNode(const struct DeviceResourceNode *node)
{ {
... ......
/* 【必要】可以调用I2cCntlrGet函数通过设备的bus号获取I2cCntlr对象的指针,以及调用I2cCntlrRemove函数将I2cCntlr对象从平台核心层移除。*/ // 【必要】可以调用I2cCntlrGet函数通过设备的bus号获取I2cCntlr对象的指针,以及调用I2cCntlrRemove函数将I2cCntlr对象从平台核心层移除。
cntlr = I2cCntlrGet(bus); cntlr = I2cCntlrGet(bus);
if (cntlr != NULL && cntlr->priv == node) { if (cntlr != NULL && cntlr->priv == node) {
... ......
I2cCntlrRemove(cntlr); I2cCntlrRemove(cntlr);
/* 【必要】解除地址映射,释放锁和内存。*/ // 【必要】解除地址映射,释放锁和内存。
hi35xx = (struct Hi35xxI2cCntlr *)cntlr; hi35xx = (struct Hi35xxI2cCntlr *)cntlr;
OsalIoUnmap((void *)hi35xx->regBase); OsalIoUnmap((void *)hi35xx->regBase);
(void)OsalSpinDestroy(&hi35xx->spin); (void)OsalSpinDestroy(&hi35xx->spin);
...@@ -384,3 +397,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 ...@@ -384,3 +397,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文
return; return;
} }
``` ```
4. 驱动调试
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,消息传输的成功与否等。
...@@ -9,38 +9,49 @@ I3C(Improved Inter Integrated Circuit)总线是由MIPI Alliance开发的一 ...@@ -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主设备控制。相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。I3C增加了带内中断(In-Bind Interrupt)功能,支持I3C设备进行热接入操作,弥补了I2C总线需要额外增加中断线来完成中断的不足。I3C总线上允许同时存在I2C设备、I3C从设备和I3C次级主设备。
I3C接口定义了完成I3C传输的通用方法集合,包括: I3C接口定义了完成I3C传输的通用方法集合,包括:
- I3C控制器管理:打开或关闭I3C控制器。 - I3C控制器管理:打开或关闭I3C控制器。
- I3C控制器配置:获取或配置I3C控制器参数。 - I3C控制器配置:获取或配置I3C控制器参数。
- I3C消息传输:通过消息传输结构体数组进行自定义传输。 - I3C消息传输:通过消息传输结构体数组进行自定义传输。
- I3C带内中断:请求或释放带内中断。 - I3C带内中断:请求或释放带内中断。
### 基本概念<a name="section3"></a> ### 基本概念<a name="section3"></a>
- IBI(In-Band Interrupt)<br> - IBI(In-Band Interrupt)<br>
带内中断。在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从机同时发出中断请求,I3C主机则通过从机地址进行仲裁,低地址优先相应。 带内中断。在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从机同时发出中断请求,I3C主机则通过从机地址进行仲裁,低地址优先相应。
- DAA(Dynamic Address Assignment)<br> - DAA(Dynamic Address Assignment)<br>
动态地址分配。I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C设备都应以两种方式之一来唯一标识: 动态地址分配。I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C设备都应以两种方式之一来唯一标识:
1)设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址; 1)设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址;
2)在任何情况下,设备均应具有48位的临时ID。 除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。 2)在任何情况下,设备均应具有48位的临时ID。 除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。
- CCC(Common Command Code)<br> - CCC(Common Command Code)<br>
通用命令代码,所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。 通用命令代码,所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。
- BCR(Bus Characteristic Register)<br> - BCR(Bus Characteristic Register)<br>
总线特性寄存器,每个连接到 I3C 总线的 I3C 设备都应具有相关的只读总线特性寄存器 (BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。 总线特性寄存器,每个连接到 I3C 总线的 I3C 设备都应具有相关的只读总线特性寄存器 (BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。
- DCR(Device Characteristic Register)<br> - DCR(Device Characteristic Register)<br>
设备特性寄存器,连接到 I3C 总线的每个 I3C 设备都应具有相关的只读设备特性寄存器 (DCR)。 该寄存器描述了用于动态地址分配和通用命令代码的 I3C 兼容设备类型(例如,加速度计、陀螺仪等)。 设备特性寄存器,连接到 I3C 总线的每个 I3C 设备都应具有相关的只读设备特性寄存器 (DCR)。 该寄存器描述了用于动态地址分配和通用命令代码的 I3C 兼容设备类型(例如,加速度计、陀螺仪等)。
### 运作机制<a name="section4"></a> ### 运作机制<a name="section4"></a>
在HDF框架中,I3C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I3C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I3C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。 在HDF框架中,I3C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I3C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I3C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。
相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。一路I3C总线上,可以连接多个设备,这些设备可以是I2C从设备、I3C从设备和I3C次级主设备,但只能同时存在一个主设备,一般为控制器本身。 相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。一路I3C总线上,可以连接多个设备,这些设备可以是I2C从设备、I3C从设备和I3C次级主设备,但只能同时存在一个主设备,一般为控制器本身。
**图 1** I3C物理连线示意图<a name="fig1"></a> **图 1** I3C物理连线示意图<a name="fig1"></a>
![](figures/I3C物理连线示意图.png "I3C物理连线示意图") ![I3C物理连线示意图](figures/I3C物理连线示意图.png)
### 约束与限制<a name="section5"></a> ### 约束与限制<a name="section5"></a>
...@@ -53,6 +64,7 @@ I3C模块当前仅支持轻量和小型系统内核(LiteOS-A),不支持在 ...@@ -53,6 +64,7 @@ I3C模块当前仅支持轻量和小型系统内核(LiteOS-A),不支持在
I3C可连接单个或多个I3C、I2C从器件,它主要用于: I3C可连接单个或多个I3C、I2C从器件,它主要用于:
- 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等; - 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等;
- 通过软件或硬件协议转换,与其他接口(如 UART 串口等)的设备进行通信。 - 通过软件或硬件协议转换,与其他接口(如 UART 串口等)的设备进行通信。
### 接口说明<a name="section8"></a> ### 接口说明<a name="section8"></a>
...@@ -81,7 +93,7 @@ I3C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ ...@@ -81,7 +93,7 @@ I3C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/
I3C的使用流程如图2所示。 I3C的使用流程如图2所示。
**图 2** I3C使用流程图<a name="fig2"></a> **图 2** I3C使用流程图<a name="fig2"></a>
![](figures/I3C使用流程图.png "I3C使用流程图") ![I3C使用流程图](figures/I3C使用流程图.png)
#### 打开I3C控制器<a name="section5"></a> #### 打开I3C控制器<a name="section5"></a>
...@@ -96,7 +108,7 @@ DevHandle I3cOpen(int16_t number); ...@@ -96,7 +108,7 @@ DevHandle I3cOpen(int16_t number);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | ------------------- | | ---------- | ------------------- |
| number | I3C控制器号 | | number | int16_t类型,I3C控制器号 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| NULL | 打开I3C控制器失败 | | NULL | 打开I3C控制器失败 |
| 控制器句柄 | 打开的I3C控制器句柄 | | 控制器句柄 | 打开的I3C控制器句柄 |
...@@ -104,13 +116,13 @@ DevHandle I3cOpen(int16_t number); ...@@ -104,13 +116,13 @@ DevHandle I3cOpen(int16_t number);
假设系统中存在8个I3C控制器,编号从0到7,以下示例代码为打开1号控制器: 假设系统中存在8个I3C控制器,编号从0到7,以下示例代码为打开1号控制器:
```c ```c
DevHandle i3cHandle = NULL; /* I3C控制器句柄 / DevHandle i3cHandle = NULL; // I3C控制器句柄
/* 打开I3C控制器 */ // 打开I3C控制器
i3cHandle = I3cOpen(1); i3cHandle = I3cOpen(1);
if (i3cHandle == NULL) { if (i3cHandle == NULL) {
HDF_LOGE("I3cOpen: failed\n"); HDF_LOGE("I3cOpen: i3c open fail.\n");
return; return NULL;
} }
``` ```
...@@ -126,10 +138,10 @@ int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config); ...@@ -126,10 +138,10 @@ int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | I3C控制器句柄 | | handle | DevHandle类型,I3C控制器句柄 |
| config | I3C控制器配置 | | config | 结构体指针,I3C控制器配置 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 获取成功 | | HDF_SUCCESS | 获取成功 |
| 负数 | 获取失败 | | 负数 | 获取失败 |
获取I3C控制器配置示例: 获取I3C控制器配置示例:
...@@ -137,10 +149,10 @@ int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config); ...@@ -137,10 +149,10 @@ int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config);
```c ```c
struct I3cConfig config; struct I3cConfig config;
ret = I3cGetConfig(i3cHandle, &config); int32_t ret = I3cGetConfig(i3cHandle, &config);
if (ret != HDF_SUCCESS) { if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: Get config fail!", __func__); HDF_LOGE("I3cGetConfig: get config fail, ret:%d", ret);
return HDF_FAILURE; return ret;
} }
``` ```
...@@ -156,10 +168,10 @@ int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config); ...@@ -156,10 +168,10 @@ int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | I3C控制器句柄 | | handle | DevHandle类型,I3C控制器句柄 |
| config | I3C控制器配置 | | config | 结构体指针,I3C控制器配置 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 配置成功 | | HDF_SUCCESS | 配置成功 |
| 负数 | 配置失败 | | 负数 | 配置失败 |
配置I3C控制器示例: 配置I3C控制器示例:
...@@ -169,10 +181,10 @@ struct I3cConfig config; ...@@ -169,10 +181,10 @@ struct I3cConfig config;
config->busMode = I3C_BUS_HDR_MODE; config->busMode = I3C_BUS_HDR_MODE;
config->curMaster = NULL; config->curMaster = NULL;
ret = I3cSetConfig(i3cHandle, &config); int32_t ret = I3cSetConfig(i3cHandle, &config);
if (ret != HDF_SUCCESS) { if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: Set config fail!", __func__); HDF_LOGE("I3cSetConfig: set config fail, ret:%d", ret);
return HDF_FAILURE; return ret;
} }
``` ```
...@@ -189,10 +201,10 @@ int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum T ...@@ -189,10 +201,10 @@ int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum T
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------------------------------------- | | ---------- | -------------------------------------------- |
| handle | I3C控制器句柄 | | handle | DevHandle类型,I3C控制器句柄 |
| msgs | 待传输数据的消息结构体数组 | | msgs | 结构体指针,待传输数据的消息结构体数组 |
| count | 消息数组长度 | | count | int16_t类型,消息数组长度 |
| mode | 传输模式,0:I2C模式;1:I3C模式;2:发送CCC | | mode | 枚举类型,传输模式,0:I2C模式;1:I3C模式;2:发送CCC |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 正整数 | 成功传输的消息结构体数目 | | 正整数 | 成功传输的消息结构体数目 |
| 负数 | 执行失败 | | 负数 | 执行失败 |
...@@ -203,20 +215,20 @@ I3C传输消息类型为I3cMsg,每个传输消息结构体表示一次读或 ...@@ -203,20 +215,20 @@ I3C传输消息类型为I3cMsg,每个传输消息结构体表示一次读或
int32_t ret; int32_t ret;
uint8_t wbuff[2] = { 0x12, 0x13 }; uint8_t wbuff[2] = { 0x12, 0x13 };
uint8_t rbuff[2] = { 0 }; uint8_t rbuff[2] = { 0 };
struct I3cMsg msgs[2]; /* 自定义传输的消息结构体数组 */ struct I3cMsg msgs[2]; // 自定义传输的消息结构体数组
msgs[0].buf = wbuff; /* 写入的数据 */ msgs[0].buf = wbuff; // 写入的数据
msgs[0].len = 2; /* 写入数据长度为2 */ msgs[0].len = 2; // 写入数据长度为2
msgs[0].addr = 0x3F; /* 写入设备地址为0x3F */ msgs[0].addr = 0x3F; // 写入设备地址为0x3F
msgs[0].flags = 0; /* 传输标记为0,默认为写 */ msgs[0].flags = 0; // 传输标记为0,默认为写
msgs[1].buf = rbuff; /* 要读取的数据 */ msgs[1].buf = rbuff; // 要读取的数据
msgs[1].len = 2; /* 读取数据长度为2 */ msgs[1].len = 2; // 读取数据长度为2
msgs[1].addr = 0x3F; /* 读取设备地址为0x3F */ msgs[1].addr = 0x3F; // 读取设备地址为0x3F
msgs[1].flags = I3C_FLAG_READ /* I3C_FLAG_READ置位 */ msgs[1].flags = I3C_FLAG_READ // I3C_FLAG_READ置位
/* 进行一次I2C模式自定义传输,传输的消息个数为2 */ // 进行一次I2C模式自定义传输,传输的消息个数为2
ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE); ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE);
if (ret != 2) { if (ret != 2) {
HDF_LOGE("I3cTransfer: failed, ret %d\n", ret); HDF_LOGE("I3cTransfer: transfer fail, ret:%d\n", ret);
return; return HDF_FAILURE;
} }
``` ```
...@@ -238,12 +250,12 @@ int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t ...@@ -238,12 +250,12 @@ int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | I3C控制器句柄 | | handle | DevHandle类型,I3C控制器句柄 |
| addr | I3C设备地址 | | addr | uint16_t类型,I3C设备地址 |
| func | IBI回调函数 | | func | 函数指针,IBI回调函数 |
| payload | IBI有效载荷 | | payload | IBI有效载荷 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 请求成功 | | HDF_SUCCESS | 请求成功 |
| 负数 | 请求失败 | | 负数 | 请求失败 |
请求带内中断示例: 请求带内中断示例:
...@@ -253,9 +265,9 @@ static int32_t TestI3cIbiFunc(DevHandle handle, uint16_t addr, struct I3cIbiData ...@@ -253,9 +265,9 @@ static int32_t TestI3cIbiFunc(DevHandle handle, uint16_t addr, struct I3cIbiData
{ {
(void)handle; (void)handle;
(void)addr; (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) int32_t I3cTestRequestIbi(void)
...@@ -263,22 +275,22 @@ int32_t I3cTestRequestIbi(void) ...@@ -263,22 +275,22 @@ int32_t I3cTestRequestIbi(void)
DevHandle i3cHandle = NULL; DevHandle i3cHandle = NULL;
int32_t ret; int32_t ret;
/* 打开I3C控制器 */ // 打开I3C控制器
i3cHandle = I3cOpen(1); i3cHandle = I3cOpen(1);
if (i3cHandle == NULL) { if (i3cHandle == NULL) {
HDF_LOGE("I3cOpen: failed\n"); HDF_LOGE("I3cOpen: i3c open fail.\n");
return; return;
} }
ret = I3cRequestIbi(i3cHandle, 0x3F, TestI3cIbiFunc, 16); ret = I3cRequestIbi(i3cHandle, 0x3F, TestI3cIbiFunc, 16);
if (ret != 0) { if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: Request IBI failed!", __func__); HDF_LOGE("%s: Request IBI failed!", __func__);
return -1; return ret;
} }
I3cClose(i3cHandle); I3cClose(i3cHandle);
HDF_LOGD("%s: Done", __func__); HDF_LOGD("I3cTestRequestIbi: done");
return 0; return HDF_SUCCESS;
} }
``` ```
...@@ -294,16 +306,16 @@ int32_t I3cFreeIbi(DevHandle handle, uint16_t addr); ...@@ -294,16 +306,16 @@ int32_t I3cFreeIbi(DevHandle handle, uint16_t addr);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | I3C控制器句柄 | | handle | DevHandle类型,I3C控制器句柄 |
| addr | I3C设备地址 | | addr | uint16_t类型,I3C设备地址 |
| **返回值** | **返回值描述** | | **返回值** | **返回值描述** |
| 0 | 释放成功 | | HDF_SUCCESS | 释放成功 |
| 负数 | 释放失败 | | 负数 | 释放失败 |
释放带内中断示例: 释放带内中断示例:
```c ```c
I3cFreeIbi(i3cHandle, 0x3F); /* 释放带内中断 */ I3cFreeIbi(i3cHandle, 0x3F); // 释放带内中断
``` ```
#### 关闭I3C控制器<a name="section11"></a> #### 关闭I3C控制器<a name="section11"></a>
...@@ -319,12 +331,12 @@ void I3cClose(DevHandle handle); ...@@ -319,12 +331,12 @@ void I3cClose(DevHandle handle);
| 参数 | 参数描述 | | 参数 | 参数描述 |
| ---------- | -------------- | | ---------- | -------------- |
| handle | I3C控制器句柄 | | handle | DevHandle类型,I3C控制器句柄 |
关闭I3C控制器实例: 关闭I3C控制器实例:
```c ```c
I3cClose(i3cHandle); /* 关闭I3C控制器 */ I3cClose(i3cHandle); // 关闭I3C控制器
``` ```
## 使用实例<a name="section10"></a> ## 使用实例<a name="section10"></a>
...@@ -342,28 +354,28 @@ I3cClose(i3cHandle); /* 关闭I3C控制器 */ ...@@ -342,28 +354,28 @@ I3cClose(i3cHandle); /* 关闭I3C控制器 */
示例如下: 示例如下:
```c ```c
#include "i3c_if.h" /* I3C标准接口头文件 */ #include "i3c_if.h" // I3C标准接口头文件
#include "hdf_log.h" /* 标准日志打印头文件 */ #include "hdf_log.h" // 标准日志打印头文件
#include "osal_io.h" /* 标准IO读写接口头文件 */ #include "osal_io.h" // 标准IO读写接口头文件
#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */ #include "osal_time.h" // 标准延迟&睡眠接口头文件
/* 定义一个表示设备的结构体,存储信息 */ // 定义一个表示设备的结构体,存储信息
struct TestI3cDevice { struct TestI3cDevice {
uint16_t busNum; /* I3C总线号 */ uint16_t busNum; // I3C总线号
uint16_t addr; /* I3C设备地址 */ uint16_t addr; // I3C设备地址
uint16_t regLen; /* 寄存器字节宽度 */ uint16_t regLen; // 寄存器字节宽度
DevHandle i3cHandle; /* I3C控制器句柄 */ DevHandle i3cHandle; // I3C控制器句柄
}; };
/* 基于I3cTransfer方法封装一个寄存器读写的辅助函数, 通过flag表示读或写 */ // 基于I3cTransfer方法封装一个寄存器读写的辅助函数,通过flag表示读或写
static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAddr, static int32_t TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAddr,
unsigned char *regData, unsigned int dataLen, uint8_t flag) unsigned char *regData, unsigned int dataLen, uint8_t flag)
{ {
int index = 0; int index = 0;
unsigned char regBuf[4] = {0}; unsigned char regBuf[4] = {0};
struct I3cMsg msgs[2] = {0}; struct I3cMsg msgs[2] = {0};
/* 单双字节寄存器长度适配 */ // 单双字节寄存器长度适配
if (testDevice->regLen == 1) { if (testDevice->regLen == 1) {
regBuf[index++] = regAddr & 0xFF; regBuf[index++] = regAddr & 0xFF;
} else { } else {
...@@ -371,81 +383,80 @@ static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAd ...@@ -371,81 +383,80 @@ static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAd
regBuf[index++] = regAddr & 0xFF; regBuf[index++] = regAddr & 0xFF;
} }
/* 填充I3cMsg消息结构 */ // 填充I3cMsg消息结构
msgs[0].addr = testDevice->addr; msgs[0].addr = testDevice->addr;
msgs[0].flags = 0; /* 标记为0,表示写入 */ msgs[0].flags = 0; // 标记为0,表示写入
msgs[0].len = testDevice->regLen; msgs[0].len = testDevice->regLen;
msgs[0].buf = regBuf; msgs[0].buf = regBuf;
msgs[1].addr = testDevice->addr; 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].len = dataLen;
msgs[1].buf = regData; msgs[1].buf = regData;
if (I3cTransfer(testDevice->i3cHandle, msgs, 2, I2C_MODE) != 2) { 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_FAILURE;
} }
return HDF_SUCCESS; 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) unsigned char *regData, unsigned int dataLen)
{ {
return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 1); 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) unsigned char *regData, unsigned int dataLen)
{ {
return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 0); return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 0);
} }
/* I3C例程总入口 */ // I3C例程总入口
static int32_t TestCaseI3c(void) static int32_t TestCaseI3c(void)
{ {
int32_t i;
int32_t ret; int32_t ret;
unsigned char bufWrite[7] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xA, 0xB, 0xC }; unsigned char bufWrite[7] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xA, 0xB, 0xC };
unsigned char bufRead[7] = {0}; unsigned char bufRead[7] = {0};
static struct TestI3cDevice testDevice; static struct TestI3cDevice testDevice;
/* 设备信息初始化 */ // 设备信息初始化
testDevice.busNum = 18; testDevice.busNum = 18;
testDevice.addr = 0x3F; testDevice.addr = 0x3F;
testDevice.regLen = 1; testDevice.regLen = 2;
testDevice.i3cHandle = NULL; testDevice.i3cHandle = NULL;
/* 打开I3C控制器 */ // 打开I3C控制器
testDevice.i3cHandle = I3cOpen(testDevice.busNum); testDevice.i3cHandle = I3cOpen(testDevice.busNum);
if (testDevice.i3cHandle == NULL) { if (testDevice.i3cHandle == NULL) {
HDF_LOGE("%s: Open I3c:%u fail!", __func__, testDevice.busNum); HDF_LOGE("TestCaseI3c: open I3c:%u fail!", testDevice.busNum);
return -1; return HDF_FAILURE;
} }
/* 向地址为0x3F的设备连续写7字节数据 */ // 向地址为0x3F的设备连续写7字节数据
ret = TestI3cWriteReg(&testDevice, 0x3F, bufWrite, 7); ret = TestI3cWriteReg(&testDevice, 0x3F, bufWrite, 7);
if (ret != HDF_SUCCESS) { 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); I3cClose(testDevice.i3cHandle);
return -1; return ret;
} }
OsalMSleep(10); OsalMSleep(10);
/* 从地址为0x3F的设备连续读7字节数据 */ // 从地址为0x3F的设备连续读7字节数据
ret = TestI3cReadReg(&testDevice, 0x3F, bufRead, 7); ret = TestI3cReadReg(&testDevice, 0x3F, bufRead, 7);
if (ret != HDF_SUCCESS) { 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); I3cClose(testDevice.i3cHandle);
return -1; return ret;
} }
HDF_LOGI("%s: test i3c write&read reg success!", __func__); HDF_LOGD("TestCaseI3c: test i3c write&read reg success!");
HDF_LOGD("TestCaseI3c: function tests end.");
/* 访问完毕关闭I3C控制器 */ // 访问完毕关闭I3C控制器
I3cClose(testDevice.i3cHandle); I3cClose(testDevice.i3cHandle);
return 0; return HDF_SUCCESS;
} }
``` ```
...@@ -42,14 +42,16 @@ I3C是两线双向串行总线,针对多个传感器从设备进行了优化 ...@@ -42,14 +42,16 @@ I3C是两线双向串行总线,针对多个传感器从设备进行了优化
I3C模块各分层的作用为: I3C模块各分层的作用为:
- 接口层:提供打开设备,写入数据,关闭设备的能力。 - 接口层:提供打开设备,写入数据,关闭设备的能力。
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。由于框架需要统一管理I3C总线上挂载的所有设备,因此还提供了添加、删除以及获取设备的能力,以及中断回调函数。 - 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。由于框架需要统一管理I3C总线上挂载的所有设备,因此还提供了添加、删除以及获取设备的能力,以及中断回调函数。
- 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。 - 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。 在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
**图 1** I3C统一服务模式结构图<a name="fig1"></a> **图 1** I3C统一服务模式结构图<a name="fig1"></a>
![image1](figures/统一服务模式结构图.png) ![I3C统一服务模式结构图](figures/统一服务模式结构图.png)
### 约束与限制<a name="5"></a> ### 约束与限制<a name="5"></a>
...@@ -62,6 +64,7 @@ I3C模块当前仅支持轻量和小型系统内核(LiteOS-A) 。 ...@@ -62,6 +64,7 @@ I3C模块当前仅支持轻量和小型系统内核(LiteOS-A) 。
I3C可连接单个或多个I3C、I2C从器件,它主要用于: I3C可连接单个或多个I3C、I2C从器件,它主要用于:
- 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等。 - 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等。
- 通过软件或硬件协议转换,与其他通信接口(如UART串口等)的设备进行通信。 - 通过软件或硬件协议转换,与其他通信接口(如UART串口等)的设备进行通信。
当驱动开发者需要将I3C设备适配到OpenHarmony时,需要进行I3C驱动适配,下文将介绍如何进行I3C驱动适配。 当驱动开发者需要将I3C设备适配到OpenHarmony时,需要进行I3C驱动适配,下文将介绍如何进行I3C驱动适配。
...@@ -71,6 +74,7 @@ I3C可连接单个或多个I3C、I2C从器件,它主要用于: ...@@ -71,6 +74,7 @@ I3C可连接单个或多个I3C、I2C从器件,它主要用于:
为了保证上层在调用I3C接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/i3c/i3c_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互。 为了保证上层在调用I3C接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/i3c/i3c_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互。
I3cMethod定义: I3cMethod定义:
```c ```c
struct I3cMethod { struct I3cMethod {
int32_t (*sendCccCmd)(struct I3cCntlr *cntlr, struct I3cCccCmd *ccc); int32_t (*sendCccCmd)(struct I3cCntlr *cntlr, struct I3cCccCmd *ccc);
...@@ -83,39 +87,52 @@ struct I3cMethod { ...@@ -83,39 +87,52 @@ struct I3cMethod {
}; };
``` ```
**表1** I3cMethod结构体成员的钩子函数功能说明 **表 1** I3cMethod结构体成员的钩子函数功能说明
|函数成员|入参|出参|返回值|功能| | 函数成员 | 入参 | 出参 | 返回值 | 功能 |
|-|-|-|-|-| | - | - | - | - | - |
|sendCccCmd| **cntlr**:结构体指针,核心层I3C控制器<br />**ccc**:传入的通用命令代码结构体指针 | **ccc**:传出的通用命令代码结构体指针 | HDF_STATUS相关状态|发送CCC(Common command Code,即通用命令代码)| | sendCccCmd | **cntlr**:结构体指针,核心层I3C控制器<br />**ccc**:传入的通用命令代码结构体指针 | **ccc**:传出的通用命令代码结构体指针 | HDF_STATUS相关状态|发送CCC(Common command Code,即通用命令代码)|
|Transfer | **cntlr**:结构体指针,核心层I3C控制器<br />**msgs**:结构体指针,用户消息<br />**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息| HDF_STATUS相关状态 | 使用I3C模式传递用户消息 | | Transfer | **cntlr**:结构体指针,核心层I3C控制器<br />**msgs**:结构体指针,用户消息<br />**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息 | HDF_STATUS相关状态 | 使用I3C模式传递用户消息 |
|i2cTransfer | **cntlr**:结构体指针,核心层I3C控制器<br />**msgs**:结构体指针,用户消息<br />**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息 | HDF_STATUS相关状态 | 使用I2C模式传递用户消息 | |i2cTransfer | **cntlr**:结构体指针,核心层I3C控制器<br />**msgs**:结构体指针,用户消息<br />**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息 | HDF_STATUS相关状态 | 使用I2C模式传递用户消息 |
|setConfig| **cntlr**:结构体指针,核心层I3C控制器<br />**config**:控制器配置参数| 无 | HDF_STATUS相关状态 | 设置I3C控制器配置参数 | | setConfig | **cntlr**:结构体指针,核心层I3C控制器<br />**config**:控制器配置参数| 无 | HDF_STATUS相关状态 | 设置I3C控制器配置参数 |
|getConfig| **cntlr**:结构体指针,核心层I3C控制器| **config**:控制器配置参数 | HDF_STATUS相关状态 | 获取I3C控制器配置参数 | | getConfig | **cntlr**:结构体指针,核心层I3C控制器 | **config**:控制器配置参数 | HDF_STATUS相关状态 | 获取I3C控制器配置参数 |
|requestIbi| **device**:结构体指针,核心层I3C设备| 无 | HDF_STATUS相关状态 | 为I3C设备请求IBI(In-Bind Interrupt,即带内中断) | | requestIbi | **device**:结构体指针,核心层I3C设备 | 无 | HDF_STATUS相关状态 | 为I3C设备请求IBI(In-Bind Interrupt,即带内中断) |
|freeIbi| **device**:结构体指针,核心层I3C设备| 无 | HDF_STATUS相关状态 | 释放IBI | | freeIbi | **device**:结构体指针,核心层I3C设备 | 无 | HDF_STATUS相关状态 | 释放IBI |
### 开发步骤 <a name="9"></a> ### 开发步骤 <a name="9"></a>
I3C模块适配的四个环节是实例化驱动入口、配置属性文件、实例化I3C控制器对象以及注册中断处理子程序。 I3C模块适配包含以下五个步骤:
- 实例化驱动入口
- 实例化驱动入口:
- 实例化HdfDriverEntry结构体成员。 - 实例化HdfDriverEntry结构体成员。
- 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
- 配置属性文件 - 配置属性文件
- 在device_info.hcs文件中添加deviceNode描述。 - 在device_info.hcs文件中添加deviceNode描述。
- 【可选】添加i3c_config.hcs器件属性文件。 - 【可选】添加i3c_config.hcs器件属性文件。
- 实例化I3C控制器对象: - 实例化I3C控制器对象
- 初始化I3cCntlr成员。 - 初始化I3cCntlr成员。
- 实例化I3cCntlr成员I3cMethod方法集合,其定义和成员函数说明见下文。 - 实例化I3cCntlr成员I3cMethod方法集合,其定义和成员函数说明见下文。
- 注册中断处理子程序: - 注册中断处理子程序
为控制器注册中断处理程序,实现设备热接入和IBI(带内中断)功能。 为控制器注册中断处理程序,实现设备热接入和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对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 驱动入口必须为HdfDriverEntry(在//drivers/hdf_core/framework/include/core/hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
...@@ -136,7 +153,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -136,7 +153,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
}; };
HDF_INIT(g_virtualI3cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 HDF_INIT(g_virtualI3cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
/* 核心层i3c_core.c管理器服务的驱动入口 */ // 核心层i3c_core.c管理器服务的驱动入口
struct HdfDriverEntry g_i3cManagerEntry = { struct HdfDriverEntry g_i3cManagerEntry = {
.moduleVersion = 1, .moduleVersion = 1,
.Init = I3cManagerInit, .Init = I3cManagerInit,
...@@ -147,19 +164,20 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -147,19 +164,20 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
``` ```
2. 配置属性文件 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管理器,其各项参数必须如下设置: **表 2** device_info.hcs节点参数说明
|成员名|值| | 成员名 | 值 |
|-|-| | -------- | -------- |
|moduleName |HDF_PLATFORM_I3C_MANAGER| | policy | 驱动服务发布的策略,I3C管理器具体配置为0,表示驱动不需要发布服务 |
|serviceName|无(预留)| | priority | 驱动启动优先级(0-200),值越大优先级越低。I3C管理器具体配置为52 |
|policy|0| | permission | 驱动创建设备节点权限,I3C管理器具体配置为0664 |
|cntlrMatchAttr| 无(预留)| | moduleName | 驱动名称,I3C管理器固定为HDF_PLATFORM_I3C_MANAGER |
| serviceName | 驱动对外发布服务的名称,I3C管理器服务名设置为HDF_PLATFORM_I3C_MANAGER |
| deviceMatchAttr | 驱动私有数据匹配的关键字,I3C管理器没有使用,可忽略 |
从第二个节点开始配置具体I3C控制器信息,此节点并不表示某一路I3C控制器,而是代表一个资源性质设备,用于描述一类I3C控制器的信息。本例只有一个I3C控制器,如有多个控制器,则需要在device_info.hcs文件增加deviceNode信息,以及在i3c_config文件中增加对应的器件属性。 从第二个节点开始配置具体I3C控制器信息,此节点并不表示某一路I3C控制器,而是代表一个资源性质设备,用于描述一类I3C控制器的信息。本例只有一个I3C控制器,如有多个控制器,则需要在device_info.hcs文件增加deviceNode信息,以及在i3c_config文件中增加对应的器件属性。
...@@ -228,7 +246,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -228,7 +246,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
此步骤需要通过实现HdfDriverEntry成员函数(Bind,Init,Release)来完成。 此步骤需要通过实现HdfDriverEntry成员函数(Bind,Init,Release)来完成。
I3cCntlr成员钩子函数结构体I3cMethod的实例化,I3cLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发,其他成员在Init函数中初始化 I3cCntlr成员钩子函数结构体I3cMethod的实例化,I3cLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发。
- 自定义结构体参考 - 自定义结构体参考
...@@ -250,7 +268,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -250,7 +268,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
uint32_t i2cFmPlusRate; uint32_t i2cFmPlusRate;
}; };
/* I3cCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。 */ // I3cCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。
struct I3cCntlr { struct I3cCntlr {
OsalSpinlock lock; OsalSpinlock lock;
void *owner; void *owner;
...@@ -272,17 +290,18 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -272,17 +290,18 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
**返回值:** **返回值:**
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS 定义)。 HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
**表 3** HDF_STATUS相关状态说明
|状态(值)|问题描述| | 状态(值) | 问题描述 |
|:-|:-:| | -------- | -------- |
|HDF_ERR_INVALID_OBJECT|控制器对象非法| | HDF_ERR_INVALID_OBJECT | 控制器对象非法 |
|HDF_ERR_INVALID_PARAM |参数非法| | HDF_ERR_INVALID_PARAM | 参数非法 |
|HDF_ERR_MALLOC_FAIL |内存分配失败| | HDF_ERR_MALLOC_FAIL | 内存分配失败 |
|HDF_ERR_IO |I/O 错误| | HDF_ERR_IO | I/O错误 |
|HDF_SUCCESS |传输成功| | HDF_SUCCESS | 传输成功 |
|HDF_FAILURE |传输失败| | HDF_FAILURE | 传输失败 |
**函数说明:** **函数说明:**
...@@ -306,14 +325,14 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -306,14 +325,14 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
goto __ERR__; goto __ERR__;
} }
... ......
virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize); // 【必要】地址映射 virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize); // 【必要】地址映射
ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); //【必要】注册中断程序 ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); //【必要】注册中断程序
if (ret != HDF_SUCCESS) { if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: register irq failed!", __func__); HDF_LOGE("%s: register irq failed!", __func__);
return ret; return ret;
} }
... ......
VirtualI3cCntlrInit(virtual); // 【必要】I3C设备的初始化 VirtualI3cCntlrInit(virtual); // 【必要】I3C设备的初始化
virtual->cntlr.priv = (void *)node; // 【必要】存储设备属性 virtual->cntlr.priv = (void *)node; // 【必要】存储设备属性
virtual->cntlr.busId = virtual->busId; // 【必要】初始化I3cCntlr成员 virtual->cntlr.busId = virtual->busId; // 【必要】初始化I3cCntlr成员
...@@ -360,13 +379,13 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -360,13 +379,13 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
{ {
struct DeviceResourceIface *drsOps = NULL; struct DeviceResourceIface *drsOps = NULL;
/* 获取drsOps方法 */ // 获取drsOps方法
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
HDF_LOGE("%s: Invalid drs ops fail!", __func__); HDF_LOGE("%s: Invalid drs ops fail!", __func__);
return HDF_FAILURE; return HDF_FAILURE;
} }
/* 将配置参数依次读出,并填充至结构体中 */ // 将配置参数依次读出,并填充至结构体中
if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) { if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) {
HDF_LOGE("%s: Read busId fail!", __func__); HDF_LOGE("%s: Read busId fail!", __func__);
return HDF_ERR_IO; return HDF_ERR_IO;
...@@ -379,7 +398,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -379,7 +398,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
HDF_LOGE("%s: Read IrqNum fail!", __func__); HDF_LOGE("%s: Read IrqNum fail!", __func__);
return HDF_ERR_IO; return HDF_ERR_IO;
} }
··· ......
return HDF_SUCCESS; return HDF_SUCCESS;
} }
``` ```
...@@ -421,8 +440,8 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -421,8 +440,8 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
HDF_LOGE("%s: read busId fail!", __func__); HDF_LOGE("%s: read busId fail!", __func__);
return; return;
} }
... ......
/* 可以调用I3cCntlrGet函数通过设备的cntlrNum获取I3cCntlr对象,以及调用I3cCntlrRemove函数来释放I3cCntlr对象的内容。 */ // 可以调用I3cCntlrGet函数通过设备的cntlrNum获取I3cCntlr对象,以及调用I3cCntlrRemove函数来释放I3cCntlr对象的内容。
cntlr = I3cCntlrGet(busId); cntlr = I3cCntlrGet(busId);
if (cntlr != NULL && cntlr->priv == node) { if (cntlr != NULL && cntlr->priv == node) {
I3cCntlrPut(cntlr); I3cCntlrPut(cntlr);
...@@ -444,8 +463,8 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -444,8 +463,8 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
HDF_LOGE("%s: device or property is NULL", __func__); HDF_LOGE("%s: device or property is NULL", __func__);
return; return;
} }
... ......
/* 遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作 */ // 遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
VirtualI3cRemoveByNode(childNode); //函数定义如上 VirtualI3cRemoveByNode(childNode); //函数定义如上
} }
...@@ -471,7 +490,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -471,7 +490,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
case I3C_RESERVED_ADDR_7H7A: case I3C_RESERVED_ADDR_7H7A:
case I3C_RESERVED_ADDR_7H7C: case I3C_RESERVED_ADDR_7H7C:
case I3C_RESERVED_ADDR_7H7F: case I3C_RESERVED_ADDR_7H7F:
/* 广播地址单比特错误的所有情形 */ // 广播地址单比特错误的所有情形
HDF_LOGW("%s: broadcast Address single bit error!", __func__); HDF_LOGW("%s: broadcast Address single bit error!", __func__);
break; break;
default: default:
...@@ -495,7 +514,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -495,7 +514,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
return HDF_ERR_INVALID_PARAM; return HDF_ERR_INVALID_PARAM;
} }
virtual = (struct VirtualI3cCntlr *)data; virtual = (struct VirtualI3cCntlr *)data;
/* 【必要】获取产生中断的地址,使用CHECK_RESERVED_ADDR宏判断该地址是否为I3C保留地址。 */ // 【必要】获取产生中断的地址,使用CHECK_RESERVED_ADDR宏判断该地址是否为I3C保留地址。
ibiAddr = VirtualI3cGetIbiAddr(); ibiAddr = VirtualI3cGetIbiAddr();
if (CHECK_RESERVED_ADDR(ibiAddr) == I3C_ADDR_RESERVED) { if (CHECK_RESERVED_ADDR(ibiAddr) == I3C_ADDR_RESERVED) {
HDF_LOGD("%s: Calling VirtualI3cResAddrWorker...", __func__); HDF_LOGD("%s: Calling VirtualI3cResAddrWorker...", __func__);
...@@ -508,13 +527,17 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 ...@@ -508,13 +527,17 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
return HDF_ERR_MALLOC_FAIL; return HDF_ERR_MALLOC_FAIL;
} }
if (device->ibi->payload > VIRTUAL_I3C_TEST_STR_LEN) { if (device->ibi->payload > VIRTUAL_I3C_TEST_STR_LEN) {
/* 将字符串"Hello I3C!"放入IBI缓冲区内 */ // 将字符串"Hello I3C!"放入IBI缓冲区内
*device->ibi->data = *testStr; *device->ibi->data = *testStr;
} }
/* 根据产生IBI的I3C设备调用IBI回调函数 */ // 根据产生IBI的I3C设备调用IBI回调函数
return I3cCntlrIbiCallback(device); return I3cCntlrIbiCallback(device);
} }
return HDF_SUCCESS; return HDF_SUCCESS;
} }
``` ```
5. 驱动调试
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功,数据能否传输等。
...@@ -27,17 +27,20 @@ MMC、SD、SDIO总线,其总线规范类似,都是从MMC总线规范演化 ...@@ -27,17 +27,20 @@ MMC、SD、SDIO总线,其总线规范类似,都是从MMC总线规范演化
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
MMC模块各分层作用: MMC模块各分层作用:
- 接口层提供打开MMC设备、检查MMC控制器是否存在设备、关闭MMC设备的接口。 - 接口层提供打开MMC设备、检查MMC控制器是否存在设备、关闭MMC设备的接口。
- 核心层主要提供MMC控制器、移除和管理的能力,还有公共控制器业务。通过钩子函数与适配层交互。 - 核心层主要提供MMC控制器、移除和管理的能力,还有公共控制器业务。通过钩子函数与适配层交互。
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 - 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图1** MMC独立服务模式结构图 **图 1** MMC独立服务模式结构图
![img1](figures/独立服务模式结构图.png "MMC独立服务模式结构图") ![MMC独立服务模式结构图](figures/独立服务模式结构图.png)
## 开发指导 ## 开发指导
...@@ -71,15 +74,15 @@ struct MmcCntlrOps { ...@@ -71,15 +74,15 @@ struct MmcCntlrOps {
}; };
``` ```
**表1** MmcCntlrOps结构体成员的钩子函数功能说明 **表 1** MmcCntlrOps结构体成员的钩子函数功能说明
| 成员函数 | 入参 | 返回值 | 功能 | | 成员函数 | 入参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | | -------- | -------- | -------- | -------- |
| doRequest | cntlr:结构体指针,核心层MMC控制器<br>cmd:结构体指针,传入命令值 | HDF_STATUS相关状态 | request相应处理 | | doRequest | cntlr:结构体指针,核心层MMC控制器<br>cmd:结构体指针,传入命令值 | HDF_STATUS相关状态 | request相应处理 |
| setClock | cntlr:结构体指针,核心层MMC控制器<br>clock:时钟传入值 | HDF_STATUS相关状态 | 设置时钟频率 | | setClock | cntlr:结构体指针,核心层MMC控制器<br>clock:uint32_t类型,时钟传入值 | HDF_STATUS相关状态 | 设置时钟频率 |
| setPowerMode | cntlr:结构体指针,核心层MMC控制器<br>mode:枚举值(见MmcPowerMode定义),功耗模式 | HDF_STATUS相关状态 | 设置功耗模式 | | setPowerMode | cntlr:结构体指针,核心层MMC控制器<br>mode:枚举值(见MmcPowerMode定义),功耗模式 | HDF_STATUS相关状态 | 设置功耗模式 |
| setBusWidth | cntlr:核心层结构体指针,核心层MMMC控制器<br>width:枚举(见MmcBusWidth定义),总线带宽 | HDF_STATUS相关状态 | 设置总线带宽 | | setBusWidth | cntlr:核心层结构体指针,核心层MMMC控制器<br>width:枚举类型(见MmcBusWidth定义),总线带宽 | HDF_STATUS相关状态 | 设置总线带宽 |
| setBusTiming | cntlr:结构体指针,核心层MMC控制器<br>timing:枚举(见MmcBusTiming定义),总线时序 | HDF_STATUS相关状态 | 设置总线时序 | | setBusTiming | cntlr:结构体指针,核心层MMC控制器<br>timing:枚举类型(见MmcBusTiming定义),总线时序 | HDF_STATUS相关状态 | 设置总线时序 |
| setSdioIrq | cntlr:结构体指针,核心层MMC控制器<br>enable:布尔值,控制中断 | HDF_STATUS相关状态 | 使能/去使能SDIO中断 | | setSdioIrq | cntlr:结构体指针,核心层MMC控制器<br>enable:布尔值,控制中断 | HDF_STATUS相关状态 | 使能/去使能SDIO中断 |
| hardwareReset | cntlr:结构体指针,核心层MMC控制器 | HDF_STATUS相关状态 | 复位硬件 | | hardwareReset | cntlr:结构体指针,核心层MMC控制器 | HDF_STATUS相关状态 | 复位硬件 |
| systemInit | cntlr:结构体指针,核心层MMC控制器 | HDF_STATUS相关状态 | 系统初始化 | | systemInit | cntlr:结构体指针,核心层MMC控制器 | HDF_STATUS相关状态 | 系统初始化 |
...@@ -95,18 +98,22 @@ struct MmcCntlrOps { ...@@ -95,18 +98,22 @@ struct MmcCntlrOps {
MMC模块适配包含以下四个步骤: MMC模块适配包含以下四个步骤:
- 实例化驱动入口。 - 实例化驱动入口
- 配置属性文件。
- 实例化MMC控制器对象。 - 配置属性文件
- 驱动调试。
- 实例化MMC控制器对象
- 驱动调试
### 开发实例 ### 开发实例
下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/mmc/himci_v200/himci.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/mmc/himci_v200/himci.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释放驱动资源并退出。
MMC驱动入口开发参考: MMC驱动入口开发参考:
...@@ -122,9 +129,22 @@ MMC模块适配包含以下四个步骤: ...@@ -122,9 +129,22 @@ MMC模块适配包含以下四个步骤:
HDF_INIT(g_mmcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 HDF_INIT(g_mmcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
``` ```
2. 配置属性文件。 2. 配置属性文件
完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以三个MMC控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在mmc_config.hcs文件中增加对应的器件属性。器件属性值与核心层MmcCntlr成员的默认值或限制范围有密切关系,需要在mmc_config.hcs中配置器件属性。
独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示:
完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以三个MMC控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层MmcCntlr成员的默认值或限制范围有密切关系,需要在mmc_config.hcs中配置器件属性。 **表 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 配置参考: - device_info.hcs 配置参考:
...@@ -144,7 +164,7 @@ MMC模块适配包含以下四个步骤: ...@@ -144,7 +164,7 @@ MMC模块适配包含以下四个步骤:
permission = 0644; // 驱动创建设备节点权限 permission = 0644; // 驱动创建设备节点权限
moduleName = "hi3516_mmc_driver"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。 moduleName = "hi3516_mmc_driver"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。
serviceName = "HDF_PLATFORM_MMC_0"; // 【必要】驱动对外发布服务的名称,必须唯一。 serviceName = "HDF_PLATFORM_MMC_0"; // 【必要】驱动对外发布服务的名称,必须唯一。
deviceMatchAttr = "hi3516_mmc_emmc"; // 【必要】用于配置控制器私有数据,要与mmc_config.hcs中对应控制器保持一致 deviceMatchAttr = "hi3516_mmc_emmc"; // 【必要】用于配置控制器私有数据,要与mmc_config.hcs中对应控制器保持一致。emmc类型
} }
device1 :: deviceNode { device1 :: deviceNode {
policy = 1; policy = 1;
...@@ -162,7 +182,7 @@ MMC模块适配包含以下四个步骤: ...@@ -162,7 +182,7 @@ MMC模块适配包含以下四个步骤:
serviceName = "HDF_PLATFORM_MMC_2"; serviceName = "HDF_PLATFORM_MMC_2";
deviceMatchAttr = "hi3516_mmc_sdio"; // SDIO类型 deviceMatchAttr = "hi3516_mmc_sdio"; // SDIO类型
} }
... ......
} }
} }
} }
...@@ -184,7 +204,7 @@ MMC模块适配包含以下四个步骤: ...@@ -184,7 +204,7 @@ MMC模块适配包含以下四个步骤:
freqMax = 100000000; // 【必要】最大频率值 freqMax = 100000000; // 【必要】最大频率值
freqDef = 400000; // 【必要】默认频率值 freqDef = 400000; // 【必要】默认频率值
maxBlkNum = 2048; // 【必要】最大的block号 maxBlkNum = 2048; // 【必要】最大的block号
maxBlkSize = 512; // 【必要】最大的block个数 maxBlkSize = 512; // 【必要】最大block大小
ocrDef = 0x300000; // 【必要】工作电压设置相关 ocrDef = 0x300000; // 【必要】工作电压设置相关
caps2 = 0; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps2定义。 caps2 = 0; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps2定义。
regSize = 0x118; // 【必要】寄存器位宽 regSize = 0x118; // 【必要】寄存器位宽
...@@ -230,7 +250,7 @@ MMC模块适配包含以下四个步骤: ...@@ -230,7 +250,7 @@ MMC模块适配包含以下四个步骤:
#include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs" // 配置文件相对路径 #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs" // 配置文件相对路径
``` ```
3. 实例化MMC控制器对象 3. 实例化MMC控制器对象
完成配置属性文件之后,下一步就是以核心层MmcCntlr对象的初始化为核心,包括驱动适配自定义结构体(传递参数和数据),实例化MmcCntlr成员MmcCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 完成配置属性文件之后,下一步就是以核心层MmcCntlr对象的初始化为核心,包括驱动适配自定义结构体(传递参数和数据),实例化MmcCntlr成员MmcCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
...@@ -287,7 +307,7 @@ MMC模块适配包含以下四个步骤: ...@@ -287,7 +307,7 @@ MMC模块适配包含以下四个步骤:
}; };
``` ```
- MmcCntlr成员钩子函数结构体MmcCntlrOps的实例化,其他成员在Bind函数中初始化。 - MmcCntlr成员钩子函数结构体MmcCntlrOps的实例化。
```c ```c
static struct MmcCntlrOps g_himciHostOps = { static struct MmcCntlrOps g_himciHostOps = {
...@@ -317,9 +337,9 @@ MMC模块适配包含以下四个步骤: ...@@ -317,9 +337,9 @@ MMC模块适配包含以下四个步骤:
返回值: 返回值:
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。
**表2** Bind函数说明 **表 3** HDF_STATUS相关状态说明
| 状态(值) | 问题描述 | | 状态(值) | 问题描述 |
| -------- | -------- | | -------- | -------- |
...@@ -331,7 +351,6 @@ MMC模块适配包含以下四个步骤: ...@@ -331,7 +351,6 @@ MMC模块适配包含以下四个步骤:
| HDF_FAILURE | 初始化失败 | | HDF_FAILURE | 初始化失败 |
函数说明: 函数说明:
MmcCntlr、HimciHost、HdfDeviceObject之间互相赋值,方便其他函数可以相互转化,初始化自定义结构体HimciHost对象,初始化MmcCntlr成员,调用核心层MmcCntlrAdd函数,完成MMC控制器的添加。 MmcCntlr、HimciHost、HdfDeviceObject之间互相赋值,方便其他函数可以相互转化,初始化自定义结构体HimciHost对象,初始化MmcCntlr成员,调用核心层MmcCntlrAdd函数,完成MMC控制器的添加。
```c ```c
...@@ -349,13 +368,13 @@ MMC模块适配包含以下四个步骤: ...@@ -349,13 +368,13 @@ MMC模块适配包含以下四个步骤:
cntlr->hdfDevObj = obj; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 cntlr->hdfDevObj = obj; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
obj->service = &cntlr->service; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 obj->service = &cntlr->service; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提
ret = MmcCntlrParse(cntlr, obj); // 【必要】 初始化cntlr,失败就goto _ERR。 ret = MmcCntlrParse(cntlr, obj); // 【必要】 初始化cntlr,失败就goto _ERR。
... ......
ret = HimciHostParse(host, obj); // 【必要】 初始化host对象的相关属性,失败就goto _ERR。 ret = HimciHostParse(host, obj); // 【必要】 初始化host对象的相关属性,失败就goto _ERR。
... ......
ret = HimciHostInit(host, cntlr); // 驱动适配者自定义的初始化,失败就goto _ERR。 ret = HimciHostInit(host, cntlr); // 驱动适配者自定义的初始化,失败就goto _ERR。
... ......
ret = MmcCntlrAdd(cntlr); // 调用核心层函数,失败就goto _ERR。 ret = MmcCntlrAdd(cntlr); // 调用核心层函数,失败就goto _ERR。
... ......
(void)MmcCntlrAddDetectMsgToQueue(cntlr); // 将卡检测消息添加到队列中。 (void)MmcCntlrAddDetectMsgToQueue(cntlr); // 将卡检测消息添加到队列中。
HDF_LOGD("HimciMmcBind: success."); HDF_LOGD("HimciMmcBind: success.");
return HDF_SUCCESS; return HDF_SUCCESS;
...@@ -417,13 +436,13 @@ MMC模块适配包含以下四个步骤: ...@@ -417,13 +436,13 @@ MMC模块适配包含以下四个步骤:
static void HimciMmcRelease(struct HdfDeviceObject *obj) static void HimciMmcRelease(struct HdfDeviceObject *obj)
{ {
struct MmcCntlr *cntlr = NULL; struct MmcCntlr *cntlr = NULL;
... ......
cntlr = (struct MmcCntlr *)obj->service; // 这里有HdfDeviceObject到MmcCntlr的强制转化,通过service成员,赋值见Bind函数。 cntlr = (struct MmcCntlr *)obj->service; // 这里有HdfDeviceObject到MmcCntlr的强制转化,通过service成员,赋值见Bind函数。
... ......
HimciDeleteHost((struct HimciHost *)cntlr->priv); // 驱动适配者自定义的内存释放函数,这里有MmcCntlr到HimciHost的强制转化。 HimciDeleteHost((struct HimciHost *)cntlr->priv); // 驱动适配者自定义的内存释放函数,这里有MmcCntlr到HimciHost的强制转化。
} }
``` ```
4. 驱动调试 4. 驱动调试
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据读写成功与否等。
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册