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

fix: platform documents refresh.

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